/* Back of House — Light mode, spreadsheet-like */

:root {
    --bg: #ffffff;
    --bg-alt: #f8f9fa;
    --bg-hover: #f0f1f3;
    /* Keyboard-selected ("active") row in the type-to-search autocompletes
       (Jump-to-Details nav picker, calendar search). Deliberately a pastel
       GREEN — distinct from the grey hover, the pale-blue "mine/assigned" row
       tint, and the amber watched star — so the selected row is unmistakable
       even on a colored/dimmed row. Applied !important to beat those state
       backgrounds regardless of source order. */
    --bg-active-hl: #bbf7d0;
    --border: #dee2e6;
    --border-light: #e9ecef;
    --text: #212529;
    --text-muted: #6c757d;
    --text-light: #adb5bd;
    --primary: #3B82F6;
    --primary-hover: #2563EB;
    --success: #22c55e;
    --warning: #eab308;
    --danger: #ef4444;
    --orange: #f97316;
    --sidebar-width: 280px;
    --header-height: 52px;
    /* Distance from the top of the viewport to the top of .page-wrapper —
       the nav PLUS whatever banners are currently showing. Measured by
       _syncChromeHeight() in generic_preload.js; the default here keeps
       everything correct before (and without) JS. Sticky offsets and
       full-height panes must use THIS, never --header-height, which counts
       only the nav. */
    --chrome-h: var(--header-height);
    --subnav-height: 34px;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 14px;
    line-height: 1.5;
    color: var(--text);
    background: var(--bg);
}

/* ---- Header / Nav ---- */

.top-nav {
    display: flex;
    align-items: center;
    height: var(--header-height);
    padding: 0 16px;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 100;
}

.detail-subnav {
    display: flex;
    gap: 4px;
    align-items: center;
    height: var(--subnav-height);
    padding: 0 16px;
    background: var(--bg-alt);
    border-bottom: 1px solid var(--border-light);
    position: sticky;
    top: var(--header-height);
    z-index: 99;
    overflow-x: auto;
    white-space: nowrap;
    font-size: 12px;
}

.detail-subnav a {
    padding: 4px 8px;
    color: var(--text-muted);
    text-decoration: none;
    border-radius: 3px;
}

.detail-subnav a:hover {
    background: var(--bg-hover);
    color: var(--text);
}

.detail-subnav a.active {
    background: var(--primary);
    color: #fff;
}

.detail-section[id^="section-"] {
    scroll-margin-top: calc(var(--header-height) + var(--subnav-height) + 8px);
}

/* Back of House logo — Variant 3 (left bar + stacked acronym).
   Renders as a two-line block (BOH / BACK·OF·HOUSE) inside the nav. */
.top-nav .logo {
    font-family: 'JetBrains Mono', 'Courier New', monospace;
    font-size: 16px;
    font-weight: 400;
    line-height: 1.15;
    color: var(--text);
    text-decoration: none;
    border-left: 2px solid currentColor;
    padding-left: 10px;
    margin-right: 28px;
    display: inline-block;
}
.top-nav .logo .logo-sub {
    display: block;
    font-size: 9px;
    opacity: 0.55;
    letter-spacing: 0.3em;
    font-weight: 400;
}

/* Reusable logo classes (per style guide). */
.boh-logo-bar {
    font-family: 'JetBrains Mono', 'Courier New', monospace;
    font-size: 18px;
    font-weight: 400;
    line-height: 1.3;
    border-left: 2px solid currentColor;
    padding-left: 12px;
    display: inline-block;
}
.boh-logo-bar__sub {
    display: block;
    font-size: 10px;
    opacity: 0.5;
    letter-spacing: 0.3em;
}

.top-nav .nav-links {
    display: flex;
    gap: 4px;
    list-style: none;
}

.top-nav .nav-links a {
    display: inline-block;
    padding: 6px 12px;
    color: var(--text-muted);
    text-decoration: none;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    transition: background 0.15s, color 0.15s;
}

.top-nav .nav-links a:hover {
    background: var(--bg-hover);
    color: var(--text);
}

.top-nav .nav-links a.active {
    background: var(--primary);
    color: #fff;
}

.top-nav .nav-right {
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 8px;
}

/* Second line under the nav controls: rwfetch loop "last completed" heartbeats.
   flex-basis:100% forces it onto its own row within the wrapping .nav-right. */
.nav-loop-status {
    flex-basis: 100%;
    text-align: right;
    font-size: 10px;
    line-height: 1.3;
    color: var(--text-muted, #9ca3af);
    margin-top: -2px;
}
.nav-loop { white-space: nowrap; }
.nav-loop-stale { color: #d97706; font-weight: 600; }
.nav-loop-fail { color: #dc2626; font-weight: 700; }

/* Normal flow ON PURPOSE: the alert banners scroll away with the page. They
   are notices, not chrome, and pinning them held a permanent band at the top
   of every page. What still has to track them is the FIXED left TOC column —
   see --chrome-h in generic_preload.js, which is recomputed on scroll. */
#appChrome {
    position: relative;
    z-index: 98;
}

/* ---- Layout ---- */

.page-wrapper {
    display: flex;
    min-height: calc(100vh - var(--header-height));
}

.main-content {
    flex: 1;
    /* min-width: 0 lets the flex item shrink below its content's
     * min-content so wide tables (calendar/schedule spreadsheets)
     * stay constrained to the viewport. Without it the flex item
     * grows to fit the table, the body scrolls horizontally, and
     * inner overflow:auto containers (.spreadsheet-calendar) never
     * actually clip — which means position:sticky inside them has
     * no scroll context and never pins. */
    min-width: 0;
    padding: 16px 24px;
    overflow-x: auto;
}

.sidebar {
    width: var(--sidebar-width);
    border-left: 1px solid var(--border);
    padding: 16px;
    background: var(--bg-alt);
    overflow-y: auto;
    flex-shrink: 0;
    /* Sticky, offset by the NAV only. Sticky natively gives the behaviour we
       want: the pane starts below whatever banners are showing, scrolls up
       with them, and pins under the nav once they're gone. A scroll-varying
       offset here would fight that. */
    position: sticky;
    top: var(--header-height);
    height: calc(100vh - var(--header-height));
    transition: width 0.2s, padding 0.2s;
}

.sidebar.collapsed {
    width: 32px;
    min-width: 32px;
    padding: 8px 4px;
    overflow: hidden;
}

.sidebar.collapsed .sidebar-content {
    display: none;
}

.sidebar-toggle {
    display: block;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 14px;
    color: var(--text-muted);
    padding: 4px 6px;
    margin-bottom: 8px;
    border-radius: 4px;
}

.sidebar-toggle:hover {
    background: var(--bg-hover);
    color: var(--text);
}

.sidebar h3 {
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    margin-bottom: 8px;
}

.sidebar .index-list {
    list-style: none;
}

.sidebar .index-list li {
    margin-bottom: 2px;
}

.sidebar .index-list a {
    display: block;
    padding: 4px 8px;
    color: var(--text);
    text-decoration: none;
    border-radius: 4px;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar .index-list a:hover {
    background: var(--bg-hover);
}

/* ---- Workload Summary ---- */

.workload-summary {
    background: var(--bg);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    padding: 12px;
    margin-bottom: 16px;
}

.workload-summary .stat-row {
    display: flex;
    justify-content: space-between;
    padding: 2px 0;
    font-size: 13px;
}

.workload-summary .stat-value {
    font-weight: 600;
}

/* ---- Project Row ---- */

.project-group {
    border: 1px solid var(--border-light);
    border-radius: 8px;
    margin-bottom: 8px;
    background: var(--bg);
    transition: border-color 0.15s, box-shadow 0.15s;
    min-width: max-content;
}

.project-group:hover {
    border-color: var(--primary);
    box-shadow: 0 1px 4px rgba(59, 130, 246, 0.1);
}

.project-row {
    padding: 12px 16px;
}

/* My Projects / Watched row highlights */
/* Both "assigned to me" and "watched" share the same soft-yellow highlight
 * background; a thick left border distinguishes them — RED = assigned to me,
 * GREEN = watching. dash-row-mine and dash-row-watched are mutually exclusive
 * in JS (a mine row never also gets the watched class), so a "both" project
 * always renders as assigned/red — assigned is the stronger signal.
 * These rules sit after `.project-group:hover` in source and at equal
 * specificity, so their border-left-color wins on hover too (the accent
 * stays red/green instead of flipping to the hover primary color). */
.project-group:has(.dash-row-mine),
.project-group:has(.dash-row-watched) {
    background: #fffaeb;
}
.project-group:has(.dash-row-mine) {
    border-left: 4px solid #dc2626;
}
.project-group:has(.dash-row-watched) {
    border-left: 4px solid #16a34a;
}

.project-row-header {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 6px;
}

.project-row-header .project-name {
    font-weight: 600;
    font-size: 15px;
}

.project-row-header .project-number {
    color: var(--text-muted);
    font-size: 13px;
}

.project-row-header .project-color-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    display: inline-block;
    flex-shrink: 0;
}

.project-dates-inline {
    display: flex;
    gap: 12px;
    font-size: 12px;
    color: var(--text-muted);
    margin-bottom: 8px;
    flex-wrap: wrap;
}

.project-dates-inline .date-chip {
    background: var(--bg-alt);
    padding: 2px 8px;
    border-radius: 4px;
    white-space: nowrap;
}

.project-info-block {
    display: flex;
    gap: 24px;
    margin-bottom: 8px;
    flex-wrap: wrap;
}

.project-info-block .info-field {
    flex: 1;
    min-width: 150px;
}

.project-info-block .info-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--text-muted);
    margin-bottom: 2px;
}

.project-info-block .info-value {
    font-size: 13px;
    color: var(--text);
}

/* ---- Dashboard Row Layout ---- */

.dash-filter-warning {
    padding: 12px 16px;
    background: #fef3c7;
    border: 1px solid #f59e0b;
    border-radius: 4px;
    color: #92400e;
    font-size: 13px;
    margin-bottom: 8px;
}
.dash-filter-warning a {
    color: #1e40af;
    font-weight: 600;
}
.dash-row {
    display: flex;
    gap: 0;
    align-items: stretch;
    padding: 0;
    min-width: max-content;
}

#projectList {
    /* Vertical + horizontal scroller for the virtualized project list. The
     * server-rendered `.project-group` rows are detached into a JS array and
     * only the visible window (+buffer) is attached inside #dash-rows. */
    overflow: auto;
    /* THE END OF THE LIST IS NOT THE TOP OF THE DOCUMENT. Owner, 2026-08-18:
     * *"If I think I'm scrolling up but I'm already at the top of the
     * container, then I just go shooting off to the top of the page."* Scroll
     * chaining is the browser default and this is the container it hurts most,
     * because the dashboard is one long list inside a page that also scrolls.
     * `contain` keeps an overshoot here; it does not stop the wheel reaching
     * the page from anywhere outside the list. */
    overscroll-behavior: contain;
    position: relative;
    max-height: calc(100vh - 200px);
}
/* Anti-flash: the template renders every project row unfiltered/unsorted, and
 * that markup can paint before dashboard.js's DOMContentLoaded handler runs,
 * detaches the rows, restores persisted filters (showPast/showCompleted/...)
 * and builds the real filtered+sorted view. Without this, users see a brief
 * flash of the full server list before it snaps to the filtered one.
 * dashboard.html renders #projectList with this class present from the first
 * byte (no JS race); dashboard.js removes it once the first filtered render
 * (or the no-virtualizer fallback) is in place. visibility (not display)
 * keeps the box measurable/scrollable while hidden. */
#projectList.dash-booting {
    visibility: hidden;
}
/* Spacer gives the scrollbar its full range (height = sum of all row heights,
 * set by JS). #dash-rows is lifted out of flow and translated to the first
 * visible row; min-width:max-content keeps horizontal scrolling of wide rows. */
#dash-spacer {
    position: relative;
    width: 1px;
}
#dash-rows {
    position: absolute;
    top: 0;
    left: 0;
    right: auto;
    min-width: max-content;
    will-change: transform;
}

/* Async dashboard: loading / error / empty overlay + partial-load resume bar. */
.dash-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 48px 16px;
    min-height: 200px;
    color: var(--text-muted);
    text-align: center;
}
.dash-loading-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--border);
    border-top-color: #2563eb;
    border-radius: 50%;
    animation: dash-spin 0.8s linear infinite;
}
@keyframes dash-spin { to { transform: rotate(360deg); } }
.dash-loading-text { font-size: 14px; }
.dash-loading-error .dash-loading-text { color: #b91c1c; }
.dash-resume-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 8px 12px;
    margin-top: 8px;
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 13px;
    color: var(--text-muted);
}

.dash-col {
    padding: 8px 10px;
    border-right: 1px solid var(--border-light);
    flex-shrink: 0;
}

.dash-col:last-child {
    border-right: none;
}

/* Inline edit inputs for dashboard */
.dash-inline {
    border: 1px solid var(--border-light);
    padding: 2px 4px;
    border-radius: 4px;
    background: transparent;
    font-size: 13px;
    font-family: inherit;
    color: var(--text);
    width: 100%;
    transition: border-color 0.15s, background 0.15s;
}

.dash-inline:hover {
    border-color: var(--border);
    background: var(--bg-alt);
}

.dash-inline:focus {
    outline: none;
    border-color: var(--primary);
    background: var(--bg);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

.dash-inline-sm {
    font-size: 12px;
    padding: 1px 3px;
    width: 100%;
}

.dash-inline-area {
    resize: both;
    min-height: 36px;
    border: 1px solid var(--border-light);
    padding: 2px 4px;
    border-radius: 4px;
    background: transparent;
    font-size: 12px;
    font-family: inherit;
    color: var(--text);
    width: 100%;
    flex: 1;
    transition: border-color 0.15s, background 0.15s;
}

.dash-inline-area:hover {
    border-color: var(--border);
    background: var(--bg-alt);
}

.dash-inline-area:focus {
    outline: none;
    border-color: var(--primary);
    background: var(--bg);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

/* ---- Lazy resting cells + shared editor ----
 * A `.dash-lazy` div renders the field's text at rest (no live input). On
 * click the shared VL editor (input/textarea) is moved INSIDE the div; the
 * editor is chrome-less, so the div's own border/padding stays the field
 * frame — no double border, no layout shift on activate. */
.dash-lazy {
    cursor: text;
    box-sizing: border-box;
    white-space: pre-wrap;        /* textarea variants keep newlines + wrap */
    overflow: hidden;
}
.dash-lazy[data-ctl="input"] {
    white-space: nowrap;          /* input variants: single line, clipped */
    /*text-overflow: ellipsis;*/
    min-height: 1.4em;            /* hold one-line height when empty */
    line-height: 1.4;
}
.dash-lazy[data-ctl="textarea"] {
    display: flex;
    flex-direction: column;
}
/* keep empty cells sized + clickable; spec cells show their label as a hint */
.dash-lazy:empty::before { content: '\200b'; }
.dash-lazy[data-placeholder]:empty::before {
    content: attr(data-placeholder);
    color: var(--text-muted);
}
/* focus-ring parity with the old input/textarea :focus rules */
.dash-inline.dash-lazy:focus-within,
.dash-inline-area.dash-lazy:focus-within {
    outline: none;
    border-color: var(--primary);
    background: var(--bg);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}
.dash-feed-input.dash-lazy:focus-within { background: #fff; }

/* The shared editor controls: chrome-less, fill the wrapping cell so the
 * caret/text sit exactly where the resting text did. */
.vl-editor {
    font: inherit;
    color: inherit;
    border: none;
    outline: none;
    background: transparent;
    margin: 0;
    padding: 0;
    width: 100%;
    box-sizing: border-box;
}
.vl-editor-area {
    display: block;
    flex: 1;
    resize: none;
    min-height: 1.4em;
}

/* Left block: name/number header spanning over dates + loc/status/desc */
.dash-col-left {
    width: 680px;
    min-width: 680px;
    display: flex;
    flex-direction: column;
}

.dash-left-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border-light);
    margin-bottom: 6px;
}
/* Line 1: triage icons + full-width name + the details arrow. */
.dash-left-header-main {
    display: flex;
    align-items: center;
    gap: 8px;
}
/* Line 2: order number / RW quote-order kind / order type / status chip — moved
   off the name's line so the project name never gets squeezed/clipped. */
.dash-left-header-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 8px;
    padding-left: 2px;
}

.dash-inline-name {
    font-weight: 600;
    font-size: 14px;
    flex: 1;
    min-width: 4em;
}
/* On its own line now, the number reads left-aligned and sizes to content. */
.dash-left-header-meta .dash-inline-number {
    width: auto;
    text-align: left;
}

.dash-detail-link {
    font-size: 16px;
    font-weight: 600;
    color: var(--primary);
    text-decoration: none;
    flex-shrink: 0;
    padding: 0 4px;
    line-height: 1;
}

.dash-detail-link:hover {
    color: var(--primary-hover, #2563eb);
}

.dash-inline-number {
    color: var(--text-muted);
    font-size: 13px;
    width: 100px;
    text-align: right;
    flex-shrink: 0;
}

.dash-left-body {
    display: flex;
    gap: 0;
    flex: 1;
}

.dash-left-dates {
    width: 248px;
    min-width: 248px;
    padding-right: 10px;
    border-right: 1px solid var(--border);
}

.dash-date-input {
    width: 110px !important;
    font-size: 11px !important;
}

.dash-left-fields {
    display: flex;
    flex: 1;
    gap: 0;
    align-items: stretch;
}

.dash-left-field {
    flex: 1;
    padding: 0 8px;
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
}

.dash-left-field:last-child {
    border-right: none;
}

.dash-left-field .dash-inline-area {
    flex: 1;
    /* Constrain the Next Step / Description / Location / PM Status boxes so a
       long value can't grow the row or shove a neighbour into the next column
       (owner report 2026-07-23: the Location box widened and pushed PM Status
       into the contact column). Cap the height and scroll instead; wrap long
       unbroken strings so they can't blow out the width either. */
    max-height: 7em;
    overflow-y: auto;
    /* A 7em box inside a virtualised row: chaining out of it jumps the
     * whole list, which is the same complaint one size down. */
    overscroll-behavior: contain;
    overflow-wrap: anywhere;
}

/* Milestone alerts column (conditional) */
.dash-col-ms-alerts {
    width: 160px;
    min-width: 160px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    justify-content: center;
    padding: 4px 8px;
}

.dash-ms-alert {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
    font-size: 11px;
    padding: 2px 6px;
    border-radius: 3px;
    line-height: 1.3;
}

.dash-ms-alert-name {
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dash-ms-alert-due {
    flex-shrink: 0;
    font-weight: 600;
}

.dash-ms-overdue {
    background: #fee2e2;
    color: #991b1b;
}

.dash-ms-alerting {
    background: #fff7ed;
    color: #9a3412;
}

.dash-ms-upcoming {
    background: #fef9c3;
    color: #854d0e;
}

/* Project row left-edge alert stripe */
.dash-row-ms-overdue,
.dash-row-ms-alerting,
.dash-row-ms-upcoming {
    position: relative;
    overflow: hidden;
}

.dash-row-ms-overdue::before,
.dash-row-ms-alerting::before,
.dash-row-ms-upcoming::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 2em;
    border-radius: 8px 0 0 8px;
}

.dash-row-ms-overdue::before {
    background: #ef4444;
}
.dash-row-ms-alerting::before {
    background: #f97316;
}
.dash-row-ms-upcoming::before {
    background: #eab308;
}

.dash-row-ms-overdue > .dash-col:first-child,
.dash-row-ms-alerting > .dash-col:first-child,
.dash-row-ms-upcoming > .dash-col:first-child {
    padding-left: calc(2em + 10px);
}

.dash-col-people {
    width: 130px;
    min-width: 130px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    justify-content: flex-start;
}

.dash-people-field {
    display: flex;
    flex-direction: column;
}

.dash-people-field .dash-inline-sm {
    width: 100%;
}

.dash-date-stack {
    font-size: 12px;
    line-height: 1.5;
}

.dash-date-row {
    display: flex;
    align-items: center;
    gap: 2px;
    justify-content: space-between;
}

.dash-date-label {
    color: var(--text-muted);
    min-width: 80px;
    max-width: 128px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 11px;
}

.dash-col-reqs {
    /* min-width (not fixed width) so >2 requirement columns widen the block;
     * the row is a flex of flex-shrink:0 columns and scrolls horizontally. */
    min-width: 300px;
    position: relative;
}

.dash-req-grid {
    /* Column-major flow capped at 10 rows tall: items fill DOWN up to 10,
     * then wrap RIGHT into as many implicit columns as needed (25 reqs =
     * 3 columns of <=10). Trailing empty `auto` rows collapse to 0, so short
     * lists don't reserve height. The template's inline --dash-req-rows
     * (ceil(n/2), for the old balanced 2-column layout) is no longer read.
     * Fixed 135px implicit columns reproduce the old column width (300px
     * block - 2*10px padding - 10px gap = 2*135px) and preserve .req-name
     * ellipsis clipping; extra columns widen the block (.dash-col-reqs has
     * min-width, not fixed width) and the dash row already h-scrolls
     * (min-width: max-content in #projectList's overflow:auto). */
    display: grid;
    grid-template-rows: repeat(10, auto);
    grid-auto-flow: column;
    grid-auto-columns: 135px;
    gap: 2px 10px;
}

.dash-req-item {
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    position: relative;
}

.dash-req-item:hover {
    background: var(--bg-hover);
}

.dash-col-actions {
    width: 80px;
    min-width: 80px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    justify-content: flex-start;
}

.dash-col-actions .btn-sm {
    width: 100%;
    justify-content: center;
}

.dash-col-notes {
    min-width: 200px;
    display: flex;
}

.dash-notes-strip {
    display: flex;
    gap: 8px;
    padding: 0;
    align-items: stretch;
}

.dash-note-card {
    background: var(--bg-alt);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    padding: 0;
    font-size: 12px;
    width: 160px;
    flex-shrink: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.dash-feed-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 2px 6px 2px 10px;
    border-radius: 6px 6px 0 0;
    background: #dbeafe;
}
.dash-feed-label {
    font-weight: 600;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: #1e3a5f;
}
.dash-feed-label-open { color: #dc2626; }
.dash-feed-header:has(.dash-feed-label-open) { background: #fde8e8; }
.dash-feed-check {
    display: flex;
    align-items: center;
    cursor: pointer;
    margin: 0;
}
.dash-feed-check input { margin: 0; }
.dash-feed-input {
    flex: 1;
    border: none;
    background: transparent;
    font-size: 12px;
    font-family: inherit;
    padding: 4px 8px;
    resize: none;
    outline: none;
    min-height: 20px;
    width: 100%;
    box-sizing: border-box;
}
.dash-feed-input:focus {
    background: #fff;
}
.dash-feed-done {
    opacity: 0.5;
}
.dash-feed-done .dash-feed-input {
    text-decoration: line-through;
}
.dash-feed-meta { padding: 0 8px 4px; font-size: 11px; color: #888; white-space: nowrap; }

.dash-feed-toggle:not(.active) {
    opacity: 0.5;
}

/* Note/field columns (Next Step, Schedule Notes, Flip, Hardware, Trucking) */
.dash-col-field {
    width: var(--dash-field-w, 150px);
    min-width: 120px;
    display: flex;
    flex-direction: column;
}
.dash-col-field .dash-inline-area {
    flex: 1;
}

/* ------------------------------------------------------------------
 * Dashboard row height caps: long content scrolls IN PLACE instead of
 * stretching the row and pushing the rest of the row off-screen.
 * ------------------------------------------------------------------ */

/* (Requirements list height cap lives on .dash-req-grid itself:
 * grid-template-rows: repeat(10, auto) + grid-auto-flow: column makes the
 * list at most 10 items tall, wrapping into extra columns — no scroll.) */

/* Every text display cell: cap at 10 lines (10 * line-height). em-based so
 * the cap tracks font-size changes. Inherited line-height is 1.5, so
 * 10 lines = 15em. These rules sit AFTER `.dash-lazy { overflow:hidden }`,
 * so the overflow-y longhand wins (keeps overflow-x hidden, y scrollable). */
.dash-inline-area,
.dash-feed-input,
.dash-scratch-note {
    max-height: 15em;
    overflow-y: auto;
    overscroll-behavior: contain;
}
/* ...unless the user has dragged this one taller. The cap is a RESTING
   default (stops a long note stretching the row); an explicit drag is an
   explicit request to see more, so it wins. Stamped by
   initResizableFields() in generic_preload.js. */
.dash-inline-area.is-resized,
.dash-feed-input.is-resized,
.dash-scratch-note.is-resized {
    max-height: none;
}
/* Editing state: the shared VL editor moves a chrome-less textarea INTO the
 * resting cell on click and sets NO inline height, so this max-height wins
 * with no !important; the textarea scrolls its own overflow while editing. */
.vl-editor-area {
    max-height: 15em;
    overflow-y: auto;
}

/* Subtle thin scrollbars for the capped scroll areas. */
.dash-req-grid,
.dash-inline-area,
.dash-feed-input,
.dash-scratch-note,
.vl-editor-area {
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}
.dash-req-grid::-webkit-scrollbar,
.dash-inline-area::-webkit-scrollbar,
.dash-feed-input::-webkit-scrollbar,
.dash-scratch-note::-webkit-scrollbar,
.vl-editor-area::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}
.dash-req-grid::-webkit-scrollbar-thumb,
.dash-inline-area::-webkit-scrollbar-thumb,
.dash-feed-input::-webkit-scrollbar-thumb,
.dash-scratch-note::-webkit-scrollbar-thumb,
.vl-editor-area::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}
.dash-req-grid::-webkit-scrollbar-track,
.dash-inline-area::-webkit-scrollbar-track,
.dash-feed-input::-webkit-scrollbar-track,
.dash-scratch-note::-webkit-scrollbar-track,
.vl-editor-area::-webkit-scrollbar-track {
    background: transparent;
}

/* Quick-add dropdown in requirements column */
.dash-req-quickadd {
    display: flex;
    gap: 4px;
    margin-top: 6px;
    align-items: center;
}
select.dash-quickadd-select {
    font-size: 11px;
    padding: 2px 4px;
    max-width: 120px;
    min-width: 0;
}

/* Expand buttons row under each project */
.dash-expand-row {
    display: flex;
    gap: 4px;
    padding: 2px 10px 4px;
    border-bottom: 1px solid var(--border-light);
    background: var(--bg-alt);
    position: sticky;
    left: 0;
    max-width: calc(100vw - 40px);
}
.dash-expand-btn.active {
    background: var(--primary);
    color: #fff;
}
/* Per-project check-in nudge button (dashboard + detail). Sits at the FRONT of
   the expand row so it's visible without scrolling that wide container. */
.dash-checkin-btn {
    white-space: nowrap;
    color: var(--text-muted, #6b7280);
}
.dash-checkin-btn .dash-checkin-due { font-weight: 700; }
.dash-checkin-btn.due {
    border-color: #f59e0b;
    color: #b45309;
    background: #fffbeb;
}
.dash-checkin-btn.overdue {
    border-color: #ef4444;
    color: #b91c1c;
    background: #fef2f2;
}
.dash-checkin-btn.dash-checkin-done {
    border-color: #86efac;
    color: #15803d;
    background: #f0fdf4;
}
.dash-expand-panel {
    border-bottom: 1px solid var(--border-light);
    background: #fafbfc;
    overflow-x: auto;
    position: sticky;
    left: 0;
    max-width: calc(100vw - 40px);
}
.dash-expand-table {
    font-size: 12px;
    margin: 0;
}
.dash-expand-loading {
    padding: 8px 12px;
    color: var(--text-muted);
    font-size: 12px;
}

/* --- the dashboard's "To order" drawer -------------------------------------
   Owner, 2026-08-18: *"a drawer for the equivalent of Add to Order ... just the
   list of items: 5x trusts, nothing else, just quantity and item name."* So the
   table has exactly two columns and no header — a header over two obvious
   columns is a row of the drawer spent saying what you can already see.
   The quantity is right-aligned against the name so the numbers line up down
   the list, which is the whole reason to put it first. */
.dash-toorder { padding: 6px 10px 8px; }
.dash-toorder-tbl { border-collapse: collapse; margin: 0 0 6px; }
.dash-toorder-tbl td { padding: 1px 6px 1px 0; font-size: 12px;
    vertical-align: baseline; }
.dash-toorder-qty { text-align: right; font-variant-numeric: tabular-nums;
    font-weight: 650; white-space: nowrap; }
.dash-toorder-name { color: #333; }
/* A worksheet row cannot be adjusted from the quote page either (§54.2), so it
   is drawn quieter here rather than looking like something to act on. */
.dash-toorder-fixed .dash-toorder-name { color: #777; font-style: italic; }
.dash-toorder-none { font-size: 12px; color: #666; margin-bottom: 6px; }
.dash-toorder-link { font-size: 12px; }

/* THE REASON, one word, muted — it is context for the name beside it and must
   not compete with the quantity. The long sentence is the row's tooltip. */
.dash-toorder-why { font-size: 10.5px; text-transform: uppercase;
    letter-spacing: .03em; color: #999; padding-left: 8px;
    white-space: nowrap; }
.dash-toorder-why-spare { color: #b26a00; }
.dash-toorder-why-worksheet { color: #1565c0; }

/* REFRESHING OVER THE ANSWER YOU ALREADY HAVE, rather than instead of it.
   The rows dim but stay readable and stay put — a list that collapses to a
   spinner loses both the information and your place in it. */
.dash-toorder { position: relative; }
.dash-toorder-stale .dash-toorder-tbl { opacity: .45; }
.dash-toorder-refresh { position: absolute; top: 4px; right: 8px;
    display: flex; align-items: center; gap: 5px;
    font-size: 11px; color: #666; background: rgba(255,255,255,.88);
    border-radius: 10px; padding: 1px 8px 1px 5px; }
.dash-toorder-spin { width: 9px; height: 9px; border-radius: 50%;
    border: 2px solid #cfcfcf; border-top-color: #666;
    animation: dashToOrderSpin .7s linear infinite; }
@keyframes dashToOrderSpin { to { transform: rotate(360deg); } }
/* A refresh that did not land says so ABOVE the rows it failed to replace. */
.dash-toorder-stalewarn { font-size: 11px; color: #b26a00; margin-bottom: 4px; }

/* THE MARK ON THE PILL. Red, small, and BEFORE the word — it is a flag on the
   name, which is what he asked for, and putting it after would make it read as
   punctuation. */
.dash-toorder-bang { color: #c62828; font-weight: 800; }
.dash-toorder-btn.dash-toorder-has { border-color: #c62828; }


/* Project search bar (inline with header) */
.dash-search-bar {
    font-size: 13px;
    padding: 4px 8px;
    width: 200px;
}

/* Collapsible sort/filter panel — hidden by default at every width, revealed
 * by the "Filters" toggle in the projects header. JS adds `.dash-open` (and
 * persists it) so a user who opens it keeps it open across reloads. Starting
 * hidden keeps the tall checkbox row from eating vertical space, which matters
 * most on mobile but is a welcome default on desktop too. */
.dash-filter-panel { display: none; }
.dash-filter-panel.dash-open { display: block; }

.dash-filters-toggle .dash-caret { display: inline-block; }
.dash-filters-toggle.active {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
}

/* Requirement Popover */
.req-popover {
    /* WO-153 — ABSOLUTE, NOT FIXED. Fixed pins it to the SCREEN, so scrolling
       slid the project row out from under its own drawer and a row near the
       bottom opened one below the fold that could never be reached. Absolute
       puts it in the PAGE, beside the row, and the two scroll together with no
       handler and no reposition loop. `_dashAnchorPopover` gives it document
       coordinates and flips it above the button when there is no room below. */
    position: absolute;
    z-index: 300;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    padding: 10px 14px;
    min-width: 200px;
    white-space: nowrap;
}

/* Dashboard +Spec / +Rec quick-add popover (replaced the add drawer). */
.dash-add-popover {
    min-width: 240px;
    max-width: 340px;
    max-height: 60vh;
    overflow-y: auto;
    white-space: normal;
}
.dash-add-pop-title {
    font-weight: 600;
    font-size: 12px;
    margin-bottom: 6px;
    color: var(--text-muted, #6b7280);
}
.dash-add-pop-input {
    width: 100%;
    margin-bottom: 6px;
    box-sizing: border-box;
}

/* Per-field "edited when" badge next to a detail-page field label. */
.field-edited-badge {
    margin-left: 6px;
    font-weight: 400;
    font-size: 11px;
    color: var(--text-muted, #9ca3af);
    white-space: nowrap;
}

/* Collapsible embedded Notes page on the dashboard. */
.dash-notes-embed {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.dash-notes-embed-body {
    flex-basis: 100%;
    width: 100%;
}
.dash-notes-embed-frame {
    width: 100%;
    height: 55vh;
    min-height: 320px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg);
}

.req-pop-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    padding: 3px 0;
    cursor: pointer;
    white-space: nowrap;
}

/* Milestone popover (dashboard) */
.ms-popover {
    position: absolute;
    z-index: 300;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    padding: 10px 14px;
    min-width: 180px;
    white-space: nowrap;
}

.ms-pop-title {
    font-weight: 600;
    font-size: 13px;
    margin-bottom: 2px;
}

.ms-pop-due {
    font-size: 12px;
    color: var(--text-muted);
}

.ms-pop-repeat {
    font-size: 11px;
    color: var(--text-muted);
    font-style: italic;
}

/* ---- Requirements Grid ---- */

.req-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 4px 16px;
    margin: 8px 0;
}

.req-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 0;
    font-size: 12px;
}

.req-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

.req-dot.green { background: var(--success); }
.req-dot.yellow { background: var(--warning); }
.req-dot.red { background: var(--danger); }
.req-dot.orange { background: var(--orange); }

/* Fully-completed requirement rows: greyed and sunk to the bottom (toggle in
   the Requirements section header, default on). Dot keeps its green color. */
.req-row-done { opacity: 0.5; }
.req-row-done td { color: var(--text-muted); }
.req-row-done .inline-edit { color: var(--text-muted); }

.req-sink-toggle {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    cursor: pointer;
}

.req-checks-inline {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: center;
}

.req-check-label {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
    cursor: pointer;
}

.req-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- Specs List (horizontal scroll) ---- */

.specs-row {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding: 4px 0;
}

.spec-chip {
    background: var(--bg-alt);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    padding: 4px 10px;
    font-size: 12px;
    white-space: nowrap;
    flex-shrink: 0;
}

.spec-chip .spec-label {
    font-weight: 600;
    color: var(--text-muted);
}

/* ---- Row Actions ---- */

.row-actions {
    display: flex;
    gap: 4px;
    margin-top: 8px;
}

/* ---- Buttons ---- */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 5px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--text);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    text-decoration: none;
}

.btn:hover {
    background: var(--bg-hover);
    border-color: var(--text-light);
}

.btn-sm {
    padding: 3px 8px;
    font-size: 11px;
}

.btn-primary {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
}

.btn-primary:hover {
    background: var(--primary-hover);
    border-color: var(--primary-hover);
}

.btn-danger {
    color: var(--danger);
    border-color: var(--danger);
}

.btn-danger:hover {
    background: var(--danger);
    color: #fff;
}

/* ---- Drawers ---- */

.drawer-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.3);
    z-index: 200;
}

.drawer-overlay.active {
    display: block;
}

.drawer {
    position: fixed;
    top: 0;
    right: -420px;
    width: 420px;
    height: 100vh;
    background: var(--bg);
    border-left: 1px solid var(--border);
    z-index: 201;
    transition: right 0.25s ease;
    display: flex;
    flex-direction: column;
    box-shadow: -4px 0 12px rgba(0, 0, 0, 0.1);
}

.drawer.active {
    right: 0;
}

.drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px;
    border-bottom: 1px solid var(--border);
}

.drawer-header h3 {
    font-size: 16px;
    font-weight: 600;
}

.drawer-close {
    background: none;
    border: none;
    font-size: 20px;
    cursor: pointer;
    color: var(--text-muted);
    padding: 4px;
}

.drawer-body {
    flex: 1;
    padding: 16px;
    overflow-y: auto;
}

.drawer-footer {
    padding: 12px 16px;
    border-top: 1px solid var(--border);
}

/* ---- Forms ---- */

.form-group {
    margin-bottom: 12px;
}

.form-group label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 4px;
}

.form-control {
    width: 100%;
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 13px;
    font-family: inherit;
    transition: border-color 0.15s;
}

.form-control:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

textarea.form-control {
    min-height: 80px;
    /* Two-dimensional on purpose. Width is only useful because
       initResizableFields() (generic_preload.js) re-spans the grid cell to
       match, so the neighbours reflow instead of being overlapped. */
    resize: both;
}

select.form-control {
    appearance: auto;
}

.inline-edit {
    border: 1px solid var(--border-light);
    padding: 2px 6px;
    border-radius: 4px;
    background: transparent;
    font-size: inherit;
    font-family: inherit;
    color: inherit;
    width: 100%;
    transition: border-color 0.15s, background 0.15s;
}

.inline-edit:hover {
    border-color: var(--border-light);
    background: var(--bg-alt);
}

.inline-edit:focus {
    outline: none;
    border-color: var(--primary);
    background: var(--bg);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

/* ---- Todo / Note items ---- */

.todo-item, .note-item {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid var(--border-light);
    border-radius: 6px;
    margin-bottom: 8px;
    background: var(--bg);
}

.todo-checkbox {
    margin-top: 3px;
    cursor: pointer;
    width: 16px;
    height: 16px;
    accent-color: var(--primary);
}

.todo-text.completed {
    text-decoration: line-through;
    color: var(--text-muted);
}

.todo-meta {
    display: flex;
    gap: 8px;
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 2px;
}

/* Due date urgency backgrounds */
.todo-due-week {
    background: #fefce8;
    padding-left: 8px;
    padding-right: 8px;
    margin-left: -8px;
    margin-right: -8px;
    border-radius: 4px;
}
.todo-due-soon {
    background: #fff7ed;
    padding-left: 8px;
    padding-right: 8px;
    margin-left: -8px;
    margin-right: -8px;
    border-radius: 4px;
}
.todo-due-today {
    background: #fef2f2;
    padding-left: 8px;
    padding-right: 8px;
    margin-left: -8px;
    margin-right: -8px;
    border-radius: 4px;
}
.todo-due-overdue {
    background: #fef2f2;
    border: 3px solid #fca5a5;
    padding-left: 8px;
    padding-right: 8px;
    margin-left: -8px;
    margin-right: -8px;
    border-radius: 4px;
}

.priority-badge {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
}

.priority-badge.low { background: #e0f2fe; color: #0369a1; }
.priority-badge.medium { background: #fef3c7; color: #92400e; }
.priority-badge.high { background: #fee2e2; color: #991b1b; }
.priority-badge.urgent { background: #ef4444; color: #fff; }

.note-handled {
    color: var(--text-light);
}

/* ---- Scratch Pad ---- */

.scratch-pad {
    background: #fffde7;
    border: 1px solid #fff176;
    border-radius: 8px;
    padding: 12px;
    margin-bottom: 16px;
}

.scratch-pad textarea {
    width: 100%;
    min-height: 200px;
    border: none;
    background: transparent;
    font-family: inherit;
    font-size: 13px;
    resize: both;
    outline: none;
}

.scratch-pad-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: #f59e0b;
    margin-bottom: 6px;
}

/* Saved indicator: always present in the layout (so showing/hiding it
 * doesn't reflow the textarea below). Visibility-toggled via .is-visible. */
.scratch-pad-status {
    display: inline-block;
    visibility: hidden;
    opacity: 0;
    font-size: 11px;
    color: #888;
    font-style: italic;
    padding: 2px 4px;
    min-height: 1em;
    line-height: 1;
    transition: opacity 0.2s linear;
}
.scratch-pad-status.is-visible {
    visibility: visible;
    opacity: 1;
}

.scratch-pad-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.scratch-pad-item {
    background: var(--bg-alt);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 12px;
    cursor: pointer;
    min-width: 100px;
    max-width: 200px;
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
}
.scratch-pad-item strong { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scratch-pad-delete {
    background: none;
    border: none;
    color: #999;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    padding: 0 2px;
    flex-shrink: 0;
}
.scratch-pad-delete:hover { color: #ef4444; }
.scratch-pad-item:hover { border-color: var(--border); }

/* Scratch-pad drawer split: top half = scratch pad UI, bottom half = open
   todos for the current context. Each half scrolls independently when its
   content overflows the 50% allocation. */
.gsp-split {
    display: flex;
    flex-direction: column;
    height: 100%;
    gap: 0;
    margin: -16px;  /* cancel .drawer-body's 16px padding so the split fills */
}
.gsp-split-top,
.gsp-split-bottom {
    overflow-y: auto;
    padding: 12px 16px;
}
.gsp-split-top {
    flex: 1 1 50%;
    min-height: 0;
    border-bottom: 1px solid var(--border);
}
.gsp-split-bottom {
    flex: 1 1 50%;
    min-height: 0;
    background: #fafbfc;
}
.gsp-todos-header {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 8px;
    letter-spacing: 0.04em;
}
.gsp-todo-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px;
    border-bottom: 1px solid var(--border-light);
    font-size: 12px;
    line-height: 1.3;
    /* Row renders as an anchor — strip default link styling. */
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}
.gsp-todo-row:hover {
    background: #f3f4f6;
    color: inherit;
    text-decoration: none;
}
.gsp-todo-prio {
    flex: 0 0 auto;
    font-size: 10px;
    padding: 1px 5px;
}
.gsp-todo-status {
    flex: 0 0 auto;
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 3px;
    border: 1px solid #d1d5db;
    background: #f3f4f6;
    color: #4b5563;
    white-space: nowrap;
}
/* Status-key tints. Keys match TodoStatus.key values seeded in 0040. */
.gsp-todo-status-not_started { background: #f3f4f6; color: #4b5563; border-color: #d1d5db; }
.gsp-todo-status-do_next     { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.gsp-todo-status-in_progress { background: #dbeafe; color: #1e40af; border-color: #93c5fd; }
.gsp-todo-status-on_hold     { background: #fce7f3; color: #9d174d; border-color: #fbcfe8; }
.gsp-todo-status-waiting     { background: #ede9fe; color: #5b21b6; border-color: #ddd6fe; }
.gsp-todo-status-complete    { background: #d1fae5; color: #065f46; border-color: #a7f3d0; }
.gsp-todo-proj {
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 600;
    max-width: 90px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gsp-todo-text {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gsp-todo-due {
    flex: 0 0 auto;
    font-size: 10px;
    color: var(--text-muted);
}
.scratch-pad-item.active {
    border-color: #2563eb;
    background: #eff6ff;
}

.scratch-pad-snippet {
    color: var(--muted);
    font-size: 11px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
}

/* ---- Calendar ---- */

/* Calendar toolbar */
.cal-toolbar {
    margin-bottom: 8px;
}
.cal-toolbar-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 6px 0;
}
.cal-toolbar-group {
    display: flex;
    align-items: center;
    gap: 6px;
}
.cal-toolbar-sep {
    width: 1px;
    height: 24px;
    background: var(--border-light);
}

/* Mode toggle */
.cal-mode-toggle {
    display: flex;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
}
.cal-mode-btn {
    padding: 5px 14px;
    font-size: 12px;
    font-weight: 500;
    border: none;
    background: var(--bg);
    color: var(--text-muted);
    cursor: pointer;
    transition: all 0.15s;
}
.cal-mode-btn:not(:last-child) {
    border-right: 1px solid var(--border);
}
.cal-mode-btn.active {
    background: var(--primary);
    color: #fff;
}
.cal-mode-btn:hover:not(.active) {
    background: var(--bg-alt);
    color: var(--text);
}

/* Nav buttons */
.cal-nav {
    display: flex;
    align-items: center;
    gap: 2px;
}
.cal-nav-btn {
    padding: 4px 10px;
    font-size: 12px;
    border: 1px solid var(--border-light);
    background: var(--bg);
    border-radius: 4px;
    cursor: pointer;
    color: var(--text);
}
.cal-nav-btn:hover {
    background: var(--bg-alt);
}
.cal-today-btn {
    font-weight: 600;
}
.cal-period-label {
    font-weight: 600;
    font-size: 13px;
    padding: 0 8px;
    white-space: nowrap;
}

/* Select */
.cal-select {
    padding: 4px 8px;
    font-size: 12px;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    background: var(--bg);
    cursor: pointer;
}

/* Checkbox options */
.cal-options {
    gap: 10px;
}
.cal-check {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    color: var(--text-muted);
    cursor: pointer;
    white-space: nowrap;
}
.cal-check input {
    margin: 0;
}

/* Date type dropdown */
.cal-dropdown {
    position: relative;
}
.cal-dropdown-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    font-size: 12px;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    background: var(--bg);
    cursor: pointer;
    white-space: nowrap;
}
.cal-dropdown-btn:hover {
    background: var(--bg-alt);
}
.cal-dropdown-caret {
    font-size: 10px;
    color: var(--text-muted);
}
.cal-dropdown-menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 2px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
    z-index: 50;
    min-width: 160px;
    max-height: 300px;
    overflow-y: auto;
    padding: 4px 0;
}
.cal-dropdown-menu.open {
    display: block;
}
.cal-dropdown-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    font-size: 12px;
    cursor: pointer;
    white-space: nowrap;
}
.cal-dropdown-item:hover {
    background: var(--bg-alt);
}
.cal-dropdown-item input {
    margin: 0;
}

/* Show/hide controls based on active mode */
.cal-std-controls { display: none; }
.cal-ss-controls { display: none; }
.cal-ss-only { display: none; }
[data-active-mode="standard"] .cal-std-controls { display: flex; }
[data-active-mode="spreadsheet"] .cal-ss-controls { display: flex; }
[data-active-mode="spreadsheet"] .cal-ss-only { display: flex; }

.calendar-grid {
    border: 1px solid #999;
    border-radius: 8px;
    overflow-x: auto;
}

.calendar-cell {
    border: 1px solid #999;
    min-height: 90px;
    padding: 4px 6px;
}

.calendar-cell-header {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 4px;
}
.cal-today-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 3px;
    border-radius: 50%;
    background: rgb(234, 88, 12);
    color: #fff;
    font-weight: 700;
    font-size: 11px;
    line-height: 18px;
    margin: -3px 0;
}

.calendar-event {
    /* Two-column layout: name fills available space and truncates with an
       ellipsis; bold date-type label is pinned to the right and never
       compressed. */
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 11px;
    margin-bottom: 2px;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
}
.calendar-event .cal-ev-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.calendar-event .cal-ev-type {
    flex: 0 0 auto;
    font-weight: 700;
    margin-left: auto;
}
.calendar-event.cal-ev-quiet { opacity: 0.5; }

.calendar-event.continuous {
    border-radius: 0;
    margin: 0 -7px 2px -7px;
    padding: 0 6px;
    height: 18px;
    line-height: 18px;
}

/* Calendar milestones */
.calendar-milestone {
    padding: 2px 6px;
    font-size: 10px;
    margin-bottom: 2px;
    color: #333;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background: #f8f8f8;
    border-left: 3px solid #6b7280;
    border-radius: 0 3px 3px 0;
}
.cal-ms-icon {
    font-size: 8px;
    vertical-align: middle;
}
.ss-milestone-marker {
    font-size: 10px;
    position: absolute;
    top: -1px;
    right: 0;
    line-height: 1;
    pointer-events: none;
    text-shadow: 0 0 2px rgba(255,255,255,0.8);
}
.ss-milestone-cell {
    font-weight: 600;
}

/* Spreadsheet calendar */
.spreadsheet-calendar-top-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    height: 12px;
}
.spreadsheet-calendar-top-scroll-inner {
    height: 1px;
}

.spreadsheet-calendar {
    overflow: auto;
    max-height: calc(100vh - 190px);
}

.spreadsheet-calendar table {
    /* `separate` (not `collapse`) so position:sticky works on the
     * <th>/<td class="project-label"> cells. Most browsers don't apply
     * sticky to table cells when borders are collapsed. */
    border-collapse: separate;
    border-spacing: 0;
    width: max-content;
    margin-bottom: 60px;
}

.spreadsheet-calendar th,
.spreadsheet-calendar td {
    border: 1px solid var(--border-light);
    padding: 2px 4px;
    font-size: 11px;
    min-width: 32px;
    text-align: center;
}

.spreadsheet-calendar th {
    background: var(--bg-alt);
    font-weight: 600;
    position: sticky;
    top: 0;
    z-index: 12;
}

.spreadsheet-calendar .ss-month-start {
    border-left: 2px solid #000 !important;
}

.spreadsheet-calendar .ss-weekend {
    background-color: #eff6ff;
}

.spreadsheet-calendar th.ss-weekend {
    background-color: #dbeafe;
}

.spreadsheet-calendar td.ss-today {
    background-color: rgba(249, 115, 22, 0.08);
}

.spreadsheet-calendar th.ss-today {
    background-color: rgba(249, 115, 22, 0.15) !important;
    color: #c2410c;
    font-weight: 700;
}

.ss-dow {
    display: block;
    font-size: 9px;
    font-weight: 400;
    color: var(--text-muted);
    line-height: 1;
}

.spreadsheet-calendar th.project-label {
    z-index: 13;
}

.spreadsheet-calendar .project-label {
    position: sticky;
    left: 0;
    /* Solid opaque background so day cells aren't visible "through" the
     * project label as the user scrolls horizontally. The slightly
     * tinted bg-alt + a strong right border + subtle shadow visually
     * mark this column as its own container, separated from the day
     * grid. */
    background: var(--bg-alt, #f9fafb);
    z-index: 11;
    text-align: left;
    padding: 4px 8px;
    font-weight: 500;
    white-space: nowrap;
    border-right: 2px solid var(--border);
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.08);
    /* Force a fixed column width so the cell holds its size regardless
     * of content. Without this, table-layout:auto sizes the column to
     * fit its longest unbreakable token (the project number link),
     * which collapses the cell down to ~100px and hides the project
     * name even though sticky is working. Clip overflow so long
     * names / statuses / notes don't bleed onto the day grid.
     *
     * EXPANDED mode (default) needs more room to fit the Status +
     * Notes inputs + scroll-to button alongside the name. COMPACT mode
     * (ss-compact class on the container) hides those fields, so a
     * narrower column is plenty. */
    width: 700px;
    min-width: 700px;
    max-width: 700px;
    overflow: hidden;
}
.spreadsheet-calendar .project-label .ss-label-inner {
    overflow: hidden;
    flex-wrap: nowrap;
    min-width: 0;
}
.spreadsheet-calendar .ss-project-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    /* Natural width on the LEFT (shrink/truncate if long), NOT flex-grow — the
     * gap is held by .ss-label-fields' margin-left:auto so status/notes/locate
     * right-anchor to the same columns on every row. */
    flex: 0 1 auto;
}
/* Row mine/watched tints set their own bg on the project label to keep
 * the row indicator visible in compact mode — make sure that bg also
 * stays opaque when sticky-scrolled. */
.spreadsheet-calendar tr:nth-child(even) > .project-label {
    background: #f3f4f6;
}

.ss-label-inner {
    display: flex;
    align-items: center;
    gap: 6px;
    /* Fill the whole 700px label cell so margin-left:auto on .ss-label-fields
     * has room to push the fields (Status/Notes/⌖) to the right edge. Without
     * this the flex row shrink-wraps to its content and the fields sit right
     * after the name — the "columns all over the place" symptom. */
    width: 100%;
    box-sizing: border-box;
}

.ss-label-fields {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    padding-left: 12px;
}

.ss-status-input,
.ss-notes-input {
    border: 1px solid var(--border-light);
    background-color: transparent;
    font-size: 10px;
    font-family: inherit;
    outline: none;
    transition: border-color 0.2s, background-color 0.2s;
    padding: 2px 4px;
    border-radius: 3px;
    /* Fixed columns — neither grow nor shrink, so Status/Notes line up across
     * every row (the name shrinks/truncates instead when space is tight). */
    flex: 0 0 auto;
}
.ss-status-input { width: 90px; }
.ss-notes-input { width: 120px; }
span.ss-lazy-field {
    display: inline-block;
    cursor: text;
    height: 18px;
    vertical-align: middle;
    /* Inline labels were overflowing their fixed width when not focused
     * — clip + ellipsis so a long status/note doesn't crash into the
     * adjacent field. The full value still appears once the user
     * clicks in (the field expands into an editable input). */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Compact mode: hides Status + Notes inside the project label cell so
 * the column becomes "name + scroll-to-btn + days". The scroll-to
 * crosshair stays visible (it's the most useful button in either
 * mode). `display: contents` on the wrapper makes its box disappear
 * but lets the surviving children flow into the parent flex layout. */
.spreadsheet-calendar.ss-compact .ss-label-fields {
    display: contents;
}
.spreadsheet-calendar.ss-compact .ss-label-fields .ss-field-label,
.spreadsheet-calendar.ss-compact .ss-label-fields .ss-status-input,
.spreadsheet-calendar.ss-compact .ss-label-fields .ss-notes-input,
.spreadsheet-calendar.ss-compact .ss-project-number,
.spreadsheet-calendar.ss-compact .ss-no-order-number {
    display: none;
}
.spreadsheet-calendar.ss-compact .project-label {
    width: 240px !important;
    min-width: 240px !important;
    max-width: 240px !important;
}
/* Project name in compact mode: stay one line. The .ss-label-inner
 * wrapper handles horizontal overflow scroll, so we don't need to
 * clip — long names just push past the visible viewport and the user
 * scrolls sideways. */
.spreadsheet-calendar .ss-project-name {
    white-space: nowrap;
}
.ss-compact-toggle {
    position: absolute;
    top: 2px;
    right: 4px;
    width: 18px;
    height: 18px;
    line-height: 16px;
    text-align: center;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: #fff;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 11px;
    padding: 0;
    z-index: 5;
}
.ss-compact-toggle:hover {
    background: var(--bg-alt);
    color: var(--text);
}
/* (Removed `th.project-label { position: relative }` — that override
 * was knocking out position:sticky on the header label cell. Sticky
 * is itself a positioning context, so the .ss-compact-toggle absolute
 * button still anchors correctly inside the sticky header. */
.ss-status-input:focus,
.ss-notes-input:focus,
.ss-crew-input:focus {
    border-color: var(--border);
    background: #fff;
}
.ss-save-pending,
.ss-save-pending:focus {
    border-color: #eab308 !important;
    background-color: #fefce8 !important;
}
.ss-save-ok,
.ss-save-ok:focus {
    border-color: #22c55e !important;
    background-color: #dcfce7 !important;
}
.ss-save-err,
.ss-save-err:focus {
    border-color: #ef4444 !important;
    background-color: #fee2e2 !important;
}
.ss-field-label {
    font-size: 9px;
    color: var(--text-muted);
    font-weight: 400;
    white-space: nowrap;
    flex: 0 0 auto;   /* fixed — part of the right-anchored field block */
}

.ss-scroll-to-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 14px;
    color: var(--text-muted);
    padding: 0 2px;
    line-height: 1;
    flex: 0 0 auto;   /* locate icon pinned at the far right, never shrinks */
    margin-left: 4px;
}
.ss-scroll-to-btn:hover {
    color: #2563eb;
}

/* Day cell selection */
.ss-day-cell {
    cursor: cell;
    user-select: none;
    position: relative;
}
.ss-drag-active {
    cursor: cell !important;
    user-select: none !important;
}
.ss-day-cell.ss-selected {
    outline: 2px solid #2563eb !important;
    outline-offset: -2px;
}

/* Date type dropdown */
.ss-dt-dropdown {
    display: flex;
    align-items: center;
    gap: 4px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 4px 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.ss-dt-input {
    font-size: 12px;
    padding: 3px 6px;
    border: 1px solid var(--border-light);
    border-radius: 3px;
    outline: none;
    width: 140px;
}
.ss-dt-input:focus {
    border-color: #2563eb;
}
.ss-dt-clear-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 16px;
    color: #991b1b;
    padding: 0 4px;
    line-height: 1;
}
.ss-dt-clear-btn:hover {
    color: #dc2626;
}

/* Multi-datetype day editor (single day OR a selected range): vertical rows. */
.ss-mt-editor { display: block; min-width: 210px; padding: 8px; }
.ss-mt-title { font-size: 12px; font-weight: 700; color: #374151; margin-bottom: 6px; }
.ss-mt-sub { display: block; font-size: 10px; font-weight: 400; color: #9ca3af; }
.ss-mt-rows { display: block; }
.ss-mt-row { display: flex; align-items: center; gap: 4px; margin-bottom: 4px; }
.ss-mt-row .ss-mt-input { flex: 1 1 auto; width: auto; }
.ss-mt-x {
    background: #fff; border: 1px solid #fecaca; color: #b91c1c; border-radius: 4px;
    width: 22px; height: 22px; line-height: 1; cursor: pointer; font-weight: 700; flex: 0 0 auto;
}
.ss-mt-x:hover { background: #fef2f2; }
.ss-mt-row.ss-mt-cleared .ss-mt-input { text-decoration: line-through; opacity: .5; }
.ss-mt-add {
    background: none; border: 0; color: #2563eb; cursor: pointer; font-size: 11px;
    padding: 2px 0; margin: 2px 0 6px;
}
.ss-mt-actions { display: flex; gap: 6px; }
.ss-mt-save {
    flex: 1 1 auto; background: #16a34a; color: #fff; border: 0; border-radius: 4px;
    padding: 4px 0; cursor: pointer; font-weight: 600;
}
.ss-mt-cancel {
    background: #f3f4f6; border: 1px solid var(--border); border-radius: 4px;
    padding: 4px 10px; cursor: pointer;
}

.ss-drag-handle {
    cursor: grab;
    color: var(--text-muted);
    font-size: 11px;
    margin-right: 4px;
    user-select: none;
}
.ss-drag-handle:hover { color: var(--text); }

.ss-dragging {
    opacity: 0.4;
}
.ss-drag-over > td:first-child {
    box-shadow: 0 2px 0 #2563eb inset;
}
.ss-drag-over {
    outline: 2px solid #2563eb;
    outline-offset: -2px;
}
.ss-crew-drag-handle {
    cursor: grab;
    color: var(--text-muted);
    margin-right: 3px;
    font-size: 10px;
    user-select: none;
}
.ss-crew-drag-handle:hover { color: var(--text); }
.ss-crew-dragging {
    opacity: 0.4;
    background: #dbeafe !important;
}

.ss-move-btn {
    background: none;
    border: none;
    font-size: 10px;
    cursor: pointer;
    color: var(--text-muted);
    padding: 0 2px;
    margin-right: 2px;
}
.ss-move-btn:hover { color: var(--text); }
.ss-move-reset { color: #991b1b; }

.ss-project-label-cell {
    position: relative;
    overflow: visible !important;
}
.ss-insert-btn {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid var(--border-light);
    background: var(--bg);
    color: var(--text-muted);
    font-size: 12px;
    line-height: 14px;
    text-align: center;
    cursor: pointer;
    padding: 0;
    opacity: 0;
    transition: opacity 0.15s;
}
.ss-insert-btn-inline {
    position: absolute;
    left: 4px;
    top: -8px;
    z-index: 2;
}
/* Calendar spreadsheet: mine/watched row indicators */
.spreadsheet-calendar tr.ss-row-mine > .project-label {
    border-left: 3px solid #3B82F6;
}
.spreadsheet-calendar tr.ss-row-watched > .project-label {
    border-left: 3px solid #f59e0b;
}

.spreadsheet-calendar tr:hover > .project-label {
    z-index: 15;
    background: #fefce8;
}
.spreadsheet-calendar tr:hover > td,
.spreadsheet-calendar tr:hover > th {
    box-shadow: inset 0 1px 0 #94a3b8, inset 0 -1px 0 #94a3b8;
}
.ss-project-label-cell:hover .ss-insert-btn-inline,
tr:hover .ss-insert-btn-inline,
.ss-vrow-addrole:hover .ss-insert-btn {
    opacity: 1;
}
.ss-insert-btn:hover {
    background: #2563eb;
    color: #fff;
    border-color: #2563eb;
}

.ss-insert-form {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    background: #eff6ff;
}
.ss-insert-name { width: 200px; font-size: 12px; }
.ss-insert-number { width: 120px; font-size: 12px; }

.ss-project-number {
    color: var(--text-muted);
    font-size: 10px;
    margin-left: 4px;
    text-decoration: none;
}
.ss-project-number:hover {
    text-decoration: underline;
    color: #2563eb;
}
/* "ORDER"/"QUOTE" prefix before the number — small, bold, muted. */
.ss-order-kind {
    font-weight: 700;
    letter-spacing: 0.03em;
    margin-right: 3px;
    opacity: 0.8;
}
.ss-no-order-number {
    color: #dc2626;
    font-size: 10px;
    margin-left: 4px;
    text-decoration: none;
    font-style: italic;
    opacity: 0.75;
}
.ss-no-order-number:hover {
    text-decoration: underline;
    opacity: 1;
}

/* Spreadsheet crew sub-rows */
.ss-caret {
    cursor: pointer;
    display: inline-block;
    width: 14px;
    font-size: 10px;
    user-select: none;
}

.ss-crew-row td {
    background: #f8fafc;
}

.ss-crew-label {
    font-weight: 400 !important;
    padding-left: 12px !important;
    font-size: 10px !important;
    color: #64748b;
    min-width: 280px !important;
    background: #f8fafc !important;
}

.ss-crew-label-inner {
    display: flex;
    align-items: center;
    gap: 4px;
}

.ss-crew-role-name {
    flex-shrink: 0;
    font-weight: 600;
    color: #475569;
    min-width: 80px;
}
input.ss-crew-role-name {
    width: 90px;
    font-size: 11px;
    padding: 0 2px;
    border: 1px solid #000;
    border-radius: 3px;
}
span.ss-crew-role-name {
    cursor: text;
}

.ss-crew-input {
    border: 1px solid #000;
    border-radius: 4px;
    padding: 1px 4px;
    font-size: 10px;
    flex: 1;
    min-width: 80px;
    background: #fff;
}

/* Labor black-box controls on a calendar crew row: status (autocomplete),
   note, and the public toggle. Thin black borders so they read against the
   near-white row background. */
.ss-crew-status,
.ss-crew-note {
    border: 1px solid #000;
    border-radius: 4px;
    padding: 1px 4px;
    font-size: 10px;
    background: #fff;
    cursor: text;
    flex: 0 0 auto;
    min-width: 14px;
    text-align: center;
}
.ss-crew-status { min-width: 54px; text-align: left; }
.ss-crew-note { min-width: 40px; }
/* Empty status/note cells: show a muted placeholder so the cell stays a
   comfortable click target instead of collapsing to a tiny dot. */
.ss-crew-status.ss-lazy-field:empty::before { content: 'status'; }
.ss-crew-note.ss-lazy-field:empty::before { content: 'note'; }
.ss-crew-status.ss-lazy-field:empty::before,
.ss-crew-note.ss-lazy-field:empty::before {
    color: var(--text-muted);
    font-style: italic;
    opacity: 0.7;
}
.ss-crew-public-btn {
    border: 1px solid #000 !important;
    border-radius: 4px;
    padding: 0 4px !important;
    line-height: 1.4;
    background: #fff;
    color: #888;
}
.ss-crew-public-btn.is-public {
    color: #1a7f37;
    border-color: #1a7f37 !important;
    font-weight: 700;
}

/* Read-only crew name for viewers without build_labor; masked private
   assignments show "tentative" in muted italic (role stays visible). */
.ss-crew-private .ss-crew-role-name,
.ss-crew-label-inner > .ss-crew-input:not(.ss-lazy-field) {
    padding: 1px 4px;
    font-size: 10px;
}
.ss-crew-tentative {
    font-style: italic;
    color: var(--text-muted, #888);
    border: 1px dashed var(--border-light);
    border-radius: 4px;
    background: transparent;
}

.ss-crew-search-btn {
    padding: 0 4px !important;
    font-size: 10px !important;
    line-height: 1.4 !important;
    flex-shrink: 0;
    border: 1px solid #000 !important;
    border-radius: 4px;
}

.ss-crew-delete-btn {
    padding: 0 4px !important;
    font-size: 12px !important;
    line-height: 1.2 !important;
    flex-shrink: 0;
    color: var(--text-light);
    border: 1px solid #000 !important;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.15s, color 0.15s;
}

.ss-crew-label-inner:hover .ss-crew-delete-btn {
    opacity: 1;
}

.ss-crew-delete-btn:hover {
    color: var(--danger);
}

.ss-crew-cell {
    font-size: 9px !important;
    white-space: nowrap;
    user-select: none;
    -webkit-user-select: none;
}
.ss-crew-toggle:hover {
    opacity: 0.7;
}

/* Pending-change pulse — ALL staged cells pulse in UNISON. Rather than give
   each cell its own animation (which starts whenever that cell is painted, so
   they drift out of phase — and the spreadsheet virtual-scrolls, repainting
   cells constantly), a SINGLE animation drives one inherited custom property on
   the grid container; every pending cell just reads it. Any cell — including
   one scrolled into view a moment ago — shows the same shared phase, so they
   all breathe together. (Owner: "one pulse for all".) */
/* PULSE ON THE CELLS, NOT ON AN ANCESTOR.
   This used to animate an INHERITED custom property (--ss-pulse) on #ss-rows —
   the common ancestor of every cell in the grid — to keep all pending cells in
   perfect unison. It worked, but an animated inherited property invalidates
   style for EVERY descendant on every frame, so the whole spreadsheet had its
   style recomputed 60x/sec. The original comment said as much; the mitigation
   was to run it only while changes are staged, which is exactly when the user
   is working. Owner: "the page becomes annoyingly slow when changes are
   pending."

   Animating `opacity` directly on the few pending cells is compositor-only:
   no style recalc, no layout, no paint, and nothing outside those cells is
   touched. Unison is preserved by ssResyncPulse() in calendar.js, which
   restarts the animation on all pending cells whenever one is added — a
   one-off restyle of a handful of elements instead of a permanent 60fps tax on
   thousands. */
@keyframes ss-pulse-opacity {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.4; }
}
.ss-crew-pending,
.ss-crew-pending-remove,
.ss-date-pending,
.ss-date-pending-remove {
    animation: ss-pulse-opacity 1.2s ease-in-out infinite;
}
.ss-crew-pending-remove {
    box-shadow: inset 0 0 0 2px #dc2626; background: #fee2e2;
}
/* Staged (not-yet-saved) date-type change: create/retype = blue tint, delete =
   red tint. */
.ss-date-pending {
    box-shadow: inset 0 0 0 2px #2563eb;
}
.ss-date-pending-remove {
    box-shadow: inset 0 0 0 2px #dc2626; background: #fee2e2;
}
.ss-float-error {
    position: fixed;
    transform: translate(-50%, -100%);
    background: #dc2626;
    color: #fff;
    font-size: 12px;
    padding: 4px 10px;
    border-radius: 4px;
    white-space: nowrap;
    pointer-events: none;
    z-index: 9999;
    box-shadow: 0 2px 6px rgba(0,0,0,.25);
    transition: opacity .6s;
}
.ss-float-error-fade {
    opacity: 0;
}

/* In-flow (above the date grid) so the Save/Cancel bar no longer floats over
   and obscures the dates. The slot RESERVES its height at all times — empty or
   full — so showing/hiding the bar never reflows the grid and never shifts what
   is under the mouse (owner: "unhiding it pushes the calendar down"). */
#ssCrewPendingBarSlot {
    display: flex;
    justify-content: center;
    align-items: center;
    pointer-events: none;
    min-height: 42px;
    margin: 2px 0 4px;
}

/* Pulsing "dates filtered" warning — sits above the dates next to the pending
   bar; click to clear the date-type filter back to all. */
@keyframes ss-filter-warn-pulse {
    0%, 100% { background: #fef3c7; }
    50% { background: #fde68a; }
}
.ss-date-filter-warn {
    text-align: center;
    font-size: 12px;
    font-weight: 600;
    color: #92400e;
    background: #fef3c7;
    border: 1px solid #fcd34d;
    border-radius: 6px;
    padding: 4px 10px;
    margin: 2px auto 4px;
    max-width: max-content;
    cursor: pointer;
    animation: ss-filter-warn-pulse 1.1s ease-in-out infinite;
}
.ss-crew-pending-bar {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    background: #fef3c7;
    border: 1px solid #f59e0b;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    color: #92400e;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    white-space: nowrap;
}
.ss-crew-pending-bar .pending-icon {
    font-size: 16px;
}
.ss-crew-pending-bar .pending-text {
    flex: 1;
}
.ss-crew-pending-bar .btn {
    font-size: 12px;
    padding: 4px 12px;
}

/* ==========================================================================
   Virtualized spreadsheet (CSS-grid skeleton, DayRole model)
   #ss-scroll > #ss-head + #ss-spacer > #ss-rows > .ss-vrow
   Explicit widths (set inline by calendar.js) + sticky recipe verified in
   the spike. Only rows are virtualized; the 365 day columns are real.
   ========================================================================== */
:root {
    --ss-row-h: 24px;   /* MUST match SS_ROW_H in calendar.js (read from here) */
    --ss-col-w: 32px;   /* per-day column width */
}

#ss-scroll { position: relative; }

#ss-head {
    display: grid;
    position: sticky;
    top: 0;
    z-index: 12;
}
#ss-head .ss-head-cell,
#ss-head .ss-head-label {
    border: 1px solid var(--border-light);
    border-top: none;
    padding: 2px 4px;
    font-size: 11px;
    line-height: 1.1;
    background: var(--bg-alt);
    font-weight: 600;
    overflow: hidden;
    box-sizing: border-box;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
#ss-head .ss-head-label {
    position: sticky;
    left: 0;
    z-index: 13;
    /* Same checkerboard exemption as .ss-vrow > .project-label. */
    will-change: transform;
    text-align: left;
    align-items: flex-start;
    background: var(--bg-alt, #f9fafb);
    border-right: 2px solid var(--border);
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.08);
}

#ss-spacer { position: relative; }
#ss-rows {
    position: absolute;
    top: 0;
    left: 0;
}

/* Ring-buffer recycler (see VL.createFixed in virtual_list.js): each pooled
   row is independently `top: idx*rowH` positioned (set inline by the engine)
   rather than living in normal document flow inside a single translateY'd
   container — a row that stays inside the scroll window keeps its DOM
   position/content untouched across scrolls, so only rows actually entering
   the window get repositioned + refilled. position/left/width are set
   inline by the engine at row-creation time; only the shared per-row look
   (grid template, height, borders) lives here. */
.ss-vrow {
    display: grid;
    height: var(--ss-row-h);
    box-sizing: border-box;
}
/* Every grid child (label + day cells) shares the base cell frame. */
.ss-vrow > div {
    border: 1px solid var(--border-light);
    border-top: none;
    box-sizing: border-box;
    overflow: hidden;
    text-align: center;
    font-size: 9px;
    line-height: calc(var(--ss-row-h) - 5px);
    white-space: nowrap;
}
/* Grid tracks control the label width; neutralise the table-era fixed widths
   so the sticky label fills exactly its column (700px / 240px compact). */
#ss-head .ss-head-label,
.ss-vrow > .project-label {
    width: auto;
    min-width: 0;
    max-width: none;
}
.ss-vrow > .project-label {
    position: sticky;
    left: 0;
    z-index: 11;
    /* Own compositor layer: during fast leftward scrolls the freshly
     * exposed left edge (where these sticky labels sit) can outrun tile
     * rasterization and flash blank tiles OVER the labels. Keeping the
     * labels pre-rasterized on their own layer exempts them from that
     * checkerboarding. ~50 layers x 700x24px — negligible memory. */
    will-change: transform;
    background: var(--bg-alt, #f9fafb);
    text-align: left;
    font-size: 11px;
    line-height: normal;
    padding: 0 8px;
    display: flex;
    align-items: center;
    border-right: 2px solid var(--border);
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.08);
}
.ss-vrow.ss-vrow-crew > .project-label,
.ss-vrow.ss-vrow-addrole > .project-label {
    background: #f8fafc;
    font-size: 10px;
}
.ss-vrow-crew .ss-crew-label-inner,
.ss-add-role-label { display: flex; align-items: center; gap: 4px; width: 100%; }

/* Row tints on div day cells (table variants used td/th selectors). */
.ss-vrow > .ss-weekend { background-color: #eff6ff; }
.ss-vrow > .ss-today { background-color: rgba(249, 115, 22, 0.08); }
/* Today's HEADER cell is sticky — its orange must be OPAQUE or rows scroll
   through it. Layer the translucent tint over the header's own base so it stays
   tinted but solid (works in light + dark). */
#ss-head .ss-today {
    background: linear-gradient(rgba(249, 115, 22, 0.15), rgba(249, 115, 22, 0.15)), var(--bg-alt);
    color: #c2410c;
}
/* Make TODAY's column unmistakable: near-pure bright-red left + right edges,
   drawn as INSET box-shadows so they add no width (the column can't shift). Each
   cell in the column contributes its own edges, so they stack into two solid red
   lines bracketing the column top-to-bottom (header included). */
.ss-vrow > .ss-today,
#ss-head .ss-today,
.spreadsheet-calendar td.ss-today,
.spreadsheet-calendar th.ss-today {
    box-shadow: inset 3px 0 0 0 #f50c0c, inset -3px 0 0 0 #f50c0c;
}
.ss-vrow > .ss-day-cell,
.ss-vrow > .ss-crew-cell { cursor: cell; }
/* mine / watched row indicators */
.ss-vrow.ss-row-mine > .project-label { border-left: 3px solid #3B82F6; }
.ss-vrow.ss-row-watched > .project-label { border-left: 3px solid #f59e0b; }
.ss-vrow:hover > .project-label { background: #fefce8; z-index: 15; }
/* Whole-row hover highlight. The sticky label AND every day cell are grid
   children of one .ss-vrow, so a pure :hover lights the whole row no matter
   which part the cursor is over — no JS, and it survives row recycling (a
   pseudo-class isn't node state the fill cache can wipe). Day cells carry
   their own OPAQUE backgrounds (weekend/today tints, milestone fills), so a
   plain row `background` would only show in the gaps; instead we paint an
   inset box-shadow on each child — a full-bleed translucent yellow fill (huge
   spread so it covers the 700px label too) plus an amber line top+bottom.
   box-shadow paints over the cell's own background, so the tint reads across
   the entire row. Applied via box-shadow only (no filter/transform on an
   ancestor of the sticky label), so the frozen label column keeps sticking. */
.ss-vrow:hover > div {
    box-shadow: inset 0 1px 0 rgba(202, 138, 4, 0.55),
                inset 0 -1px 0 rgba(202, 138, 4, 0.55),
                inset 0 0 0 1000px rgba(250, 204, 21, 0.14);
}

.ss-float-form {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    background: #eff6ff;
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}


/* ---- Crew Schedule ---- */

.crew-date-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 1px 0;
    padding: 4px 6px;
    margin-bottom: 4px;
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.crew-date-nav-link {
    font-size: 11px;
    padding: 2px 5px;
    color: var(--primary);
    text-decoration: none;
    border-radius: 3px;
    white-space: nowrap;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    line-height: 1.1;
}

.crew-date-nav-type {
    font-size: 7px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
    max-width: 32px;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1;
}
/* Non-working RW property date label in the nav (Start/Pick/Stop) — italic and
   fainter so it reads as reference, distinct from working day-type labels. */
.crew-date-nav-type.crew-date-nav-prop {
    font-style: italic;
    text-transform: none;
    opacity: 0.75;
}

.crew-date-nav-link:hover {
    background: var(--primary);
    color: #fff;
}

.crew-date-nav-link.dark {
    color: var(--text-muted);
}

.crew-date-nav-link.today {
    font-weight: 700;
    border-bottom: 2px solid rgb(234, 88, 12);
    border-radius: 0;
}

.crew-date-nav-link.nav-active {
    background: var(--primary);
    color: #fff;
    font-weight: 700;
}

/* Bottom-panel date nav: same look as top, but doesn't navigate — clicks
   toggle the shared `selectedDates` set (handleHeaderClick). The .selected
   class is applied by applySelectionClass() in lockstep with the timeline
   day-headers, so picking days in either place keeps both in sync. */
.crew-date-nav-bottom { margin: 4px 6px; }
.crew-bottom-date-link.selected {
    background: #c7d2fe;
    color: #1e293b;
    font-weight: 700;
}

/* Meal rows in the bottom-panel task lists (Day's Tasks + All Tasks).
   Greyed-out "unavailable time" treatment — matches the spreadsheet
   meal row style. Bold so the row reads as a fixed slot. */
.assigned-task-row.assigned-task-meal {
    background: #e5e7eb;
    border-left: 2px solid #9ca3af;
    color: #6b7280;
    font-weight: 600;
}
.assigned-task-row.assigned-task-meal:hover {
    background: #d1d5db;
}

/* ---------------------------------------------------------------
   Spreadsheet view (production schedule). Lives between the
   timeline and the bottom grid. Collapsible.
   --------------------------------------------------------------- */
.sheet-view-wrap {
    margin: 12px 0;
    border: 1px solid var(--border, #e5e7eb);
    border-radius: 6px;
    background: #fff;
}
.sheet-view-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 6px 10px;
    border-bottom: 1px solid var(--border-light, #e5e7eb);
    background: #f9fafb;
    border-radius: 6px 6px 0 0;
}
.sheet-view-collapse {
    background: transparent;
    border: none;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.sheet-view-title { font-size: 13px; }
.sheet-view-body { padding: 8px 10px; }
.sheet-day { margin-bottom: 18px; }
.sheet-day-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    border-bottom: 1px solid var(--border-light, #e5e7eb);
}
.sheet-day-meta {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 12px;
    margin: 6px 0 8px;
}
/* EOD goals textarea: shrink to one row by default — `textarea.form-control`
 * sets min-height:80px globally, which we explicitly override here. The
 * resize handle stays so the user can grow it for longer goal lists. */
textarea.sheet-eod-goals {
    min-height: 0;
}
.sheet-meta-cell label {
    display: block;
    font-size: 10px;
    font-weight: 600;
    color: var(--text-muted, #6b7280);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.sheet-heq-rollup {
    padding: 4px 6px;
    border: 1px solid var(--border-light, #e5e7eb);
    border-radius: 3px;
    font-size: 12px;
    background: #fafafa;
    min-height: 28px;
}
.sheet-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    /* Fixed layout + explicit per-column widths below so every day's
       table aligns to the same grid. Without this, each day sizes its
       own columns from its content. */
    table-layout: fixed;
}
/* Column widths (kept tight for the right-side columns; the wider
   Description / Name flex with the remaining space). */
.sheet-table col.sheet-col-w-handle { width: 18px; }
.sheet-table col.sheet-col-w-sel { width: 24px; }
.sheet-table col.sheet-col-w-date { width: 60px; }
.sheet-table col.sheet-col-w-time { width: 70px; }
.sheet-table col.sheet-col-w-dur { width: 60px; }
.sheet-table col.sheet-col-w-team { width: 110px; }
.sheet-table col.sheet-col-w-leads { width: 40px; }
.sheet-table col.sheet-col-w-name { width: 160px; }
.sheet-table col.sheet-col-w-desc { width: 220px; }
.sheet-table col.sheet-col-w-loc { width: 100px; }
.sheet-table col.sheet-col-w-vendor { width: 100px; }
.sheet-table col.sheet-col-w-contact { width: 100px; }
.sheet-table col.sheet-col-w-heq { width: 110px; }
.sheet-table col.sheet-col-w-sh { width: 110px; }
.sheet-table col.sheet-col-w-actions { width: 28px; }
.sheet-table th, .sheet-table td {
    border: 1px solid #9ca3af;
    padding: 2px 4px;
    vertical-align: middle;
}
.sheet-table th {
    background: #f3f4f6;
    text-align: left;
    font-weight: 600;
    font-size: 11px;
    white-space: nowrap;
}
.sheet-col-sel { width: 22px; text-align: center; }
.sheet-col-handle { width: 18px; text-align: center; padding: 0 !important; }
.sheet-drag-handle {
    cursor: grab;
    color: #9ca3af;
    font-size: 12px;
    line-height: 1;
    user-select: none;
    padding: 4px 2px;
    display: inline-block;
}
.sheet-drag-handle:hover { color: #4338ca; background: #eef2ff; border-radius: 3px; }
.sheet-drag-handle:active { cursor: grabbing; }
.sheet-row-dragging { opacity: 0.35; }
.sheet-row-loading td {
    border-color: #ca8a04;
}
.sheet-row-error td {
    border-color: #dc2626;
}
.sheet-row-success td {
    animation: sheet-row-success-fade 2500ms forwards;
}
@keyframes sheet-row-success-fade {
    0%, 80% {
        border-color: #16a34a;
    }
    100% {
        border-color: #9ca3af;
    }
}
.sheet-row-drop-target {
    box-shadow: inset 0 2px 0 #6366f1;
}
.sheet-row-drop-ghost {
    background: #eef2ff !important;
    color: #4338ca;
    opacity: 0.85;
    box-shadow: inset 0 0 0 2px #6366f1;
}
.sheet-row-drop-ghost td {
    border-top: 1px dashed #6366f1 !important;
    border-bottom: 1px dashed #6366f1 !important;
}
.sheet-row-drop-ghost input,
.sheet-row-drop-ghost select,
.sheet-row-drop-ghost textarea {
    color: #4338ca;
    background: transparent;
}
.sheet-row-drop-ghost .sheet-drag-handle {
    visibility: hidden;
}
/* Affected by an in-progress cascade preview: bump the time text indigo
   so the user sees what's about to change. The original text is kept in
   data-original so we can restore on dragleave. */
.sheet-row-cascade-preview .sheet-cell-time,
.sheet-row-cascade-preview td.sheet-col-date,
.sheet-row-cascade-preview td:nth-child(5) {
    color: #4338ca;
    font-weight: 700;
}
.sheet-cell-input {
    border: none;
    background: transparent;
    width: 100%;
    font: inherit;
    padding: 2px 4px;
    box-sizing: border-box;
}
.sheet-cell-input:focus {
    outline: 2px solid #c7d2fe;
    outline-offset: -2px;
    background: #fff;
}
.sheet-cell-readonly { color: var(--text-muted, #6b7280); }
/* Trucking rows — direction-tinted background. Pickup leans purple,
 * delivery / both leans green. Bold like meals so the row reads as
 * a fixed slot rather than work. */
.sheet-row-trucking-delivery,
.sheet-row-trucking-delivery td {
    background: #d1fae5 !important;   /* light green */
    font-weight: 600;
}
.sheet-row-trucking-pickup,
.sheet-row-trucking-pickup td {
    background: #ede9fe !important;   /* light purple */
    font-weight: 600;
}
.sheet-row-trucking .sheet-cell-input {
    background: transparent;
    font-weight: 600;
}

/* Meal rows render as "unavailable" time — greyed BG, muted text, and
 * bold so the row reads as a fixed slot rather than a regular work
 * task. Applies in every mode (Color up team-tinting is suppressed
 * for meals on the JS side). */
.sheet-row-meal,
.sheet-row-meal td {
    background: #e5e7eb !important;
    color: #6b7280;
    font-weight: 600;
}
.sheet-row-meal .sheet-cell-input {
    background: transparent;
    color: #6b7280;
    font-weight: 600;
}
.sheet-row-meal .sheet-cell-readonly {
    color: #9ca3af;
}
.sheet-row-selected {
    background: #eef2ff;
    box-shadow: inset 3px 0 0 #6366f1, inset -1px 0 0 #6366f1;
}
.sheet-row-selected td {
    border-top-color: #6366f1;
    border-bottom-color: #6366f1;
}
.sheet-row-note { background: #f0fdf4; }
.sheet-col-date {
    font-variant-numeric: tabular-nums;
    color: var(--text-muted, #6b7280);
    white-space: nowrap;
}
/* End cell turns red when the END time crosses past midnight. Inherits
   the readonly muted color otherwise. */
.sheet-cell-end-overnight .sheet-cell-readonly {
    color: #dc2626;
    font-weight: 700;
}

/* Overnight tasks: same shift_day, but start (or end) lands on the next
   calendar day. Diagonal stripe + "+" suffix on the value (added in JS). */
.sheet-cell-overnight {
    background-image: repeating-linear-gradient(
        45deg,
        rgba(99,102,241,0.08),
        rgba(99,102,241,0.08) 4px,
        rgba(99,102,241,0.18) 4px,
        rgba(99,102,241,0.18) 8px
    );
    color: #4338ca;
    font-weight: 600;
}

/* Extend Timeline mode: each day's grid grows down to fit overnight tasks
   in-place, so we hide the "wraps to next day" / "from previous day" labels
   and grow the track height by the per-day overflow (set as a CSS variable
   on each track by JS from data-overflow-minutes). */
body.crew-extend-timeline .crew-timeline-continues-label,
body.crew-extend-timeline .crew-overflow-gutter,
body.crew-extend-timeline .crew-overflow-gutter-label,
body.crew-extend-timeline .crew-overflow-gutter-stripe {
    display: none !important;
}
/* In extend mode the next day's content shouldn't reserve gutter space —
   the previous day already shows the overnight portion in-place. */
body.crew-extend-timeline .has-overflow-gutter .crew-timeline-generic,
body.crew-extend-timeline .has-overflow-gutter .crew-team-labels,
body.crew-extend-timeline .has-overflow-gutter .crew-timeline-track {
    margin-left: 0 !important;
}
/* The day column itself is sized to (col_units + 1) * 100px to leave room
   for the inbound gutter. In extend mode the gutter is hidden, so drop the
   extra unit — otherwise the team-labels grid (col_units columns of 1fr)
   stretches across the inflated width and labels drift right of their
   timeline lanes. */
body.crew-extend-timeline .crew-day-column.crew-day-has-inbound {
    min-width: max(200px, calc(var(--col-units, 2) * 100px)) !important;
    width: max(200px, calc(var(--col-units, 2) * 100px)) !important;
}
body.crew-extend-timeline .crew-timeline-track {
    height: calc((1440 + var(--day-overflow-min, 0)) * var(--ppm) * 1px);
}
body.crew-extend-timeline .crew-ruler-track {
    /* Match the tallest day so the ruler stays aligned with extended cols. */
    height: calc((1440 + var(--max-overflow-min, 0)) * var(--ppm) * 1px);
}
.sheet-row-note .sheet-cell-note-body { font-style: italic; }
.sheet-cell-note-label {
    font-weight: 600;
    text-align: left;
    white-space: nowrap;
    color: #4338ca;
}
/* Floating insert buttons that appear to the LEFT of each row on hover.
 * `position: relative` belongs on the handle cell, not the <tr>; setting
 * it on the row breaks table border-collapse rendering (top/bottom edges
 * disappear, especially noticeable on meal rows). */
.sheet-row-task .sheet-col-handle,
.sheet-row-note .sheet-col-handle { position: relative; }
.sheet-row-insert-btn {
    position: absolute;
    left: -28px;
    width: 22px;
    height: 18px;
    background: #eef2ff;
    color: #4338ca;
    border: 1px solid #4338ca;
    border-radius: 3px;
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    padding: 0;
    z-index: 5;
}
.sheet-row-insert-btn:hover {
    background: #c7d2fe;
}
.sheet-row-insert-above { top: -10px; }
.sheet-row-insert-below { bottom: -10px; display: none; }
/* Only the last task/note row of each day gets a below button so the
 * gap between every pair of rows has exactly one insert point. */
.sheet-row-task:last-of-type .sheet-row-insert-below,
.sheet-row-note:last-of-type .sheet-row-insert-below {
    display: block;
}
.sheet-row-task:hover .sheet-row-insert-btn,
.sheet-row-note:hover .sheet-row-insert-btn,
.sheet-row-insert-btn:hover {
    opacity: 1;
}
.sheet-row-draft td {
    background: #eef2ff;
}
.sheet-team-popover {
    background: #fff;
    border: 1px solid #4338ca;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.15);
    padding: 4px 0;
    min-width: 180px;
    max-height: 280px;
    overflow-y: auto;
    font-size: 12px;
}
.sheet-team-pop-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    cursor: pointer;
    user-select: none;
}
.sheet-team-pop-row:hover {
    background: #eef2ff;
}
.sheet-team-pop-create {
    color: #4338ca;
    font-weight: 600;
    border-top: 1px dashed #c7d2fe;
}
.sheet-row-draft td:first-child {
    border-left: 2px solid #6366f1;
}
.sheet-row-draft .sheet-col-handle {
    color: #4338ca;
}
.sheet-row-note.sheet-row-goal {
    background: #fefce8;
    border-left: 3px solid #ca8a04;
}
.sheet-bulk-bar {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    background: #1e293b;
    color: #fff;
    border-top: 1px solid var(--border, #e5e7eb);
    border-radius: 0 0 6px 6px;
}
.sheet-bulk-bar .form-control { color: #1e293b; }
.sheet-bulk-bar .btn { background: #fff; color: #1e293b; }
.sheet-bulk-bar .btn-danger { background: #dc2626; color: #fff; }

.crew-snap-select {
    margin-left: auto;
    font-size: 11px;
    padding: 2px 4px;
    border: 1px solid var(--border);
    border-radius: 3px;
    background: #fff;
    align-self: center;
    flex-shrink: 0;
}

.crew-grid-with-pool {
    display: flex;
    gap: 0;
    align-items: stretch;
}

.crew-grid-with-pool > .crew-grid {
    flex: 1;
    min-width: 0;
}

.crew-pool-sidebar {
    flex-shrink: 0;
    width: 200px;
    min-width: 200px;
    background: #fff;
    border: 1px solid var(--border);
    border-left: none;
    border-radius: 0 8px 8px 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.crew-tools-sidebar {
    flex-shrink: 0;
    width: 280px;
    min-width: 280px;
    background: #fff;
    border: 1px solid var(--border);
    border-left: none;
    border-radius: 0;
    overflow-y: auto;
    padding: 10px;
    font-size: 12px;
}
.crew-tools-sidebar + .crew-pool-sidebar {
    /* hide pool sidebar when tools panel is open */
}
.crew-tools-sidebar .tool-card {
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 6px 6px;
    padding: 10px;
    background: var(--bg);
}
.crew-tools-sidebar .tool-day-pill {
    display: inline-block;
    font-size: 10px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 10px;
    background: var(--primary);
    color: #fff;
}
.crew-tools-sidebar .tool-option-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.crew-tools-sidebar .tool-option-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.crew-tools-sidebar input[type="number"]::-webkit-inner-spin-button,
.crew-tools-sidebar input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.crew-tools-sidebar .tool-option-label {
    font-weight: 600;
    font-size: 10px;
    color: var(--text-muted);
    min-width: 48px;
    flex-shrink: 0;
}
.crew-tools-sidebar .tool-section-label {
    font-weight: 600;
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 4px;
}
.crew-tools-sidebar .tool-summary {
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 6px;
    max-height: 120px;
    overflow-y: auto;
}

.crew-pool-sidebar .crew-panel-header {
    padding: 8px 10px;
}

.crew-pool-sidebar .crew-panel-header h3 {
    font-size: 12px;
}

.crew-pool-create-form {
    padding: 6px 8px;
    border-bottom: 1px solid var(--border-light);
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.crew-pool-create-form .form-control {
    font-size: 11px;
    padding: 3px 6px;
    margin: 0;
}

.crew-pool-edit-panel {
    padding: 8px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-alt);
}

.crew-pool-edit-panel .form-control {
    font-size: 11px;
    padding: 3px 6px;
    margin: 0;
}
.crew-time-nudge {
    padding: 2px 6px;
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
    min-width: 24px;
}

.crew-task-edit-column {
    width: 200px;
    min-width: 200px;
    padding: 8px;
    border-left: 1px solid var(--border);
    background: var(--bg-alt);
}
.crew-task-edit-column .form-control {
    font-size: 11px;
    padding: 3px 6px;
    margin: 0;
}
.crew-task-edit-column.disabled {
    opacity: 0.5;
    pointer-events: none;
}

.crew-pool-list {
    flex: 1;
    overflow-y: auto;
    padding: 6px 8px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: 0;
}

.crew-grid {
    display: flex;
    overflow-x: auto;
    gap: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
}

.crew-grid-with-pool > .crew-grid {
    border-radius: 8px 0 0 8px;
}

.crew-day-column {
    min-width: 200px;
    border-right: 1px solid var(--border);
    flex-shrink: 0;
}

.crew-range-expand {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 12px 2px 4px;
    flex-shrink: 0;
    min-width: 40px;
    width: 40px;
    background: var(--bg-alt);
    border-right: 1px solid var(--border);
}

.crew-range-expand:last-child {
    border-right: none;
    border-left: 1px solid var(--border);
}

/* Day columns are nested one-per-project inside a .crew-date-group. The very
   last real column of the grid drops its right border (as before Chunk 2);
   the last column inside a red-bordered multi group drops it too so it
   doesn't double up against the group's own border. */
.crew-date-group:last-child > .crew-day-column:last-child,
.crew-date-group--multi > .crew-day-column:last-child {
    border-right: none;
}

/* One date's columns, grouped so a date reads as a unit. Groups are flex
   containers; they in turn flex within .crew-grid because #crewGridBody is
   display:contents. Single-column (single-project) groups carry no border and
   look exactly like the pre-Chunk-2 flat column list. */
.crew-date-group {
    display: flex;
    flex-shrink: 0;
}
.crew-date-group--multi {
    border: 2px solid var(--danger, #dc2626);
    border-radius: 6px;
    margin: 0 4px;
    /* No vertical padding so the grouped columns' timeline tracks stay aligned
       with the sticky ruler; the 2px border offset is negligible. */
    padding: 0 1px;
    background: color-mix(in srgb, var(--danger, #dc2626) 4%, transparent);
}

.crew-day-header {
    padding: 8px;
    background: var(--bg-alt);
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    font-weight: 600;
    text-align: center;
}

.crew-day-column.weekend .crew-day-header {
    background: #dbeafe;
}

.crew-day-header-clickable {
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    user-select: none;
}

/* Read-only header note for a NON-working RW property date (Start/Pick/Stop) —
   e.g. a dark day reading "(Warehouse Start)". Distinct from the editable
   date-type input so it reads as reference text, not something you edit/save. */
/* Permanent single-line sub-row under the date. ALWAYS present (blank on days
   with no property date) and locked to exactly ONE line via a fixed height +
   nowrap, so every day header is identical height and the timelines stay
   aligned across all columns. */
.day-property-note {
    font-size: 10px;
    font-style: italic;
    color: #6b7280;
    line-height: 1.2;
    height: 1.2em;              /* reserve one line even when empty */
    margin-top: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.crew-dark-day .crew-day-header .day-property-note { color: #334155; }
/* Interim badge for a rare 2nd working type on one calendar day — inline in the
   sub-row so it never adds height. */
.day-extra-working {
    font-weight: 600;
    color: var(--danger);
    font-style: normal;
}

.crew-day-header:has(.crew-day-header-clickable:hover) {
    background: rgba(59, 130, 246, 0.08);
}

.crew-day-header.selected {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
    background: rgba(59, 130, 246, 0.15);
}

.crew-day-column.sunday-col {
    border-left: 2px solid #94a3b8;
}

/* Dark day shading is scoped to the header + timeline area only — the
   space where a roster would live (below the timeline) stays plain white
   so dark-day columns don't visually extend past the timeline grid. */
.crew-dark-day { background: transparent; }
.crew-dark-day .crew-day-header {
    background: #cbd5e1;
    color: #1f2937;
}
.crew-dark-day .crew-day-header .crew-day-desc-input { color: #374151; }
.crew-dark-day .crew-day-header .crew-day-notes-input { color: #4b5563; }
.crew-dark-day .crew-timeline-wrap {
    background: #e5e7eb;
}

.crew-datetype-input {
    border: 1px solid transparent;
    background: transparent;
    font-size: 11px;
    font-weight: 600;
    text-align: center;
    padding: 2px 4px;
    border-radius: 3px;
    width: 100%;
    margin-top: 2px;
}

.crew-datetype-input:hover {
    border-color: var(--border-light);
    background: #fff;
}

.crew-datetype-input:focus {
    outline: none;
    border-color: var(--primary);
    background: #fff;
}

.crew-day-header {
    position: relative;
}

.crew-fill-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: ew-resize;
    opacity: 0;
    transition: opacity 0.1s;
    z-index: 5;
    color: var(--primary);
    font-size: 18px;
    font-weight: 900;
    line-height: 1;
    user-select: none;
    background: transparent;
}

.crew-fill-handle.left { left: -14px; }
.crew-fill-handle.right { right: -14px; }

/* Hide drag handles on timeline edges so expand-range buttons stay accessible */
.crew-day-column.crew-edge-first .crew-fill-handle.left,
.crew-day-column.crew-edge-last .crew-fill-handle.right {
    display: none !important;
}
/* Fallback: also hide via first/last-child inside the grid body */
#crewGridBody > .crew-day-column:first-child .crew-fill-handle.left,
#crewGridBody > .crew-day-column:last-child .crew-fill-handle.right {
    display: none !important;
}

.crew-fill-handle:hover,
.crew-fill-handle.dragging {
    opacity: 1;
}

.crew-datetype-input.crew-fill-ghost {
    background: #fef3c7 !important;
    border-color: #f59e0b !important;
    color: #92400e !important;
    font-style: italic;
}

body.crew-filling,
body.crew-filling * {
    cursor: crosshair !important;
    user-select: none;
}

.crew-bottom-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-top: 16px;
}

.crew-tasks-panel,
.crew-roster-panel {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
}

.crew-panel-header {
    padding: 10px 12px;
    background: var(--bg-alt);
    border-bottom: 1px solid var(--border);
}

.crew-panel-header h3 {
    font-size: 14px;
    font-weight: 700;
    margin: 0;
}

.crew-panel-section + .crew-panel-section {
    border-top: 1px solid var(--border-light);
}

/* New Task + Edit Task shown side-by-side. Stack on narrow screens. */
.crew-task-forms-row {
    display: flex;
    flex-wrap: wrap;
    border-top: 1px solid var(--border-light);
}
.crew-task-form-col {
    flex: 1 1 320px;
    border-top: none !important;
}
.crew-task-form-col + .crew-task-form-col {
    border-left: 1px solid var(--border-light);
}
@media (max-width: 700px) {
    .crew-task-form-col + .crew-task-form-col {
        border-left: none;
        border-top: 1px solid var(--border-light);
    }
}
.crew-task-form-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}
.crew-task-form-inline .form-control { margin: 0; }

/* Unassigned pool in sidebar: stack vertically. */
/* Unassigned-task pool: pills laid out as wrapping inline flow rather
 * than stacked block cards. Each .crew-task-block inside #taskPool
 * becomes a rounded chip; container wraps to as many rows as needed.
 * Explicitly sets flex-direction: row to override .crew-pool-list's
 * column (the same element carries both classes). */
#taskPool {
    min-height: 32px;
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 4px;
    padding: 6px;
    align-content: flex-start;
}
.crew-pool-sidebar #taskPool .crew-task-block {
    flex: 0 0 auto;
    width: auto;
    padding: 3px 8px;
    font-size: 11px;
    border-radius: 12px;
    white-space: nowrap;
}

/* Tabbed tasks panel inside the crew schedule. */
.crew-task-tabs {
    padding: 4px 8px 0;
    border-bottom: 1px solid var(--border-light);
    display: flex;
    gap: 4px;
}
.crew-task-tabs .tab {
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}
.crew-task-tabs .tab.active {
    color: var(--primary);
    border-bottom-color: var(--primary);
}

/* Day's Tasks list rows. */
.assigned-day-group + .assigned-day-group { margin-top: 10px; }
.assigned-day-header {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-bottom: 4px;
}
.assigned-task-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 3px 6px;
    margin: 0 -6px;
    font-size: 12px;
    cursor: pointer;
    border-radius: 3px;
    user-select: none;
}
.assigned-task-row:hover { background: #f1f5f9; }
.assigned-task-row.task-selected {
    background: #bfdbfe;
    outline: 1px solid var(--primary);
}
.assigned-task-time {
    font-weight: 700;
    font-size: 11px;
    min-width: 66px;
    color: var(--primary);
}
.assigned-task-team {
    flex: 0 0 auto;
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #475569;
    background: #e2e8f0;
    padding: 1px 5px;
    border-radius: 3px;
}
.assigned-task-team:empty { display: none; }
.assigned-task-desc { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.assigned-task-dur { flex: 0 0 auto; }

/* When no task is selected, the Edit column stays in the flex layout
   (reserving its width) but its content is invisible so the other
   columns don't jump around on select/deselect. */
.crew-task-form-col.edit-inactive .crew-panel-body {
    opacity: 0.4;
    pointer-events: none;
}
.crew-task-form-col.edit-inactive .crew-panel-body .form-control,
.crew-task-form-col.edit-inactive .crew-panel-body .btn {
    cursor: default;
}

.crew-panel-subhead {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin: 0;
    padding: 8px 12px 4px;
}

.crew-panel-body {
    padding: 8px 12px 12px;
    min-height: 40px;
}

.crew-drag-source {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.crew-roster-chip {
    padding: 4px 10px;
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: 12px;
    font-size: 12px;
    cursor: grab;
    user-select: none;
}

.crew-roster-chip:active {
    cursor: grabbing;
}

.crew-roster-chip-new {
    background: #ecfdf5;
    border-color: #10b981;
    color: #065f46;
    cursor: pointer;
}

.crew-roster-chip-new:hover {
    background: #d1fae5;
}

.crew-role-input {
    width: 100%;
    border: 1px solid transparent;
    background: transparent;
    font-size: 11px;
    padding: 1px 4px;
    border-radius: 3px;
    margin-top: 2px;
}

.crew-role-input:hover { border-color: var(--border-light); background: #fff; }
.crew-role-input:focus { outline: none; border-color: var(--primary); background: #fff; }

/* Per-day roster block (replaces old CREW block in day columns). */
.crew-roster-block {
    padding: 4px 8px;
    border-bottom: 1px solid var(--border);
}
.crew-roster-row.crew-roster-no-team {
    background: #fef3c7;
    border-left: 3px solid #f59e0b;
    padding-left: 4px;
    border-radius: 2px;
}
.crew-roster-row {
    cursor: pointer;
    user-select: none;
    display: grid;
    grid-template-columns: 60px 1fr auto 20px;
    gap: 2px;
    align-items: center;
}
.crew-roster-col {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.crew-roster-name-col .crew-roster-crew-input {
    width: 100%;
}
.crew-roster-team-tag {
    font-size: 8px;
    color: var(--text-muted);
    background: var(--bg-alt);
    padding: 0 3px;
    border-radius: 2px;
    white-space: nowrap;
    text-align: right;
}
.crew-roster-team-tag:empty {
    display: none;
}
.crew-roster-remove-col {
    text-align: center;
}
.crew-roster-row.roster-selected {
    background: #dbeafe;
    outline: 1px solid #2563eb;
    outline-offset: -1px;
    border-radius: 2px;
}
.crew-roster-actions {
    display: flex;
    gap: 4px;
    align-items: center;
    margin-top: 4px;
}
.crew-roster-team-assign {
    font-size: 10px;
    padding: 2px 4px;
    border: 1px solid var(--border);
    border-radius: 3px;
    background: #fff;
}
.crew-roster-row {
    padding: 1px 0;
    font-size: 11px;
}
.crew-roster-role {
    font-weight: 700;
    white-space: nowrap;
    min-width: 60px;
    cursor: text;
}
input.crew-roster-role {
    width: 70px;
    font-size: 10px;
    font-weight: 700;
    padding: 0 2px;
    border: 1px solid var(--border);
    border-radius: 3px;
}
.crew-roster-crew-input {
    flex: 1;
    border: 1px solid transparent;
    background: transparent;
    font-size: 11px;
    padding: 1px 4px;
    border-radius: 3px;
    min-width: 60px;
}
.crew-roster-crew-input:hover { border-color: var(--border-light); background: #fff; }
.crew-roster-crew-input:focus { outline: none; border-color: var(--primary); background: #fff; }
.crew-roster-crew-input::placeholder { color: #94a3b8; font-style: italic; }

.crew-roster-remove {
    font-size: 10px;
    padding: 0 3px;
    line-height: 1;
    opacity: 0;
    transition: opacity 0.1s;
    color: #991b1b;
}
.crew-roster-row:hover .crew-roster-remove { opacity: 0.7; }
.crew-roster-remove:hover { opacity: 1 !important; }

.crew-roster-time {
    display: inline-block;
    padding: 0 3px;
    border-radius: 2px;
    font-size: 10px;
}
.crew-roster-time-lobby_call {
    background: #e0e7ff;
    color: #3730a3;
    font-weight: 600;
}
.crew-roster-time-lobby_call::before {
    content: 'LC ';
}

/* Project Roles list in the bottom panel. */
.crew-project-role-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 0;
    font-size: 12px;
    flex-wrap: wrap;
}
.crew-role-status-input,
.crew-role-notes-input {
    flex: 1;
    min-width: 60px;
    font-size: 11px;
    padding: 1px 4px;
    border: 1px solid var(--border-light);
    border-radius: 3px;
    background: var(--bg-alt);
}
.crew-role-status-input:focus,
.crew-role-notes-input:focus {
    border-color: var(--border);
    background: #fff;
    outline: none;
}
.crew-project-role-name {
    font-weight: 700;
    min-width: 80px;
    cursor: text;
}
input.crew-project-role-name {
    width: 90px;
    font-size: 12px;
    font-weight: 700;
    padding: 0 3px;
    border: 1px solid var(--border);
    border-radius: 3px;
}

/* Team popover */

.crew-team-popover {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.15);
    min-width: 220px;
    max-width: 300px;
}

.crew-team-pop-header {
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-light);
}

.crew-team-pop-name {
    width: 100%;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    padding: 4px 8px;
    font-size: 13px;
    font-weight: 600;
    outline: none;
}

.crew-team-pop-name:focus {
    border-color: var(--primary);
}

.crew-team-pop-roles {
    padding: 6px 0;
    max-height: 250px;
    overflow-y: auto;
}

.crew-team-pop-role {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    cursor: pointer;
    font-size: 12px;
    transition: background 0.1s;
}

.crew-team-pop-role:hover {
    background: var(--bg-hover);
}

.crew-team-label.team-pop-selected {
    background: #dbeafe;
    outline: 2px solid #2563eb;
    outline-offset: -2px;
}
.crew-team-labels {
    position: relative;
}
.crew-team-paste-btn {
    position: absolute;
    right: 2px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 11px;
    line-height: 1;
    padding: 1px 3px;
    background: transparent;
    border: none;
    cursor: pointer;
    opacity: 0.6;
}
.crew-team-paste-btn:hover {
    opacity: 1;
}

.crew-add-team-btn,
.crew-roster-add-btn,
.crew-move-btn {
    display: block;
    width: 100%;
    margin-top: 4px;
    padding: 2px 4px;
    font-size: 10px;
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 3px;
    color: var(--text-muted);
    cursor: pointer;
}
.crew-add-team-btn:hover,
.crew-roster-add-btn:hover,
.crew-move-btn:hover {
    background: var(--bg-hover);
    color: var(--text);
}
/* Move-day mode: click Move on a day, then click a destination day column. */
body.crew-move-mode .crew-day-header-clickable { cursor: crosshair; }
body.crew-move-mode .crew-day-column:hover .crew-day-header {
    outline: 2px dashed var(--primary);
    outline-offset: -2px;
    background: rgba(37, 99, 235, 0.10);
}
.crew-move-btn.active, body.crew-move-mode .crew-move-btn { border-style: solid; }
.crew-move-hint {
    position: fixed;
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2000;
    background: #2563eb;
    color: #fff;
    padding: 7px 16px;
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
}
.crew-roster-add-picker {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    min-width: 200px;
    max-height: 400px;
    overflow-y: auto;
}

/* ---- Searchable project picker ---- */
.searchable-project-picker {
    position: relative;
    display: inline-block;
}
.searchable-project-input {
    width: 320px;
}
.nav-project-input {
    width: 200px;
    font-size: 12px;
    padding: 3px 8px;
    height: 28px;
}
.top-nav .searchable-project-menu {
    z-index: 200;
}
.searchable-project-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 100%;
    width: max-content;
    max-width: 480px;
    max-height: 320px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    z-index: 100;
    display: none;
    margin-top: 2px;
}
.searchable-project-picker.open .searchable-project-menu {
    display: block;
}
.searchable-project-menu:focus {
    /* Focus can land here via keyboard Tab (toggling between the input and
       the menu); the active-item highlight below is the visual indicator,
       so suppress the browser's default focus ring on the container. */
    outline: none;
}
.searchable-project-item {
    padding: 6px 14px 6px 10px;
    cursor: pointer;
    font-size: 12px;
    white-space: nowrap;
}
/* The row you're about to pick — reached by mouse (:hover) OR arrow keys
   (.active) — is pastel green. !important so it wins over the blue "mine" tint
   and the dimmed past/terminal rows below (equal specificity, later source
   order); otherwise the highlight was invisible on a "mine"/dimmed row. */
.searchable-project-item:hover,
.searchable-project-item.active {
    background: var(--bg-active-hl) !important;
}
.searchable-project-item.nav-proj-mine {
    background: #f0f7ff;
}
.searchable-project-item.nav-proj-mine:hover {
    background: #dbeafe;
}
.nav-proj-star {
    color: #f59e0b;
    font-size: 11px;
}
/* Past-only projects (every dated day before today): dimmed but still fully
   selectable — a visual "this one's done" cue, not a filter. */
.searchable-project-item.nav-proj-past {
    opacity: 0.5;
}
.searchable-project-item.nav-proj-past:hover,
.searchable-project-item.nav-proj-past.active {
    opacity: 0.8;
}
/* Cancelled / closed / complete projects: struck through + dimmed. Excluded
   from every search EXCEPT this Jump-to-Details picker (owner's rule), where
   they stay selectable so you can still open a finished project's detail. The
   strike-through is the "this is dead, don't staff it" cue. */
.searchable-project-item.nav-proj-terminal > span:first-child {
    text-decoration: line-through;
}
.searchable-project-item.nav-proj-terminal {
    opacity: 0.45;
}
.searchable-project-item.nav-proj-terminal:hover,
.searchable-project-item.nav-proj-terminal.active {
    opacity: 0.8;
}
/* Same past-only dimming in the calendar project-filter modal + the reports
   multi-select (still selectable). */
.proj-filter-item.proj-filter-past { opacity: 0.5; }
.proj-filter-item.proj-filter-past:hover { opacity: 0.85; }
option.rpt-proj-past { color: #9ca3af; }

/* ---- Blackouts: type-to-search crew picker ---- */
.bo-crew-picker {
    position: relative;
    display: block;
}
.bo-crew-input {
    width: 220px;
}
.bo-crew-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 100%;
    width: max-content;
    max-width: 320px;
    max-height: 260px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    z-index: 100;
    display: none;
    margin-top: 2px;
}
.bo-crew-item {
    padding: 5px 12px;
    cursor: pointer;
    font-size: 12px;
    white-space: nowrap;
}
.bo-crew-item:hover,
.bo-crew-item.active {
    background: var(--bg-hover);
}
.bo-crew-empty {
    padding: 6px 12px;
}

/* ---- Crew Hours spreadsheet ---- */
.ch-table-wrap {
    overflow: auto;
    max-height: calc(100vh - 140px);
    border: 1px solid var(--border);
    border-radius: 4px;
}
.ch-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}
.ch-table th {
    background: var(--bg-alt);
    padding: 6px 8px;
    text-align: left;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: 11px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    position: sticky;
    top: 0;
    z-index: 2;
}
.ch-table td {
    padding: 4px 6px;
    border-bottom: 1px solid var(--border-light);
    vertical-align: middle;
}
.ch-row:hover {
    background: var(--bg-hover);
}
.ch-row-dark {
    background: repeating-linear-gradient(
        45deg,
        #f5f5f5,
        #f5f5f5 6px,
        #e5e5e5 6px,
        #e5e5e5 10px
    );
    color: #888;
}
.ch-row-dark .ch-time-input,
.ch-row-dark .ch-text-input {
    opacity: 0.4;
    pointer-events: none;
}

.ch-day-cell {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.ch-day-label {
    font-weight: 600;
    white-space: nowrap;
}
.ch-date-type {
    display: inline-block;
    padding: 1px 4px;
    border-radius: 2px;
    font-size: 9px;
    color: #fff;
    text-transform: uppercase;
    width: fit-content;
}
.ch-role-cell {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 140px;
}
.ch-role-name {
    font-weight: 600;
}
.ch-crew-name {
    font-size: 10px;
    color: var(--text-muted);
}
.ch-team-name {
    font-size: 10px;
    color: #4f46e5;
    font-style: italic;
}
body.ch-hide-dark .ch-row-dark { display: none; }

.ch-time-input {
    width: 90px;
    padding: 2px 4px;
    font-size: 11px;
    border: 1px solid var(--border);
    background: #fff;
    border-radius: 2px;
}
.ch-time-input:focus {
    border-color: var(--primary);
    outline: none;
}
.ch-text-input {
    width: 100%;
    padding: 2px 4px;
    font-size: 11px;
    border: 1px solid var(--border);
    background: #fff;
    border-radius: 2px;
    min-width: 100px;
}
.ch-text-input:focus {
    border-color: var(--primary);
    outline: none;
}

.ch-extras-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.ch-extra-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    background: #fef3c7;
    border: 1px solid #f59e0b;
    border-radius: 3px;
    padding: 1px 2px;
}
.ch-extra-type {
    width: 70px;
    font-size: 10px;
    border: none;
    background: transparent;
    padding: 1px 2px;
}
.ch-extra-time {
    width: 70px !important;
}
.ch-extra-del {
    background: none;
    border: none;
    color: #dc2626;
    font-size: 12px;
    cursor: pointer;
    padding: 0 2px;
}
.ch-extra-add-btn {
    margin-top: 2px;
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 2px;
    padding: 0 4px;
    font-size: 10px;
    color: var(--text-muted);
    cursor: pointer;
}

.ch-dark-toggle {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 2px;
    padding: 2px 6px;
    font-size: 10px;
    cursor: pointer;
    color: var(--text-muted);
}
.ch-row-dark .ch-dark-toggle {
    background: #6b7280;
    color: #fff;
    border-color: #6b7280;
    font-weight: 600;
}

.ch-reset-auto {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 2px;
    padding: 2px 6px;
    font-size: 11px;
    cursor: pointer;
    color: var(--text-muted);
    margin-left: 2px;
    display: none;
}
.ch-row-manual .ch-reset-auto {
    display: inline-block;
}
.ch-row-manual .ch-time-input {
    font-weight: 600;
    color: #1e40af;
}
.ch-row-auto .ch-time-input {
    color: #6b7280;
    font-style: italic;
}

.ch-mark-btn {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1px solid var(--border);
    background: #fff;
    font-weight: 700;
    font-size: 11px;
    cursor: pointer;
    color: var(--text-muted);
}
.ch-mark-btn:hover {
    background: var(--bg-hover);
}
.ch-row-actual .ch-mark-btn {
    background: #16a34a;
    color: #fff;
    border-color: #16a34a;
}
.ch-row-actual .ch-time-input {
    background: #f0fdf4;
}
.ch-row-actual {
    background: #f0fdf4;
}
.ch-row-actual:hover {
    background: #dcfce7;
}

.ch-group-header td {
    background: #1f2937;
    color: #fff;
    font-weight: 700;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 6px 10px;
    position: sticky;
    left: 0;
}
.ch-group-label {
    margin-right: 12px;
}
.ch-group-actions {
    display: inline-flex;
    gap: 6px;
}
.ch-group-mark-btn {
    background: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.3);
    color: #fff;
    font-size: 10px;
    padding: 2px 8px;
    border-radius: 3px;
    cursor: pointer;
    text-transform: none;
    letter-spacing: 0;
}
.ch-group-mark-btn:hover {
    background: rgba(255,255,255,0.25);
}

/* Paid hours column */
.ch-hours {
    text-align: right;
    white-space: nowrap;
    min-width: 60px;
}
.ch-paid-hours {
    font-size: 12px;
    font-weight: 600;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* Meal P/NP toggle */
.ch-meal-paid-cell {
    text-align: center;
}
.ch-meal-paid-btn {
    font-size: 10px;
    width: 26px;
    height: 24px;
}
.ch-meal-paid-btn[data-state="paid"] {
    background: #16a34a;
    color: #fff;
    border-color: #16a34a;
}
.ch-meal-paid-btn[data-state="np"] {
    background: #f97316;
    color: #fff;
    border-color: #f97316;
}

.crew-add-team-picker {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    min-width: 160px;
    max-height: 300px;
    overflow-y: auto;
}
.crew-add-team-picker-header {
    padding: 6px 10px;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border-light);
}
.crew-add-team-picker-item {
    padding: 6px 10px;
    font-size: 12px;
    cursor: pointer;
}
.crew-add-team-picker-item:hover {
    background: var(--bg-hover);
}
.crew-add-team-picker-divider {
    border-top: 1px solid var(--border-light);
    margin: 2px 0;
}
.crew-add-team-new-input {
    width: calc(100% - 20px);
    margin: 4px 10px 6px;
    padding: 4px 6px;
    font-size: 12px;
    border: 1px solid var(--border);
    border-radius: 3px;
    outline: none;
}
.crew-add-team-new-input:focus {
    border-color: var(--primary);
}
.crew-team-pop-team-select {
    font-size: 11px;
    padding: 2px 4px;
    border: 1px solid var(--border);
    border-radius: 3px;
    background: #fff;
}

.crew-team-pop-divider {
    border-top: 2px solid var(--border);
    margin: 6px 0 2px;
    position: relative;
}
.crew-team-pop-divider::after {
    content: 'Not on This Day\2019s Roster';
    position: absolute;
    top: -7px;
    left: 8px;
    background: #fff;
    padding: 0 4px;
    font-size: 8px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.crew-team-pop-role.crew-team-pop-offday {
    opacity: 1;
    color: #c0c0c0;
}
.crew-team-pop-role.crew-team-pop-offday .crew-team-pop-role-name,
.crew-team-pop-role.crew-team-pop-offday .crew-team-pop-crew-name {
    color: #c0c0c0;
}

.crew-team-pop-role.crew-team-pop-conflict {
    opacity: 0.5;
    background: #fef2f2;
}
.crew-team-pop-role.crew-team-pop-conflict .crew-team-pop-crew-name {
    color: #b91c1c;
}
.crew-team-pop-conflict-tag {
    margin-left: auto;
    color: #dc2626;
    font-size: 12px;
}

.crew-team-pop-role-name {
    font-weight: 600;
    white-space: nowrap;
}

.crew-team-pop-crew-name {
    color: var(--text-muted);
    font-size: 11px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Team label clickable */

.crew-team-label-click {
    cursor: pointer;
    transition: background 0.1s;
}

.crew-team-label-click:hover {
    background: var(--bg-hover);
}

/* Teams tab management */

.crew-team-mgmt-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 0;
}

.crew-team-name-input {
    flex: 1;
    font-size: 12px !important;
    padding: 3px 6px !important;
}

/* Find Crew modal results. */
.find-crew-tier-header {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    padding: 8px 0 4px;
    border-bottom: 1px solid var(--border-light);
    margin-bottom: 4px;
}
.find-crew-tier-header:first-child { padding-top: 0; }
.find-crew-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    font-size: 13px;
}
.find-crew-name { flex: 1; }
.find-crew-avail {
    font-size: 11px;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 3px;
}
.find-crew-avail.tier-1 { background: #dcfce7; color: #166534; }
.find-crew-avail.tier-2 { background: #dbeafe; color: #1e40af; }
.find-crew-avail.tier-3 { background: #fef3c7; color: #92400e; }
.find-crew-avail.tier-4 { background: #fee2e2; color: #991b1b; }
.find-crew-avail.tier-5 { background: #f3e8ff; color: #6b21a8; }

.find-crew-exp {
    font-size: 10px;
    color: var(--text-muted);
    margin-right: 6px;
    white-space: nowrap;
}

/* Two-pane Find Crew modal. */
.find-crew-modal { max-width: 90vw; width: 80vw; }

/* ---- Standalone Crew Searcher ---- */
.crew-searcher-modal { max-width: 720px; width: 92vw; }
.crew-searcher-controls {
    display: flex; align-items: flex-end; gap: 10px; padding: 10px 12px;
    border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.crew-searcher-controls label { display: flex; flex-direction: column; font-size: 11px;
    color: var(--text-muted); gap: 2px; }
.crew-searcher-controls .form-control { font-size: 13px; }
.crew-searcher-results { max-height: 65vh; overflow-y: auto; padding: 4px 10px 12px; }
.cs-role-section { margin-top: 8px; }
.cs-role-header { font-size: 13px; font-weight: 700; padding: 4px 2px; position: sticky; top: 0;
    background: var(--bg, #fff); border-bottom: 1px solid var(--border-light); }
.cs-top-note { font-size: 11px; font-weight: 700; color: #dc2626; background: #fef08a;
    border-radius: 3px; padding: 0 6px; margin-left: 6px; }
/* The top-N note doubles as a show-all/less button. */
.cs-top-toggle { border: none; cursor: pointer; font-family: inherit; line-height: 1.7; }
.cs-top-toggle:hover { background: #fde047; text-decoration: underline; }
/* Rows past the top 5 stay hidden until the toggle reveals them. Two-class
   selector so it outranks the plain `.cs-crew-row { display: flex }` rule
   (which is declared later in the file) regardless of source order. */
.cs-crew-row.cs-crew-row-extra { display: none; }
.cs-role-section.cs-show-all .cs-crew-row.cs-crew-row-extra { display: flex; }
.cs-crew-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 5px 6px; border-radius: 4px; cursor: pointer; }
.cs-crew-row:hover { background: var(--bg-hover, #f1f5f9); }
.cs-crew-name { font-weight: 600; font-size: 13px; min-width: 140px; }
.cs-avail { font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 3px; white-space: nowrap; }
.cs-avail-clear { background: #dcfce7; color: #166534; }
.cs-avail-adj { background: #fef9c3; color: #854d0e; }
.cs-avail-busy { background: #fee2e2; color: #991b1b; }
.cs-avail-pto { background: #e0e7ff; color: #3730a3; }
.cs-pto { font-size: 10px; font-weight: 600; color: #3730a3; }
.cs-crew-meta { font-size: 11px; margin-left: auto; }
.cs-person-status { flex-basis: 100%; }
.cs-ps-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 4px 14px;
    font-size: 12px; background: var(--bg-muted, #f8fafc); border-radius: 4px; padding: 6px 10px; margin-top: 4px; }
/* "What are they doing during the range" — per-overlapping-project grid: a date
   header, a "This Show" row, and the other show's working type per day. Clashing
   (in-range) days are red, showing the date type. */
.cs-overlap-wrap { margin-top: 6px; }
.cs-overlap-tbl { border-collapse: collapse; display: block; overflow-x: auto;
    white-space: nowrap; margin-bottom: 6px; }
.cs-ov-rowlabel { font-size: 10px; font-weight: 700; text-align: right; padding: 1px 6px;
    white-space: nowrap; color: #374151; position: sticky; left: 0; background: var(--bg, #fff); }
.cs-ov-dcell { font-size: 10px; font-weight: 600; text-align: center; padding: 1px 5px;
    border: 1px solid var(--border-light); min-width: 34px; }
.cs-ov-cell { font-size: 9px; text-align: center; padding: 1px 5px; border: 1px solid var(--border-light);
    color: var(--text-muted); min-width: 34px; max-width: 80px; overflow: hidden; text-overflow: ellipsis; }
/* "This Show" reference row — plain, no color/symbols. */
.cs-ov-ref { color: #6b7280; font-style: italic; }
.cs-ov-hit { background: #dc2626; }
.cs-ov-dcell.cs-ov-hit { color: #fff; }
.cs-ov-cell.cs-ov-hit { color: #fff; font-weight: 700; }
.cs-ov-x { color: #000; font-weight: 900; }   /* the black ✕ on a red conflict cell */
/* Soft clash: booked the day either side of one of our working days ("adjacent
   day busy"). Yellow, no ✕ — it's a heads-up, not a collision. */
.cs-ov-adj { background: #facc15; }
.cs-ov-dcell.cs-ov-adj { color: #1a1a1a; }
.cs-ov-cell.cs-ov-adj { color: #1a1a1a; font-weight: 700; }
/* The selected show's own working days — tinted so its full extent is visible
   against the conflicting project. */
.cs-ov-cell.cs-ov-ours {
    background: #dbeafe;
    color: #1e3a8a;
    font-style: normal;
    font-weight: 600;
}
.cs-overlap-more { font-size: 10px; color: var(--text-muted); margin: 2px 0 4px; }
.find-crew-body {
    display: flex;
    min-height: 400px;
    max-height: 70vh;
}
.find-crew-list-pane {
    flex: 1;
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.find-crew-list-scroll {
    flex: 1;
    overflow-y: auto;
    padding: 0 8px 8px;
}
.find-crew-preview-pane {
    flex: 1;
    overflow-y: auto;
    padding: 12px;
}
.find-crew-row { cursor: pointer; border-radius: 4px; padding: 5px 6px; }
.find-crew-row:hover { background: #f1f5f9; }
.find-crew-row.find-crew-selected {
    background: #dbeafe;
    outline: 1px solid var(--primary);
}

/* Preview pane detail. */
.find-crew-preview-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}
.find-crew-preview-name { font-size: 16px; }
.find-crew-preview-detail { font-size: 12px; color: var(--text-muted); margin-bottom: 2px; }
.find-crew-preview-section {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    padding: 10px 0 4px;
    border-bottom: 1px solid var(--border-light);
    margin-bottom: 4px;
}
/* Manage Crew modal rows. */
.manage-crew-row {
    display: flex;
    gap: 6px;
    align-items: center;
    padding: 3px 0;
    border-bottom: 1px solid var(--border-light);
}
.manage-crew-row .form-control {
    margin: 0;
    font-size: 12px;
    padding: 3px 6px;
}
.manage-crew-roles {
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    align-items: center;
    /* was min-width:50% + flex:3 1 50% — that forced this data column far wider
       than its inline flex:2, so it no longer lined up under the flex:2 "Role
       History" header. min-width:0 lets it shrink/wrap to its flex:2 share. */
    min-width: 0;
}
.manage-crew-role-pill {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    background: #e2e8f0;
    color: #334155;
    font-size: 10px;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 8px;
    white-space: nowrap;
}
.manage-crew-role-count {
    background: #94a3b8;
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    padding: 0 4px;
    border-radius: 6px;
    min-width: 14px;
    text-align: center;
}

.find-crew-conflict-summary {
    font-size: 12px;
    color: #dc2626;
    padding: 2px 0 6px;
}

.find-crew-conflict-row,
.find-crew-history-row {
    display: flex;
    gap: 8px;
    padding: 2px 0;
    font-size: 12px;
}

/* Shortcut legend in the crew-schedule top toolbar. */
.crew-shortcut-legend {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
    justify-content: center;
    font-size: 11px;
    color: var(--text-muted);
    white-space: nowrap;
}
.crew-shortcut-legend .sep { color: var(--border); }
.crew-shortcut-legend kbd {
    font-family: inherit;
    background: #f1f5f9;
    border: 1px solid var(--border-light);
    border-radius: 3px;
    padding: 0 4px;
    font-weight: 700;
    font-size: 10px;
}

/* ---- Timeline (replaces TASKS block) ---- */
/* --ppm: pixels per minute. Set once at grid level; 0.4 → 576px / 24h.
   Overridden at runtime as an inline style on #crewGrid by the zoom
   controls (crew_schedule.js setZoomPpm) — this rule is just the default
   / first-paint value before that JS runs. */
.crew-grid { --ppm: 0.4; --snap-min: 15; }

.crew-zoom-toolbar {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
}
.crew-zoom-toolbar .crew-zoom-pct {
    min-width: 38px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.crew-zoom-toolbar .btn {
    padding: 2px 10px;
    font-weight: 700;
    line-height: 1.2;
}

.crew-timeline-wrap {
    position: relative;
    background: #fff;
    border-bottom: 1px solid var(--border);
}

.crew-timeline-generic {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    padding: 4px 6px;
    border-bottom: 1px dashed var(--border-light);
    min-height: 24px;
}

.crew-timeline-generic-empty {
    text-align: center;
    padding: 6px;
}

.crew-timeline-generic.drop-target-active,
.crew-timeline-track.drop-target-active {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
}

.crew-team-labels {
    display: grid;
    grid-template-columns: repeat(var(--col-units, 2), 1fr);
    gap: 0;
    height: 18px;
    border-bottom: 1px solid var(--border-light);
}

.crew-team-label {
    font-size: 9px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--text-muted);
    text-align: center;
    padding: 2px 2px;
    border-right: 1px solid var(--border-light);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.crew-team-label:last-child {
    border-right: none;
}
.crew-team-dur {
    font-weight: 400;
    opacity: 0.7;
}
.crew-team-dur-only {
    font-size: 9px;
    font-weight: 600;
    color: var(--text-muted);
    text-align: center;
    padding: 2px 2px;
    grid-column: 1 / -1;
}
/* Single team on a day: span the full label row width so the name centers */
.crew-team-single {
    grid-column: 1 / -1;
    border-right: none;
}
/* Generic team rendered as the only team on a day: hide name, center duration */
.crew-team-generic {
    text-align: center;
    color: var(--text-muted);
}

.crew-timeline-track {
    position: relative;
    background-color: #fff;
    /* Base 24h height = 1440 * ppm. Overflow tasks are clipped — the
       bleed-through shows on the next day's inbound overflow instead. */
    height: calc(1440 * var(--ppm) * 1px);
    overflow: hidden;
    background-image:
        /* major hourly line — drawn at the START of each cell so a task
           border at start_min N exactly overlaps the gridline at N. */
        repeating-linear-gradient(
            to bottom,
            #d1d5db 0,
            #d1d5db 1px,
            transparent 1px,
            transparent calc(60 * var(--ppm) * 1px)
        ),
        /* snap-interval sub-grid — same start-of-cell convention. */
        repeating-linear-gradient(
            to bottom,
            #f1f5f9 0,
            #f1f5f9 1px,
            transparent 1px,
            transparent calc(var(--snap-min) * var(--ppm) * 1px)
        );
}

/* Time ruler column — rendered before day 0 and every 7 days. Shares
   the .crew-day-column structure so its header + generic-strip spacers
   naturally line up with the adjacent day columns' timeline tracks.
   First ruler stays glued to the left edge during horizontal scroll so
   times are always visible. The repeating rulers further right still
   render but lose their stickiness so they scroll with the grid. */
.crew-ruler-column {
    min-width: 28px !important;
    width: 28px;
    background: var(--bg-alt);
    flex-shrink: 0;
    z-index: 5;
}
.crew-ruler-column:first-of-type {
    position: sticky;
    left: 0;
    /* Slightly stronger right border so the sticky column visually
       separates from the scrolling content behind it. */
    border-right: 1px solid #94a3b8;
    box-shadow: 1px 0 4px rgba(0,0,0,0.06);
}
.crew-ruler-column .crew-day-header,
.crew-ruler-column .crew-timeline-generic,
.crew-ruler-column .crew-ruler-team-spacer {
    visibility: hidden;
}
.crew-ruler-team-spacer {
    height: 30px !important;
}
.crew-ruler-track {
    position: relative;
    height: calc(1440 * var(--ppm) * 1px);
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
    overflow: hidden;
}
.crew-ruler-label {
    position: absolute;
    right: 3px;
    top: calc(var(--display-min, var(--start-min)) * var(--ppm) * 1px);
    transform: translateY(-50%);
    font-size: 9px;
    font-weight: 700;
    color: var(--text-muted);
    line-height: 1;
    white-space: nowrap;
}
.crew-ruler-label:first-of-type { transform: translateY(0); }
.crew-ruler-label:last-of-type { display: block; }
.crew-ruler-label-overnight {
    color: #dc2626;
    font-weight: 700;
}
/* Past-midnight labels are only relevant when extend mode is on (the
   ruler-track grows to fit them). Otherwise they'd render past the
   track bottom into whatever sits below. */
body:not(.crew-extend-timeline) .crew-ruler-label-overnight {
    display: none;
}

/* Hover indicator — thin horizontal line that follows the cursor across
   the entire timeline, with a time badge in the sticky ruler. Driven by
   JS-set CSS variables on .crew-grid. */
.crew-grid {
    position: relative;
}
.crew-grid::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: var(--hover-y, -100px);
    height: 1px;
    background: rgba(99, 102, 241, 0.5);
    pointer-events: none;
    z-index: 4;
    display: var(--hover-display, none);
}
/* Lives inside .crew-ruler-track on the sticky ruler column, so it stays
   pinned to the viewport's left edge as the grid scrolls horizontally.
   Its CSS variables are set on the ruler-track element by the mousemove
   handler. */
.crew-hover-time-badge {
    position: absolute;
    top: var(--hover-y, -100px);
    left: 1px;
    transform: translateY(-50%);
    background: #4338ca;
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    padding: 1px 5px;
    border-radius: 3px;
    pointer-events: none;
    z-index: 6;
    display: var(--hover-display, none);
    white-space: nowrap;
}

/* Generic strip fixed height so ruler column aligns with each day's
   timeline track. Overflow chips scroll horizontally within the strip. */
.crew-timeline-generic {
    height: 28px;
    overflow-x: auto;
    overflow-y: hidden;
    flex-wrap: nowrap !important;
    align-items: center;
}

.crew-timeline-task {
    position: absolute;
    /* Default lane-based positioning: 100px per lane, 2px inset each side.
       Multi-lane spans cover `var(--lane-span)` consecutive lanes. */
    left: calc(var(--lane, 0) * 100px + 2px);
    width: calc(var(--lane-span, 1) * 100px - 4px);
    /* --display-min is JS-computed (start - grid_start). Falls back to
       --start-min for code paths that haven't been updated yet. */
    top: calc(var(--display-min, var(--start-min)) * var(--ppm) * 1px);
    height: calc(var(--duration-min) * var(--ppm) * 1px);
    margin: 0;  /* override .crew-task-block's 2px vertical margin */
    box-sizing: border-box;
    background: #dbeafe;
    border: 1px solid var(--primary);
    border-radius: 3px;
    padding: 2px 4px;
    font-size: 10px;
    overflow: hidden;
    cursor: pointer;
    z-index: 2;
}

/* Single-lane days: task uses the entire column width, not one lane. */
.crew-day-column[data-max-lanes="0"] .crew-timeline-task,
.crew-day-column[data-max-lanes="1"] .crew-timeline-task {
    left: 2px;
    right: 2px;
    width: auto;
}

/* Span-drag handles on the left/right edges of a multi-lane task.
   Only show when hovering the task AND nothing is currently selected
   (selection has priority — user can click the task normally). */
.crew-task-span-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 6px;
    background: var(--primary);
    opacity: 0;
    cursor: ew-resize;
    z-index: 3;
    transition: opacity 0.1s;
}
.crew-task-span-handle.left { left: -3px; border-radius: 3px 0 0 3px; }
.crew-task-span-handle.right { right: -3px; border-radius: 0 3px 3px 0; }

.crew-timeline-task:hover .crew-task-span-handle { opacity: 0.8; }
.crew-task-span-handle:hover,
.crew-task-span-handle.dragging { opacity: 1 !important; }

/* Hide span handles when a selection is active — selecting wins. */
body.task-drop-mode .crew-task-span-handle { display: none; }

/* Time-drag handles — clickable 4px area at the top/bottom edge of the
   task. Visual treatment is a 2px medium-blue line at the edge (via the
   ::before pseudo) so the handle barely occludes any of the task body.
   The remaining 2px of the click target is invisible but still grabbable. */
.crew-task-time-handle {
    position: absolute;
    left: 0;
    right: 0;
    height: 4px;
    background: transparent;
    opacity: 0;
    cursor: ns-resize;
    z-index: 5;
    transition: opacity 0.1s;
}
.crew-task-time-handle::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 2px;
    background: #3b82f6;
    border-radius: 1px;
}
.crew-task-time-handle.top { top: 0; }
.crew-task-time-handle.top::before { top: 0; }
.crew-task-time-handle.bottom { bottom: 0; }
.crew-task-time-handle.bottom::before { bottom: 0; }
.crew-timeline-task:hover .crew-task-time-handle { opacity: 1; }
.crew-task-time-handle.dragging { opacity: 1 !important; }
body.task-drop-mode .crew-task-time-handle { display: none; }
body.crew-time-dragging { cursor: ns-resize; }
body.crew-time-dragging .crew-timeline-task,
body.crew-time-dragging .crew-timeline-track { pointer-events: none; }

/* Floating tooltip during time-drag — shows the edge time we'd commit to
   on mouseup, plus the resulting duration. Anchored to the cursor with a
   small offset so it doesn't sit under the pointer. */
.crew-time-drag-tip {
    position: fixed;
    z-index: 10000;
    background: #111827;
    color: #fff;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 12px;
    line-height: 1.3;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    pointer-events: none;
    white-space: nowrap;
}
.crew-time-drag-tip-edge { font-weight: 600; }
.crew-time-drag-tip-dur { font-size: 11px; opacity: 0.75; }

/* Highlight today's column in timeline and sheet */
/* Today's column / row — soft amber tint, no inverted text. Same amber hue
   used for both the timeline column and the spreadsheet day-head so the
   "today" cue reads consistently across views. */
.crew-day-column.crew-day-today {
    background: rgba(245, 158, 11, 0.22);
}
.crew-day-column.crew-day-today .crew-day-header {
    background: rgba(245, 158, 11, 0.22);
}
.sheet-day-head.sheet-day-today {
    background: rgba(245, 158, 11, 0.22);
    border-radius: 4px;
    padding-left: 8px;
    padding-right: 8px;
}

/* New day columns sliding/fading in.
   - .crew-day-expanding: used by the 'after' (right) path — animates max-width
     so the timeline visually grows on the right.
   - .crew-day-fading-in: used by the 'before' (left) path — only fades opacity.
     A width animation on this side would interfere with the scroll-restore math
     that keeps the user's viewport stable while content is pushed right. */
.crew-day-column { transition: opacity 0.25s ease, max-width 0.3s ease, min-width 0.3s ease, padding 0.3s ease, margin 0.3s ease; }
.crew-day-column.crew-day-expanding {
    opacity: 0;
    max-width: 0 !important;
    min-width: 0 !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    overflow: hidden;
}
.crew-day-column.crew-day-fading-in {
    opacity: 0;
}

/* Task name edit popup — appended inside the task block so it tracks the
   task as the timeline scrolls / reflows. */
.crew-task-name-popup {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 100;
    background: #fff;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    padding: 4px;
    cursor: default;
}
.crew-task-name-popup input {
    font-size: 13px;
    padding: 4px 8px;
    border: 1px solid var(--border-light);
    border-radius: 3px;
    width: 200px;
    outline: none;
}
.crew-task-name-popup input:focus {
    border-color: var(--primary);
}

/* Subtle divider between lanes once a day is multi-lane. */
.crew-day-column[data-max-lanes="2"] .crew-timeline-track,
.crew-day-column[data-max-lanes="3"] .crew-timeline-track,
.crew-day-column[data-max-lanes="4"] .crew-timeline-track,
.crew-day-column[data-max-lanes="5"] .crew-timeline-track,
.crew-day-column[data-max-lanes="6"] .crew-timeline-track {
    background-image:
        repeating-linear-gradient(
            to right,
            transparent 0,
            transparent 99px,
            rgba(148, 163, 184, 0.4) 99px,
            rgba(148, 163, 184, 0.4) 100px
        ),
        repeating-linear-gradient(
            to bottom,
            transparent 0,
            transparent calc(60 * var(--ppm) * 1px - 1px),
            #d1d5db calc(60 * var(--ppm) * 1px - 1px),
            #d1d5db calc(60 * var(--ppm) * 1px)
        ),
        repeating-linear-gradient(
            to bottom,
            transparent 0,
            transparent calc(var(--snap-min) * var(--ppm) * 1px - 1px),
            #f1f5f9 calc(var(--snap-min) * var(--ppm) * 1px - 1px),
            #f1f5f9 calc(var(--snap-min) * var(--ppm) * 1px)
        );
}

.crew-timeline-task:active { cursor: grabbing; }

/* Selected-task highlight. Applied to any task-block (pool, generic, timeline). */
.crew-task-block.task-selected {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
    background: #bfdbfe;
}
.crew-task-block { cursor: pointer; user-select: none; }
.crew-task-block.pool-dragging { opacity: 0.4; }

/* While any task is selected, valid drop zones glow to guide the user. */
body.task-drop-mode .crew-timeline-track,
body.task-drop-mode .crew-timeline-generic,
body.task-drop-mode #taskPool {
    box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.4);
    cursor: crosshair;
}

/* Drop-time hover indicator on timeline tracks. The container's top
   border IS the precision line — it sits at the exact drop time, and
   the ghost blocks hang below showing each task's duration. */
.crew-timeline-drop-indicator {
    position: absolute;
    left: 2px; right: 2px;
    border-top: 2px solid var(--primary);
    pointer-events: none;
    z-index: 4;
}
.crew-timeline-drop-indicator.alt-drop { border-top-color: #f59e0b; }

.crew-drop-ghost {
    position: absolute;
    background: rgba(59, 130, 246, 0.18);
    border: 1px dashed rgba(59, 130, 246, 0.6);
    color: var(--primary);
    font-size: 9px;
    font-weight: 600;
    padding: 1px 4px;
    line-height: 1.1;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    box-sizing: border-box;
}
.crew-timeline-drop-indicator.alt-drop .crew-drop-ghost {
    background: rgba(245, 158, 11, 0.18);
    border-bottom-color: rgba(245, 158, 11, 0.6);
    color: #b45309;
}

.crew-marquee-rect {
    position: absolute;
    border: 1px dashed #3b82f6;
    background: rgba(59, 130, 246, 0.08);
    pointer-events: none;
    z-index: 50;
}

/* Alt-held cursor cue: tasks show a "+" cursor so user knows alt-click
   drops rather than selects. */
body.alt-held.task-drop-mode .crew-task-block { cursor: copy; }

.crew-timeline-task-body {
    font-weight: 600;
    line-height: 1.2;
}

/* Short tasks (30–59 min): at 0.4 px/min a 30-min task is only 12px tall.
   Zero vertical padding on the block + zero margin on the body lets an
   8px label actually fit inside the box. */
.crew-timeline-task-short { padding: 0 3px; line-height: 1; }
.crew-timeline-task-short .crew-timeline-task-body { font-size: 8px; margin: 0; vertical-align: center; }

.crew-timeline-task-tiny { padding: 0 3px; font-size: 8px; line-height: 1; }
.crew-timeline-task-tiny .crew-timeline-task-body { font-size: 8px; font-weight: 600; margin: 0; vertical-align: center; }

/* Red stripes: overflow extension past 4am next day. Positioned at bottom
   of current day's track, extending beyond the base 1440-minute height. */
/* Overflow gutter: extra lane on the left of a day that receives overflow
   from the previous day. Purely visual — not an actual team column. */
.crew-overflow-gutter {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100px;
    height: calc(1440 * var(--ppm) * 1px);
    border-right: 1px dashed var(--border);
    z-index: 1;
    pointer-events: none;
    background-color: #fff;
    background-image:
        repeating-linear-gradient(
            to bottom,
            transparent 0,
            transparent calc(60 * var(--ppm) * 1px - 1px),
            #d1d5db calc(60 * var(--ppm) * 1px - 1px),
            #d1d5db calc(60 * var(--ppm) * 1px)
        );
}
.crew-overflow-gutter-label {
    position: sticky;
    top: 4px;
    padding: 2px 6px;
    font-size: 8px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--text);
    font-weight: 600;
    text-align: center;
    background: rgba(255,255,255,0.5);
    z-index: 2;
}
.crew-overflow-gutter-stripe {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    background-image: repeating-linear-gradient(
        -45deg,
        rgba(59, 130, 246, 0.10) 0, rgba(59, 130, 246, 0.10) 6px,
        rgba(59, 130, 246, 0.22) 6px, rgba(59, 130, 246, 0.22) 12px
    );
}
/* When a day has an overflow gutter, shift the actual timeline content right */
.has-overflow-gutter .crew-timeline-generic,
.has-overflow-gutter .crew-team-labels,
.has-overflow-gutter .crew-timeline-track {
    margin-left: 100px;
}

/* "Continues on next day" label at the bottom of the source day track */
.crew-timeline-continues-label {
    position: absolute;
    bottom: 4px;
    left: calc(var(--lane, 0) * 100px + 4px);
    width: calc(var(--lane-span, 1) * 100px - 8px);
    text-align: center;
    font-size: 9px;
    color: var(--text);
    font-weight: 600;
    background: rgba(255,255,255,0.5);
    padding: 2px 4px;
    border-radius: 2px;
    z-index: 3;
}
/* Single-lane days: label spans the full column, same as tasks. */
.crew-day-column[data-max-lanes="0"] .crew-timeline-continues-label,
.crew-day-column[data-max-lanes="1"] .crew-timeline-continues-label {
    left: 4px;
    right: 4px;
    width: auto;
}

/* The legacy per-day overflow-extend element is now superseded by the
   .crew-timeline-track::after pseudo, which paints the stripe on EVERY
   track (so empty days also stripe in extend mode, not just days with
   their own overflow tasks). Hide the old element to avoid double-paint. */
.crew-timeline-overflow-extend { display: none !important; }

/* Past-midnight stripe on every timeline track. In non-extend mode the
   track is 1440min tall with overflow:hidden, so this pseudo renders past
   the visible area and is clipped. In extend mode the track grows to
   1440 + max_overflow, so this pseudo paints the visible past-midnight
   area uniformly across every column. */
.crew-timeline-track::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: calc((1440 - var(--grid-start-min, 0)) * var(--ppm) * 1px);
    bottom: 0;
    background-image: repeating-linear-gradient(
        -45deg,
        rgba(59, 130, 246, 0.10) 0, rgba(59, 130, 246, 0.10) 6px,
        rgba(59, 130, 246, 0.22) 6px, rgba(59, 130, 246, 0.22) 12px
    );
    z-index: 0;
    pointer-events: none;
}

/* Past-midnight portion of a task gets a stripe overlay using the same
   blue pattern. --past-midnight-from-min is set inline by the server only
   when end_min > 1440; for normal tasks it defaults to a huge value so the
   pseudo-element renders past the bottom and is clipped (invisible). */
.crew-timeline-task::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    top: calc(var(--past-midnight-from-min, 99999) * var(--ppm) * 1px);
    bottom: 0;
    background-image: repeating-linear-gradient(
        -45deg,
        rgba(59, 130, 246, 0.18) 0, rgba(59, 130, 246, 0.18) 6px,
        rgba(59, 130, 246, 0.36) 6px, rgba(59, 130, 246, 0.36) 12px
    );
    pointer-events: none;
}

/* (crew-timeline-overflow-inbound removed — overflow is now shown
   only in the gutter lane to the left of the track.) */

.crew-day-header .day-date {
    font-size: 14px;
}


.crew-day-type-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
}

.crew-day-desc-input {
    font-size: 11px;
    font-weight: 400;
    color: var(--text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 3px;
    padding: 1px 4px;
    min-width: 50px;
    max-width: 80px;
    text-align: center;
}
.crew-day-desc-input:focus {
    border-color: var(--accent);
    background: var(--bg);
    outline: none;
}
.crew-day-desc-input::placeholder { color: var(--text-muted); opacity: 0.5; }

.crew-day-notes-input {
    font-size: 10px;
    font-weight: 400;
    color: var(--text-muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 3px;
    padding: 1px 4px;
    width: 100%;
    text-align: center;
    line-height: 1.2;
    margin-top: 1px;
    box-sizing: border-box;
}
.crew-day-notes-input:focus {
    border-color: var(--accent);
    background: var(--bg);
    outline: none;
    color: var(--text);
}
.crew-day-notes-input::placeholder { color: var(--text-muted); opacity: 0.5; }

.crew-time-block {
    padding: 4px 8px;
    border-bottom: 1px solid var(--border-light);
    min-height: 32px;
    font-size: 12px;
}

.crew-task-block {
    background: #dbeafe;
    border: 1px solid #93c5fd;
    border-radius: 4px;
    padding: 4px 6px;
    font-size: 11px;
}

/* Timeline tasks override the base padding because their height is
   dictated by duration — vertical padding steals from the label. */
.crew-timeline-task { padding: 0 4px; }

/* Timeline / pool meal blocks: greyed "unavailable time" look so meals
 * read as fixed slots rather than another regular task. Bold label
 * makes the meal stand out at a glance even when greyed. */
.crew-task-meal {
    background: #e5e7eb !important;
    border-color: #9ca3af !important;
    color: #6b7280;
    font-weight: 600;
}
/* Trucking blocks — direction-tinted. Pickup leans purple,
 * delivery / both leans green. Bold like meals because the slot is
 * fixed (truck arrives at the scheduled time). */
.crew-task-trucking-delivery {
    background: #d1fae5 !important;
    border-color: #6ee7b7 !important;
    font-weight: 600;
}
.crew-task-trucking-pickup {
    background: #ede9fe !important;
    border-color: #c4b5fd !important;
    font-weight: 600;
}

/* Pool task that's a meal — small icon prefix already added in template,
   but the meal background should still apply here. */
.crew-pool-meal .crew-pool-meal-icon { font-size: 11px; }
.crew-task-trucking .crew-pool-trucking-icon { font-size: 11px; }

/* Generic (unscheduled-on-day) strip: pill-style flow so many tasks fit
   without ballooning the row. Container scrolls when pills overflow.
   See _crew_day_column.html .crew-timeline-generic. */
.crew-timeline-generic {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: 3px;
    overflow-y: auto;
    /* ~2 rows of 22px pills + small breathing room. */
    min-height: 50px;
    max-height: 76px;
    padding: 3px;
}
.crew-generic-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 6px;
    border-radius: 10px;
    font-size: 11px;
    line-height: 18px;
    background: #eef2ff;
    border: 1px solid #c7d2fe;
    cursor: pointer;
    max-width: 100%;
    overflow: hidden;
    /* override absolute positioning the timeline blocks normally use */
    position: static !important;
    width: auto !important;
    height: auto !important;
}
.crew-generic-pill .crew-generic-pill-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 140px;
}
.crew-generic-pill .crew-generic-pill-dur {
    color: #6b7280;
    font-size: 10px;
    flex-shrink: 0;
}
.crew-generic-pill.crew-task-meal { background: #e5e7eb; border-color: #9ca3af; color: #6b7280; font-weight: 600; }

/* ---- Teams tab (inside .crew-roster-panel) ---- */
.teams-tab-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 6px;
    border-bottom: 1px solid var(--border-light, #e5e7eb);
    flex-wrap: wrap;
}
.teams-tab-toolbar .cal-mode-toggle { margin-right: 4px; }
.teams-tab-body {
    display: flex;
    gap: 0;
    min-height: 200px;
    max-height: 420px;
}
.teams-tab-left {
    width: 40%;
    min-width: 160px;
    border-right: 1px solid var(--border-light, #e5e7eb);
    overflow-y: auto;
    padding: 4px;
    font-size: 12px;
}
.teams-tab-right {
    flex: 1;
    overflow-y: auto;
    padding: 6px 8px;
    font-size: 12px;
}
.teams-tab-day { margin-bottom: 4px; }
.teams-tab-day-head {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 3px 4px;
    font-weight: 600;
    cursor: pointer;
    border-radius: 3px;
}
.teams-tab-day.is-active > .teams-tab-day-head { background: #eef2ff; }
.teams-tab-add-team {
    margin-left: auto;
    font-size: 10px;
    background: transparent;
    border: 1px solid var(--border-light, #e5e7eb);
    border-radius: 3px;
    padding: 0 4px;
    cursor: pointer;
}
.teams-tab-add-team:hover { background: #eef2ff; }
.teams-tab-day-empty {
    padding-left: 14px;
    font-size: 11px;
    color: var(--text-muted, #6b7280);
}
.teams-tab-team {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 4px 2px 14px;
    cursor: pointer;
    border-radius: 3px;
}
.teams-tab-team:hover { background: #f3f4f6; }
.teams-tab-team.is-active { background: #c7d2fe; }
.teams-tab-team-name { flex: 1; }
.teams-tab-team-count {
    font-size: 10px;
    background: var(--border-light, #e5e7eb);
    color: #374151;
    border-radius: 8px;
    padding: 0 6px;
    min-width: 16px;
    text-align: center;
}
.teams-tab-right-head { margin-bottom: 6px; }
.teams-tab-empty { padding: 8px 0; }
.teams-tab-role-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.teams-tab-role {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 4px;
    border-radius: 3px;
}
.teams-tab-role:hover { background: #f9fafb; }
/* By Team states: on-this team (default look) → tap removes; available
 * (grey, italic) → tap adds; on-other (grey + red tag) → tap is a no-op
 * unless Allow already-assigned is on. */
.teams-tab-role-on-this {
    background: #eff6ff;
    border-left: 3px solid var(--primary, #2563eb);
}
.teams-tab-role-available {
    color: var(--text-muted, #6b7280);
}
.teams-tab-role-available:hover { background: #ecfdf5; }
.teams-tab-role-on-other {
    color: #9ca3af;
    cursor: not-allowed;
}
.teams-tab-role-on-other:hover { background: transparent; }
.teams-tab-role-other-tag {
    margin-left: auto;
    font-size: 10px;
    padding: 1px 6px;
    background: #fee2e2;
    color: #b91c1c;
    border-radius: 8px;
    white-space: nowrap;
}
.teams-tab-role-x {
    margin-left: auto;
    background: transparent;
    border: none;
    color: #b91c1c;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    padding: 0 4px;
}
.teams-tab-role-x:hover { background: #fee2e2; border-radius: 3px; }
.teams-tab-add-row { display: flex; gap: 4px; align-items: center; }
.teams-tab-crew-group { margin-bottom: 10px; }
.teams-tab-crew-head {
    font-weight: 600;
    padding: 2px 0;
    border-bottom: 1px dashed var(--border-light, #e5e7eb);
    margin-bottom: 2px;
}
.teams-tab-role-team { font-size: 11px; padding: 1px 4px; min-width: 110px; }

.crew-task-overlap,
.sheet-cell-overlap {
    background-image: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 6px,
        rgba(220, 38, 38, 0.15) 6px,
        rgba(220, 38, 38, 0.15) 10px
    );
}
/* Gap callout: when a same-lane task has empty time before it, mark the
 * top edge of its start/dur/end cells with a thicker bright-blue border. */
.sheet-cell-gap-before {
    border-top: 3px solid #2563eb !important;
}
/* Day-header shortcut buttons (Remove Gaps / Fill Time) — slightly
 * accented so they read as "fix something" actions next to the neutral
 * + Task / + Note buttons. */
.sheet-day-shortcut {
    background: #eff6ff;
    border-color: #2563eb;
    color: #1d4ed8;
}
.sheet-day-shortcut:hover {
    background: #dbeafe;
}

/* Always-on change log drawer pinned under the top-nav. Toggle bar is
 * always visible; body slides open/closed. Body is intentionally compact
 * (~4 rows visible) so it stays minimally intrusive while still letting
 * the user scroll through every change Remove Gaps / Fill Time / cell
 * edits made during the session. */
.sheet-change-drawer {
    /* Fixed so the toggle bar (and any pending preview) stays pinned to
     * the viewport regardless of scroll position. Sticky was unreliable
     * inside .main-content's overflow-x:auto container — when the user
     * clicked Fill/Remove Gaps from a day card mid-page, the preview
     * would open above the viewport and feel like nothing happened. The
     * pending preview must always be visible. */
    position: fixed;
    top: var(--chrome-h);
    left: 0;
    right: 0;
    z-index: 90;
    background: transparent;
    font-size: 11px;
}
/* Push the schedule page's title row down so the always-on toggle bar
 * doesn't overlay it at scroll-top. */
body[data-url-name="crew_schedule"] .main-content > :first-child {
    margin-top: 28px;
}
.sheet-change-drawer-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 2px 24px;
    background: #f9fafb;
    cursor: pointer;
    user-select: none;
    border-top: 1px solid var(--border-light);
    border-bottom: 1px solid var(--border-light);
    font-size: 11px;
    line-height: 1.4;
}
.sheet-change-drawer-toggle:hover {
    background: #f3f4f6;
}
.sheet-change-drawer-toggle strong {
    font-weight: 600;
}
.sheet-change-drawer-caret {
    display: inline-block;
    transition: transform 0.15s ease;
    color: var(--text-muted);
    font-size: 10px;
}
.sheet-change-drawer.open .sheet-change-drawer-caret {
    transform: rotate(180deg);
}
.sheet-change-drawer-count {
    color: var(--text-muted);
    font-size: 10px;
}
.sheet-change-drawer-clear {
    margin-left: auto;
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 10px;
    cursor: pointer;
    padding: 0 4px;
    text-decoration: underline;
}
.sheet-change-drawer-clear:hover {
    color: var(--text);
}
.sheet-change-drawer-body {
    /* Absolute so opening the drawer overlays the content below the
     * toggle bar (crewStats, conflict warnings, the sheet) without
     * pushing it down. Anchored to the toggle's bottom edge via top:100%. */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    max-height: 0;
    overflow: hidden;
    background: #fff;
    border-bottom: 1px solid var(--border);
    box-shadow: 0 4px 8px rgba(0,0,0,0.08);
    transition: max-height 0.22s ease;
    padding: 0 24px;
}
.sheet-change-drawer.open .sheet-change-drawer-body {
    /* ~4 rows of 20px + vertical padding */
    max-height: 96px;
    overflow-y: auto;
    padding: 2px 24px;
}
.sheet-change-row {
    display: flex;
    align-items: baseline;
    gap: 18px;
    padding: 2px 0;
    border-bottom: 1px solid var(--border-light);
    font-size: 11px;
    line-height: 1.5;
}
.sheet-change-row:last-child {
    border-bottom: none;
}
.sheet-change-row .sheet-change-time {
    color: var(--text-muted);
    font-family: ui-monospace, monospace;
    font-size: 10px;
}
.sheet-change-row .sheet-change-name {
    font-weight: 500;
    white-space: nowrap;
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sheet-change-row .sheet-change-day {
    color: var(--text-muted);
    font-size: 10px;
    white-space: nowrap;
}
.sheet-change-row .sheet-change-diff {
    font-family: ui-monospace, monospace;
    color: #1d4ed8;
    white-space: nowrap;
}
.sheet-change-row.added .sheet-change-diff { color: #047857; }
.sheet-change-row.removed .sheet-change-diff { color: #b91c1c; }
.sheet-change-row .sheet-change-diff .from {
    color: var(--text-muted);
    text-decoration: line-through;
}
.sheet-change-row .sheet-change-diff .arrow {
    margin: 0 4px;
    color: var(--text-muted);
}
.sheet-change-empty {
    color: var(--text-muted);
    font-size: 11px;
    padding: 4px 0;
}

/* Pending preview block — sits above the change log when Remove Gaps /
 * Fill Time has computed proposed changes the user can Apply or Cancel. */
.sheet-change-pending {
    background: #fffbeb;
    border: 1px solid #fde68a;
    border-radius: 4px;
    padding: 4px 8px;
    margin: 2px 0 6px;
}
.sheet-change-pending-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 2px 0 4px;
    border-bottom: 1px solid #fde68a;
    margin-bottom: 4px;
}
.sheet-change-pending-head .sheet-pending-apply {
    margin-left: auto;
}
.sheet-change-row.pending .sheet-change-time {
    color: #b45309;
}
.sheet-change-row.pending .sheet-change-diff {
    color: #b45309;
}

.crew-task-block.midnight-warning {
    background: repeating-linear-gradient(
        45deg,
        #dbeafe,
        #dbeafe 4px,
        #bfdbfe 4px,
        #bfdbfe 8px
    );
}

.crew-task-block.bleed-warning {
    background: repeating-linear-gradient(
        -45deg,
        #fef3c7,
        #fef3c7 4px,
        #fde68a 4px,
        #fde68a 8px
    );
}

.crew-assignment-block {
    background: #f0fdf4;
    border: 1px solid #86efac;
    border-radius: 4px;
    padding: 4px 6px;
    margin: 2px 0;
    font-size: 11px;
}

.crew-conflict-warning {
    background: #fef2f2;
    border: 1px solid #fca5a5;
    color: var(--danger);
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 3px;
    margin-top: 2px;
}

/* ---- Detail Page ---- */

.detail-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 24px;
    gap: 16px;
}

.detail-section {
    margin-bottom: 20px;
    padding: 14px 16px 16px;
    background: linear-gradient(to bottom right, #f5f5f5 0%, #f5f5f5 35%, #ffffff 65%, #ffffff 100%);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
}

.detail-section h2 {
    font-size: 16px;
    font-weight: 600;
    margin: 0 0 12px;
    padding-bottom: 6px;
    border-bottom: 2px solid var(--border-light);
}

.detail-fields {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
}

.linked-project-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: #e5e7eb;
    border-radius: 12px;
    padding: 2px 8px;
    font-size: 12px;
    line-height: 1.4;
}
.linked-project-tag a {
    color: #1a1a1a;
    text-decoration: none;
    font-weight: 500;
}
.linked-project-tag a:hover { text-decoration: underline; }
/* A linked project that's been CANCELLED — still listed (so the link stays
   reachable), but flagged red + struck through. */
.linked-project-tag.is-cancelled a {
    color: #dc2626;
    text-decoration: line-through;
}
.linked-project-tag.is-cancelled a:hover {
    text-decoration: line-through underline;
}
.linked-project-remove {
    background: none;
    border: none;
    color: #6b7280;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    padding: 0 2px;
}
.linked-project-remove:hover { color: #dc2626; }

/* ---- Report ---- */

.report-section {
    page-break-after: always;
    margin-bottom: 32px;
}

/* Reports drop the boxed/shadowed `.detail-section` styling — sections are
 * separated by a bold rule and ample whitespace, so the section-break
 * never gets confused with the lighter under-rule on each section's header. */
#reportContent .detail-section {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0 0 32px;
    margin-bottom: 32px;
    border-bottom: 4px double #6b7280;
}
#reportContent .detail-section:last-child {
    border-bottom: none;
}
/* When a section is followed only by hidden sections (toggled off
 * via .rpt-section-hidden), drop its trailing rule — there's nothing
 * visible after it to separate from. Modern browsers support :has(). */
#reportContent .detail-section:not(:has(~ .detail-section:not(.rpt-section-hidden))) {
    border-bottom: none;
    padding-bottom: 0;
    margin-bottom: 0;
}
/* Schedule Spreadsheet section — the last day's .rpt-sched-sheet-day
 * already has a heavy 2px black outline serving as a section-end
 * marker. Stacking the section's own `4px double` rule on top of
 * that produces a visibly doubled/tripled line at the bottom of the
 * report. Drop it here; the next section's own header / spacing
 * handles separation when one follows. */
#reportContent .detail-section[data-section="schedule_sheet"] {
    border-bottom: none;
    padding-bottom: 0;
}
#reportContent .detail-section h2 {
    border-bottom: 1px solid var(--border-light);
}

/* ====================================================================
 * Responsive — site-wide
 *
 * Two breakpoints: half-page (≤1024px, e.g. browser at 50% on a desktop
 * monitor) and phone (≤600px). Most fixes are: let nav wrap, drop the
 * right sidebar below content, stack stacked-flex rows, and on the
 * crew-schedule page move the unassigned-tasks pool ABOVE the timeline
 * as a horizontal pill row so the timeline (which can't be made
 * phone-shaped) is at least visible without scrolling past the pool.
 * The spreadsheet stays wide and scrolls horizontally — per-table.
 * ==================================================================== */

@media (max-width: 1024px) {
    /* Top nav: wrap to multiple rows, tighter spacing. */
    .top-nav {
        flex-wrap: wrap;
        height: auto;
        min-height: var(--header-height);
        padding: 4px 12px;
        gap: 4px 8px;
        row-gap: 4px;
    }
    .top-nav .nav-links {
        flex-wrap: wrap;
        gap: 0 8px;
    }
    .top-nav .nav-right {
        flex-wrap: wrap;
        gap: 4px;
    }
    .top-nav .searchable-project-picker {
        min-width: 140px;
    }

    /* Right-side sidebar: drop below main content instead of squeezing. */
    .page-wrapper {
        flex-direction: column;
    }
    .sidebar {
        width: 100%;
        height: auto;
        position: static;
        border-left: none;
        border-top: 1px solid var(--border);
    }

    /* Main content: tighter side padding so wide tables have room. */
    .main-content {
        padding: 12px 16px;
    }

    /* Crew schedule grid + pool sidebar: stack vertically with the pool
     * ABOVE the timeline so the horizontally-scrolling timeline doesn't
     * push the pool off-screen. */
    .crew-grid-with-pool {
        flex-direction: column;
    }
    .crew-pool-sidebar {
        order: -1;
        width: 100%;
        min-width: 0;
        flex-direction: column;
        max-height: 30vh;
        overflow-y: auto;
        border-radius: 8px 8px 0 0;
        border-left: 1px solid var(--border);
        border-bottom: none;
    }
    /* Pool pill flow rules live in the base #taskPool block — no
     * overrides needed at this breakpoint. */

    /* Tools panel sits inline with pool — also full-width above timeline. */
    .crew-tools-sidebar {
        order: -1;
        width: 100%;
        min-width: 0;
        max-height: 50vh;
    }

    /* Schedule title row + buttons: wrap. */
    body[data-url-name="crew_schedule"] .main-content > :first-child {
        flex-wrap: wrap;
    }

    /* Reports + admin modals: full-width on smaller screens. */
    .manage-req-modal {
        width: 95vw !important;
        max-width: 95vw !important;
    }

    /* Generic flex utility rows: wrap so big buttons don't overflow. */
    .flex.justify-between { flex-wrap: wrap; gap: 6px; }

    /* Tasks/Roster bottom grid: stack vertical on smaller screens
     * (two-column is only useful with enough horizontal room). */
    .crew-bottom-grid {
        grid-template-columns: 1fr;
    }

    /* ---- Dashboard headers: let the global nav scroll away, pin ours ----
     * The wrapped top-nav is tall on a phone and rarely needed mid-scroll.
     * Let it flow off the top and instead pin the compact Projects header
     * (search + Filters toggle) so those stay reachable while scrolling a
     * long list. Scoped to the dashboard so other pages keep their sticky
     * nav. */
    body[data-url-name="dashboard"] .top-nav { position: static; }
    .dash-projects-header {
        position: sticky;
        top: 0;
        z-index: 50;
        background: var(--bg);
        flex-wrap: wrap;
        gap: 6px;
        padding: 6px 0;
        margin-bottom: 8px !important;
        box-shadow: 0 3px 6px -2px rgba(0, 0, 0, 0.12);
    }
    /* Search bar drops to its own full-width row under the title + buttons. */
    .dash-projects-header .dash-search-bar {
        order: 3;
        flex: 1 1 100%;
        width: 100%;
    }

    /* ---- Dashboard rows: wrap into vertical layout ----
     *  1. Left (project info)
     *  2. Milestones (only if any)
     *  3. Requirements
     *  4. People (Sales/PM/CC) + Next Step on the same row
     *  5. Action buttons
     *  6. Feed (notes/todos/specs) — full-width stacked (see notes block below)
     * Each block goes full-width except people+next-step which share
     * 50/50.
     */
    /* Single scroll surface on mobile. On desktop #projectList is its own
     * scroller (overflow:auto; max-height:calc(100vh - 200px)), which on a
     * phone means a short inner box nested inside the page — touch drags fight
     * between the inner list and the body and the whole thing feels "stuck".
     * Drop the inner scroller so the body scrolls everything as one surface;
     * that's also what lets the global nav scroll away while the projects
     * header stays pinned. The virtualizer copes: with no inner viewport it
     * just attaches all rows and the body scrolls them. */
    #projectList {
        overflow: visible;
        max-height: none;
    }
    /* The virtualized row container is position:absolute with
     * min-width:max-content, so it sizes to the widest DESKTOP row. That gives
     * every .dash-row full desktop width, so the flex-wrap rules below never
     * actually wrap. Pin it to the container width (left:0 + right:0) and drop
     * the max-content floor so rows reflow/stack on narrow screens. */
    #dash-rows {
        min-width: 0;
        right: 0;
    }
    .project-group {
        min-width: 0;
    }
    .dash-row {
        flex-wrap: wrap;
        min-width: 0;
    }
    .dash-row > .dash-col {
        flex: 0 0 100%;
        width: auto;
        min-width: 0;
        max-width: 100%;
        border-right: none;
        border-bottom: 1px solid var(--border-light);
    }
    .dash-row > .dash-col:last-child { border-bottom: none; }

    /* Project name: every other item in the header (watch/snooze/don't-care
     * buttons, badge, dot, the 100px number, RW + status chips, detail link)
     * is fixed-size, so the name is the only thing that can shrink — on a
     * narrow screen it collapses to its 4em floor (~10 characters).
     *
     * Let the header wrap, and order the name first with a 20em basis: while
     * that basis fits alongside the controls (tablet) it stays inline and
     * grows into the slack; once it doesn't (phone) it takes a line of its
     * own and, being alone on that line, gets the full row width. Two lines
     * max either way. */
    .dash-left-header-main {
        flex-wrap: wrap;
        row-gap: 4px;
    }
    .dash-left-header-main .dash-inline-name {
        order: -1;
        flex: 1 1 20em;
        min-width: 0;
    }

    /* The milestone-alert stripe is a 2em ::before pinned left:0 across
     * the whole row. In flat-row layout only the first column got the
     * cleared padding; in the wrapped vertical layout EVERY column sits
     * over the stripe, so push them all clear. */
    .dash-row-ms-overdue   > .dash-col,
    .dash-row-ms-alerting  > .dash-col,
    .dash-row-ms-upcoming  > .dash-col {
        padding-left: calc(2em + 10px);
    }
    .dash-row > .dash-col-left      { order: 1; }
    .dash-row > .dash-col-ms-alerts { order: 2; }
    .dash-row > .dash-col-reqs      { order: 3; }
    .dash-row > .dash-col-people    {
        order: 4;
        flex: 0 0 50%;
        max-width: 50%;
        border-right: 1px solid var(--border-light);
    }
    .dash-row > .dash-col-field     {
        order: 5;
    }
    .dash-row > .dash-col-actions   { order: 6; }
    .dash-row > .dash-col-notes     { order: 7; }

    /* Feed (notes/todos/specs) on smaller screens: full-width cards
     * stacked vertically. The old layout was a 1.5in-wide wrapping strip
     * inside a fixed ~96px `overflow-y:auto` window — on touch that inner
     * scroll region hijacked the drag, so the page felt stuck whenever a
     * finger landed on the feed. No inner scroll here: each card is the
     * full column width and the whole page scrolls as one surface. */
    .dash-col-notes {
        display: block;  /* drop the flex-row default so the strip can size to 100% */
    }
    .dash-col-notes .dash-notes-strip {
        flex-direction: column;
        flex-wrap: nowrap;
        max-height: none;
        overflow: visible;
        gap: 6px;
        width: 100%;
        box-sizing: border-box;
    }
    .dash-col-notes .dash-note-card {
        flex: none;
        width: 100%;
        box-sizing: border-box;
        height: auto;   /* grow to the clamped body height below */
    }
    /* Resting: clamp the note body to ~3 lines so a long note doesn't run
     * the row to full height. Focusing the cell (the shared editor moves
     * in → :focus-within) drops the clamp so the whole note is editable. */
    .dash-col-notes .dash-note-card .dash-feed-input {
        height: auto;
        line-height: 1.4;
        max-height: calc(1.4em * 3 + 8px);
        overflow: hidden;
    }
    .dash-col-notes .dash-note-card .dash-feed-input:focus-within,
    .dash-col-notes .dash-note-card .dash-feed-input:focus {
        max-height: none;
        overflow: visible;
    }

    /* Requirements grid: on desktop it flows into fixed 135px columns and
     * leans on the row's horizontal scroll to reveal the overflow. That
     * scroll is switched off on mobile (#dash-rows min-width:0 above), so
     * those fixed columns would instead push the whole page sideways.
     * Reflow into as many equal columns as the width allows, wrapping down
     * — no horizontal overflow. */
    .dash-col-reqs .dash-req-grid {
        grid-template-rows: none;
        grid-auto-flow: row;
        grid-auto-columns: auto;
        grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    }
    .dash-col-reqs .req-name {
        overflow: hidden;
        text-overflow: ellipsis;
    }
}

@media (max-width: 600px) {
    /* Top nav: bare-minimum compact — text scrunches but stays usable. */
    :root { --header-height: 44px; }
    .top-nav {
        padding: 2px 6px;
        gap: 2px 6px;
        font-size: 12px;
    }
    .top-nav .logo { font-size: 13px; margin-right: 8px; padding-left: 6px; }
    .top-nav .logo .logo-sub { font-size: 7px; }
    .top-nav .nav-links a { padding: 2px 4px; font-size: 11px; }
    .top-nav .nav-right { font-size: 11px; }
    .top-nav .nav-right .btn,
    .top-nav .nav-right .form-control { font-size: 11px; padding: 2px 6px; }
    .top-nav .nav-right .text-muted,
    .top-nav .nav-right label { display: none; }

    /* Main content: minimum side padding. */
    .main-content { padding: 6px 8px; }

    /* Forms: stack vertical. */
    .crew-task-form-inline,
    .form-grid {
        flex-direction: column;
        align-items: stretch;
    }
    .form-grid > * { width: 100% !important; }

    /* Long flex rows that aren't tables → vertical stacks. */
    .flex.gap-2, .flex.gap-4 {
        flex-wrap: wrap;
    }

    /* Page title rows: smaller heading, wrap controls. */
    h1 { font-size: 18px !important; }
    h2 { font-size: 16px; }

    /* Drawer (right-side slide-out modal): full width. */
    .drawer { width: 100% !important; max-width: 100% !important; }

    /* Pool sidebar: smaller pills, lower max-height. */
    .crew-pool-sidebar { max-height: 22vh; }
    .crew-pool-sidebar #taskPool .crew-task-block {
        font-size: 10px;
        padding: 2px 6px;
    }

    /* Change drawer: tighter so 4 rows still fit on a small screen. */
    .sheet-change-drawer-toggle { padding: 2px 8px; font-size: 11px; }
    .sheet-change-row { gap: 8px; }
    .sheet-change-row .sheet-change-name { max-width: 140px; }
    .sheet-change-row .sheet-change-day { display: none; }

    /* Spreadsheet: keep horizontal scroll, but tighten per-cell padding. */
    .sheet-table { font-size: 10px; }
    .sheet-table th,
    .sheet-table td { padding: 2px 4px; }
    .sheet-cell-input { font-size: 10px; padding: 1px 4px; }

    /* Modals: full viewport. */
    .manage-req-modal,
    .find-crew-modal {
        width: 100vw !important;
        max-width: 100vw !important;
        height: 100vh !important;
        max-height: 100vh !important;
        border-radius: 0;
    }
    .manage-req-modal-overlay { padding: 0; }

    /* Admin/unified modal on phone: the tab bar has ~20 tabs in one non-wrapping
     * row that runs off-screen and can't be reached. Let the header stack and
     * the tabs wrap to as many rows as needed so every tab is tappable, and
     * pin the close button to the corner so it never gets pushed away. */
    .manage-req-modal-header {
        flex-wrap: wrap;
        gap: 8px;
        padding: 12px 44px 12px 14px;   /* right pad clears the close button */
        position: relative;
    }
    .manage-req-modal-header > div {     /* the h3 + tabs wrapper (inline-styled) */
        flex-wrap: wrap;
        width: 100%;
    }
    .manage-req-tabs {
        flex-wrap: wrap;
        width: 100%;
    }
    .manage-req-tab {
        padding: 7px 12px;               /* bigger tap target than the 4px desktop */
        font-size: 13px;
    }
    .manage-req-modal-header .drawer-close {
        position: absolute;
        top: 10px;
        right: 12px;
        margin: 0;
    }

    /* Tab panels: reflow to a single vertical column and scroll vertically.
     * Toolbar rows (search + buttons) wrap instead of overflowing. Genuinely
     * tabular content (wide admin tables) scrolls horizontally inside its own
     * box rather than blowing the modal width out. */
    .admin-tab-panel { padding: 12px 14px; }
    .admin-tab-panel .flex { flex-wrap: wrap; }
    .admin-tab-panel .form-control { min-width: 0; }
    .admin-tab-panel table { width: max-content; min-width: 100%; }
    .admin-tab-panel > div[style*="overflow"] {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* Sidebar: collapsed-style minimal. */
    .sidebar { padding: 8px; }

    /* Reports: 1-col project picker, stack action buttons. */
    #reportSectionToggles { font-size: 11px; gap: 6px; }

    /* Crew sheet font: tiny by default to fit. */
    .crew-sheet-table { font-size: 9px; }

    /* Dashboard on phone: people + next-step stack full-width too
     * (50/50 was OK at half-page, too cramped at <600px). */
    .dash-row > .dash-col-people,
    .dash-row > .dash-col-field {
        flex: 0 0 100%;
        max-width: 100%;
        border-right: none;
    }
    /* Sales / PM / CC inline 3-up so the people row stays one row tall. */
    .dash-col-people {
        flex-direction: row !important;
        gap: 6px;
    }
    .dash-col-people .dash-people-field { flex: 1 1 0; min-width: 0; }

    /* ===== Mobile pass: main pages ===== */

    /* Top-nav project picker: clamp the dropdown to the viewport (it's absolute
     * and lives outside .main-content, so a 480px menu causes real page scroll)
     * and let the input go fluid instead of a rigid 200px. */
    .searchable-project-menu { max-width: calc(100vw - 16px); }
    .top-nav .searchable-project-picker { min-width: 0; flex: 1 1 100%; }
    .nav-project-input { width: 100%; }

    /* Calendar: the toolbar row wraps between groups but each group didn't wrap
     * its own items, so the 4-filter group ran off-screen. Let groups wrap and
     * the search field go fluid. (Month grid + spreadsheet already scroll in
     * their own boxes — left as horizontal-scroll-in-container by design.) */
    .cal-toolbar-group { flex-wrap: wrap; }
    .cal-toolbar-sep { display: none; }
    .cal-search { margin-left: 0 !important; width: 100%; }
    .cal-search-input { width: 100%; }
    .cal-select { flex: 1 1 auto; min-width: 0; }

    /* Reports: the injected report tables had no scroll wrapper, so they
     * scrolled all of main-content. Give each its own horizontal scroll box. */
    #reportContent .detail-section > table,
    #reportContent .crew-sheet-blocks,
    #reportContent .rpt-sched-sheet-table,
    #reportContent .rpt-crew-sched-table,
    #reportContent .rpt-fixture-table,
    #reportContent .hitlist-table {
        display: block;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    .filter-bar > div[style*="min-width"] { min-width: 0 !important; width: 100%; }

    /* Project detail: stack the header, wrap the dense todo/note rows and their
     * meta line, wrap the inline section-header flex rows, and box the wide
     * Requirements table so it scrolls inside itself. */
    .detail-header { flex-direction: column; }
    .todo-item, .note-item { flex-wrap: wrap; }
    .todo-item > div, .note-item > div { min-width: 0; }
    .todo-meta { flex-wrap: wrap; }
    #requirementsContainer {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    .detail-section > div[style*="display:flex"] { flex-wrap: wrap; }
    .detail-req-quickadd { margin-left: 0; }
    /* Fixtures/Trucking tables are table-layout:fixed with wide em columns and
     * no scroll wrapper — Trucking (~1048px) forces the whole page wide, and
     * Fixtures squishes to unusable cells. Box both so they scroll in place. */
    #fixtureTable, #truckingTable {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    /* Project detail: the sticky top-nav and the sticky section subnav fight
     * for the top slot on a phone — the wrapped nav is taller than
     * --header-height, so the subnav (pinned at that offset) covers it and the
     * nav is lost. Let the nav flow normally as a wrapping bar right above the
     * project title, and pin the section subnav to the very top instead. */
    body[data-url-name="project_detail"] .top-nav {
        position: static;
    }
    body[data-url-name="project_detail"] .detail-subnav {
        top: 0;
    }

    /* Todos list view: the dense grid is a ~900px fixed-track grid. Collapse it
     * to a wrapping flex row with the description on its own full-width line. */
    .todos-list-dense .todo-row-dense {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 4px 8px;
    }
    .todos-list-dense .todo-row-text { flex: 1 1 100%; }

    /* Crew schedule spreadsheet: the fixed-layout ~1320px sheet squished all 15
     * columns to fit. Let it size to content and scroll inside its body. Also
     * wrap the sheet toolbars, and stack the bottom tasks-panel's fixed 200px
     * edit column full-width. */
    #sheetViewBody { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    #sheetViewBody .sheet-table { width: max-content; min-width: 100%; table-layout: auto; }
    .sheet-view-toolbar { flex-wrap: wrap; gap: 6px 10px; }
    .sheet-view-toolbar > [style*="margin-left:auto"] { margin-left: 0; }
    .sheet-bulk-bar { flex-wrap: wrap; }
    .crew-tasks-panel { flex-direction: column; }
    .crew-task-edit-column {
        width: 100%;
        min-width: 0;
        border-left: none;
        border-top: 1px solid var(--border);
    }
    .crew-tasks-panel .crew-pool-create-form { flex-wrap: wrap !important; }

    /* Dashboard left column: the dates block + the 4 field textareas (Next
     * Step / Description / Location / PM Status) sit in a horizontal flex that
     * overflows on a phone. Stack the dates over the fields, and stack the
     * fields vertically full-width. */
    .dash-left-body { flex-direction: column; }
    .dash-left-dates {
        width: 100%;
        min-width: 0;
        padding-right: 0;
        border-right: none;
        border-bottom: 1px solid var(--border);
        padding-bottom: 6px;
        margin-bottom: 6px;
    }
    .dash-left-fields { flex-direction: column; }
    .dash-left-field {
        padding: 0;
        border-right: none;
        margin-bottom: 6px;
    }
    .dash-left-field:last-child { margin-bottom: 0; }
    .dash-inline-area { box-sizing: border-box; max-width: 100%; }
}

/* Spreadsheet calendar on a phone — PORTRAIT (narrow width) OR LANDSCAPE (short
   height): the frozen project column is narrow, so show ONLY the project name +
   the locator (⌖ scroll-to-dates) icon. Hide the order-number link, the
   drag/insert/reset controls, the expand caret, and the Status/Notes fields.
   (Matches ssLabelWidth()'s _ssIsPhone() clamp in calendar.js.) */
@media (max-width: 600px), (max-height: 500px) {
    .ss-project-number,
    .ss-no-order-number,
    .ss-drag-handle,
    .ss-move-btn,
    .ss-caret,
    .ss-insert-btn-inline,
    .ss-label-fields .ss-field-label,
    .ss-label-fields .ss-status-input,
    .ss-label-fields .ss-notes-input { display: none; }
}

@media print {
    .top-nav, .sidebar, .btn, .no-print,
    .milestone-alert-placeholder, #milestoneAlertBanner,
    #todoAlertBanner,
    .drawer, .drawer-overlay {
        display: none !important;
    }
    .rpt-internal {
        display: none !important;
    }
    .main-content {
        padding: 0;
    }
    .report-section {
        page-break-after: always;
    }
    /* On screen we use generous whitespace + a double rule for clarity.
       On paper that wastes vertical space — tighten it so sections pack
       together and the page-break engine has more leeway. */
    #reportContent .detail-section {
        padding-bottom: 8px;
        margin-bottom: 8px;
    }
    @page {
        size: landscape;
        margin: 0.5in;
    }
}

/* Add-row at the bottom of fixture/trucking spreadsheets. The separator row
   gives a clear visual break between existing data and the entry row. */
.add-row-separator td {
    border: none !important;
    padding: 0 !important;
    height: 8px;
    border-top: 2px solid #d1d5db !important;
    background: transparent;
}
.add-row td {
    background: #f9fafb;
    padding: 4px;
}
.add-row .inline-edit {
    width: 100%;
    box-sizing: border-box;
}

/* Items flagged internal — greyed out in browser, hidden in print. */
.rpt-internal {
    opacity: 0.4;
}

/* Sections toggled off via the top-bar checkboxes — hidden in browser
   and print alike. */
.rpt-section-hidden {
    display: none !important;
}

/* ---- Wave-2: "Clean view" (copy-for-email) ---- */
/* Same effect as the @media print rule above ('.no-print'/'.rpt-internal'
   hidden), but toggleable on-screen so a plain Ctrl+C also yields a clean
   copy without printing first. Scoped to #reportContent so the toolbar's
   own no-print controls (including this checkbox) stay visible. */
#reportContent.report-clean-view .no-print,
#reportContent.report-clean-view .rpt-internal {
    display: none !important;
}

/* ---- Wave-2: per-report-type column-hide popover ---- */
.rpt-col-toggle {
    position: relative;
    display: inline-block;
    font-size: 11px;
    color: #4b5563;
}
.rpt-col-toggle > summary {
    cursor: pointer;
    user-select: none;
}
.rpt-col-toggle-list {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    margin-top: 4px;
    padding: 6px 10px;
    background: #fff;
    border: 1px solid var(--border-light, #e5e7eb);
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    display: flex;
    flex-direction: column;
    gap: 2px;
    white-space: nowrap;
}
.rpt-col-toggle-list label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-weight: 400;
    color: #374151;
}

/* "Separate sections" toggle: each section starts on a new page when
   printing, and gets a thicker rule on screen so the boundaries read like
   independent sheets. */
@media print {
    .rpt-section-page-break {
        page-break-before: always;
    }
    .report-section > .rpt-section-page-break:first-child {
        page-break-before: auto;
    }
}

/* ---- Crew Sheet ---- */
.crew-sheet-table {
    border-collapse: collapse;
    font-size: var(--crew-sheet-font-size, 13px);
}
.crew-sheet-table th,
.crew-sheet-table td {
    border: 1px solid #9ca3af;  /* darker than default — stands out on the section gradient */
}
.crew-sheet-table thead th.crew-sheet-day {
    background: #f3f4f6;
}
.crew-sheet-table .crew-sheet-name {
    white-space: nowrap;
    padding: 4px 8px;
}
.crew-sheet-table .crew-sheet-cell {
    padding: 4px;
    text-align: center;
    font-size: 12px;
}
.crew-sheet-table .crew-sheet-day {
    text-align: center;
    padding: 2px 4px;
    white-space: nowrap;
}
/* Dark days — gap days with no project work. Striped grey background. */
.crew-sheet-table .crew-sheet-dark {
    background: repeating-linear-gradient(45deg, #e5e7eb, #e5e7eb 4px, #d1d5db 4px, #d1d5db 8px);
    color: #6b7280;
}
.crew-sheet-table thead th.crew-sheet-dark {
    background: #d1d5db;
    color: #4b5563;
    font-style: italic;
}
/* Each .crew-sheet-block is one row table holding CHUNKS_PER_ROW × 7 days
   plus a single Name/Phone/Email column on the left. Rows stack vertically;
   the name column repeats with each new row. Spacer cells provide a small
   visual gap between the 7-day groups within a row. */
.crew-sheet-blocks {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.crew-sheet-table .crew-sheet-spacer {
    border: none !important;
    background: transparent !important;
    width: 12px;
    padding: 0;
}
.crew-sheet-row-header {
    font-size: 12px;
    font-weight: 700;
    color: #4b5563;
    margin: 0 0 4px;
}
/* Title embedded inside the first row's page-break-inside container, so
   it cannot get stranded at the bottom of a page. Mirrors the look of a
   normal section H2. */
.crew-sheet-section-title {
    font-size: 16px;
    font-weight: 600;
    margin: 0 0 12px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border-light);
}
/* Synthetic "unfilled" row — italicized name, role still shown plain in
   the day cells so it reads like any other crew row. */
.crew-sheet-unfilled .crew-sheet-name em {
    font-style: italic;
    color: #6b7280;
}
.crew-sheet-team-tag {
    font-size: 9px;
    color: #6b7280;
    line-height: 1.1;
}

/* Section header with inline option toggles. Section H2 stays on the
   left at its normal weight; the .rpt-section-opts cluster sits to the
   right with smaller text and faint dividers between checkboxes. */
.rpt-section-head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.rpt-section-head h2 { margin: 0; }
.rpt-section-opts {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    font-size: 12px;
    font-weight: 400;
    color: var(--text-muted);
}
.rpt-section-opts label {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
}

/* Crew Schedule report section — one table per project, banner rows
   separate days. table-layout: fixed + colgroup widths keep every day's
   columns aligned regardless of content. */
.rpt-crew-sched-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    font-size: 12px;
    margin-bottom: 8px;
}
.rpt-crew-sched-table th,
.rpt-crew-sched-table td {
    padding: 3px 6px;
    border: 1px solid #e5e7eb;
    text-align: left;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rpt-crew-sched-row-headers th {
    background: #f9fafb;
    font-weight: 600;
    font-size: 11px;
}
.rpt-crew-sched-day-banner td {
    background: #f3f4f6;
    border-left: 3px solid #6b7280;
    font-weight: 600;
    font-size: 13px;
    padding: 5px 8px;
}
.rpt-crew-sched-table td.text-center { text-align: center; }
/* Column widths for the fixed-layout grid. Sum is 100%; values chosen so
   crew names get the most room and time/E-A stay narrow. */
.rpt-cs-w-crew { width: 18%; }
.rpt-cs-w-role { width: 11%; }
.rpt-cs-w-team { width: 11%; }
.rpt-cs-w-ea   { width: 5%; }
.rpt-cs-w-time { width: 9%; }
.rpt-cs-w-meal { width: 13%; }
.rpt-cs-w-pnp  { width: 3%; }
.rpt-cs-w-paid { width: 5%; }

/* Print-only truncation: full version on screen, short version (~7 chars)
   when printing. Used in the crew sheet so names + project labels don't push
   the day grid off the page. */
.print-trunc-short { display: none; }
@media print {
    .print-trunc-full { display: none; }
    .print-trunc-short { display: inline; }
}

/* Clickable phone/email cells in the crew sheet — build a bulk-message
   list to paste into Messages or Gmail. */
.crew-bulk-clickable {
    cursor: pointer;
    color: #2563eb;
}
.crew-bulk-clickable:hover {
    text-decoration: underline;
}
.crew-edit-icon {
    cursor: pointer;
    color: #9ca3af;
    font-size: 10px;
    margin-left: 6px;
    opacity: 0;
    transition: opacity 0.1s;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.crew-contact-cell:hover .crew-edit-icon {
    opacity: 1;
}
.crew-edit-icon:hover {
    color: #2563eb;
    text-decoration: underline;
}
.crew-contact-input {
    width: 100%;
    box-sizing: border-box;
    padding: 2px 4px;
    border: 1px solid #2563eb;
    border-radius: 3px;
    font: inherit;
}
.crew-bulk-container {
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    background: #f3f4f6;
    border: 1px solid #d1d5db;
    border-radius: 4px;
    font-size: 13px;
}
.crew-bulk-label {
    font-weight: 600;
    color: #4b5563;
    /* Excluded from clipboard copy via JS — separate span from the list. */
}
.crew-bulk-list {
    font-family: ui-monospace, SFMono-Regular, monospace;
    font-size: 12px;
    user-select: text;
    max-width: 60vw;
    overflow-x: auto;
    white-space: nowrap;
}
@media print {
    /* ---- Strict mode (default) ----
       Big containers stay together at the cost of trailing whitespace
       at page bottoms. */
    .crew-sheet-block {
        page-break-inside: avoid;
    }
    .rpt-day-block {
        page-break-inside: avoid;
    }
    /* Don't orphan a section header at the bottom of a page. */
    #reportContent .detail-section h2 {
        page-break-after: avoid;
    }
    .crew-sheet-row-header {
        page-break-after: avoid;
    }

    /* ---- Compact mode ----
       Body class toggled by the "Compact print" checkbox. Loosens the big
       containers above so content packs tightly across page boundaries,
       and only protects small atomic units (single day cards, individual
       table rows / list items) from being sliced. */
    body.print-compact .crew-sheet-block,
    body.print-compact #reportContent .detail-section h2,
    body.print-compact .crew-sheet-row-header {
        page-break-after: auto !important;
        page-break-inside: auto !important;
    }
    body.print-compact .rpt-day-block,
    body.print-compact .rpt-req-card,
    body.print-compact .note-item,
    body.print-compact .todo-item,
    body.print-compact .rpt-fixture-table tr,
    body.print-compact .crew-sheet-table tr {
        page-break-inside: avoid;
    }

    /* Schedule Spreadsheet section in compact print:
     *  • Drop the per-day bottom margin so days pack edge-to-edge.
     *  • Let the day's container break across pages (the heavy 2px
     *    border still marks the boundary visually).
     *  • Only protect individual task / note rows from being sliced
     *    so a row of text never gets cut in half.
     */
    body.print-compact .rpt-sched-sheet-day {
        margin-bottom: 0;
        page-break-inside: auto;
    }
    body.print-compact .rpt-sched-sheet-table tbody tr {
        page-break-inside: avoid;
    }

    /* Tighter padding + smaller default font so a row of crew typically
       fits within one landscape page (~7.5" tall). */
    .crew-sheet-table {
        font-size: var(--crew-sheet-print-font-size, 9px);
    }
    .crew-sheet-table th,
    .crew-sheet-table td {
        padding: 1px 3px;
    }
    .crew-sheet-table .crew-sheet-cell {
        padding: 1px 2px;
    }
    .crew-sheet-table .crew-sheet-name {
        padding: 1px 4px;
    }
}

/* Fixture table on the report. */
.rpt-fixture-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.rpt-fixture-table th,
.rpt-fixture-table td {
    text-align: left;
    padding: 4px 8px;
    border-bottom: 1px solid #e5e7eb;
    vertical-align: top;
}
.rpt-fixture-table th {
    font-weight: 600;
    background: #f9fafb;
}

/* ---- Filter Bar ---- */

.filter-bar {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.filter-bar select,
.filter-bar input {
    padding: 5px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 13px;
    font-family: inherit;
}

/* ---- Tabs ---- */

.tab-bar {
    display: flex;
    gap: 0;
    border-bottom: 2px solid var(--border);
    margin-bottom: 16px;
}

.tab-bar .tab {
    padding: 8px 16px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: color 0.15s, border-color 0.15s;
    background: none;
    border-top: none;
    border-left: none;
    border-right: none;
}

.tab-bar .tab:hover {
    color: var(--text);
}

.tab-bar .tab.active {
    color: var(--primary);
    border-bottom-color: var(--primary);
}

.tab-content {
    display: none;
}

.tab-content.active {
    display: block;
}

/* ---- Empty State ---- */

.empty-state {
    text-align: center;
    padding: 40px;
    color: var(--text-muted);
}

.empty-state p {
    margin-bottom: 12px;
}

/* ---- Login Page ---- */

.login-page {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background: var(--bg-alt);
}

.login-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 32px;
    width: 360px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.login-card h1 {
    font-size: 22px;
    margin-bottom: 24px;
    text-align: center;
}

.login-card .form-group {
    margin-bottom: 16px;
}

.login-card .btn-primary {
    width: 100%;
    padding: 10px;
    font-size: 14px;
}

/* ---- Utilities ---- */

/* ---- Manage Requirements Modal ---- */

.manage-req-modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
}

.manage-req-modal {
    background: var(--bg);
    border-radius: 12px;
    width: 80vw;
    max-width: 90vw;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

.manage-req-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border);
}

.manage-req-modal-header h3 {
    font-size: 16px;
    font-weight: 600;
}

.manage-req-modal-body {
    padding: 16px 20px;
    overflow-y: auto;
    flex: 1;
}

.manage-req-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border-light);
}

.manage-req-item:last-child {
    border-bottom: none;
}

.manage-req-tabs {
    display: flex;
    gap: 2px;
    background: var(--border-light);
    border-radius: 6px;
    padding: 2px;
}

.manage-req-tab {
    padding: 4px 14px;
    font-size: 12px;
    font-weight: 500;
    border: none;
    background: transparent;
    border-radius: 4px;
    cursor: pointer;
    color: var(--text-muted);
}

.manage-req-tab.active {
    background: var(--bg);
    color: var(--text);
    box-shadow: 0 1px 2px rgba(0,0,0,0.08);
}

/* Admin modal: give every assorted table real cell/column borders. Many of the
   tab tables (.admin-table, .manage-table, the inline border-collapse ones,
   .table-sm, todostatus-table, …) had no column separators. Scoped to the
   admin overlay so no other modal is affected. */
#adminModalOverlay table {
    border-collapse: collapse;
}
#adminModalOverlay table th,
#adminModalOverlay table td {
    border: 1px solid var(--border);
    padding: 2px 6px;
}

/* ---- DateType Grid (spreadsheet-style modal) ---- */
.dt-grid {
    display: grid;
    grid-template-columns: 60px 1fr 50px 62px 74px 64px 64px 64px 32px;
    gap: 4px;
    align-items: center;
    padding: 4px 0;
}
.dt-grid-header {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 2px;
}
.dt-grid-row {
    border-bottom: 1px solid var(--border-light);
    padding: 3px 0;
}
.dt-grid-row:hover {
    background: var(--bg-alt);
}
.dt-col-sort { text-align: center; }
.dt-col-name { min-width: 0; }
.dt-col-color { text-align: center; justify-self: center; }
.dt-col-check { text-align: center; justify-self: center; }
.dt-col-del { text-align: center; justify-self: center; }
.dt-grid-row .form-control {
    font-size: 12px;
    padding: 3px 6px;
    height: auto;
}
.dt-color-input {
    width: 32px;
    height: 26px;
    padding: 1px;
    border: 1px solid var(--border);
    border-radius: 4px;
    cursor: pointer;
    background: none;
}
.dt-check-label {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    cursor: pointer;
}
.dt-check {
    width: 16px;
    height: 16px;
    cursor: pointer;
}
.dt-del-btn {
    color: #991b1b !important;
    font-size: 16px;
    line-height: 1;
    padding: 2px 6px;
}
.dt-del-btn:hover {
    background: #fee2e2;
}

/* ---- DateType delete modal (in-use: replace or cascade) ---- */
.dt-del-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 10001;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}
.dt-del-modal-box {
    background: var(--bg, #fff);
    color: var(--text, #111);
    border: 1px solid var(--border);
    border-radius: 8px;
    max-width: 520px;
    width: 100%;
    padding: 18px 20px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
    font-size: 13px;
}
.dt-del-modal-head {
    font-size: 15px;
    font-weight: 700;
    margin-bottom: 10px;
}
.dt-del-usage { margin-bottom: 10px; line-height: 1.5; }
.dt-del-projs { margin-top: 4px; color: var(--text-muted); font-size: 12px; }
.dt-del-note {
    background: #fffbeb;
    border: 1px solid #fcd34d;
    color: #92400e;
    border-radius: 6px;
    padding: 7px 10px;
    font-size: 12px;
    margin-bottom: 12px;
}
.dt-del-section {
    border-top: 1px solid var(--border-light);
    padding-top: 12px;
    margin-top: 4px;
}
.dt-del-section-title { font-weight: 600; margin-bottom: 8px; }
.dt-del-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.dt-del-alias-check,
.dt-del-alias-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    font-size: 12px;
    color: var(--text-muted);
    flex-wrap: wrap;
}
.dt-del-danger .dt-del-section-title { color: #991b1b; }
.dt-del-everything { margin-top: 10px; }
.dt-del-modal-foot { margin-top: 16px; text-align: right; }

/* ---- API Feedback States ---- */

/* Global API toast — fixed pill at top-center. */
.api-toast {
    position: fixed;
    top: 8px;
    left: 50%;
    transform: translateX(-50%) translateY(-40px);
    padding: 6px 14px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    z-index: 9999;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s, transform 0.15s;
    white-space: pre-line;
    text-align: center;
    max-width: 80vw;
}
.api-toast.visible {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}
.api-toast-pending { background: #fefce8; color: #92400e; border: 1px solid #facc15; }
.api-toast-success { background: #f0fdf4; color: #166534; border: 1px solid #22c55e; }
.api-toast-error   { background: #fef2f2; color: #991b1b; border: 1px solid #ef4444; }

/* Failed-save flash: an optimistic change stuck in the UI but the server
   rejected it. Applied by _flashError() to the triggering element (input, row,
   card, button…). Fades on its own. */
.api-error-flash {
    background-color: #fef2f2 !important;
    box-shadow: 0 0 0 2px #dc2626 inset !important;
    border-color: #dc2626 !important;
    border-radius: 4px;
    transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

.feedback-pending {
    background-color: #fefce8 !important;
    border-color: #facc15 !important;
    transition: background-color 0.15s, border-color 0.15s;
}

.feedback-success {
    background-color: #f0fdf4 !important;
    border-color: #22c55e !important;
    transition: background-color 0.15s, border-color 0.15s;
}

.feedback-error {
    background-color: #fef2f2 !important;
    border-color: #ef4444 !important;
    transition: background-color 0.15s, border-color 0.15s;
}

/* ---- Utilities ---- */

.text-muted { color: var(--text-muted); }
.text-sm { font-size: 12px; }
.text-xs { font-size: 11px; }
.mt-2 { margin-top: 8px; }
.mt-4 { margin-top: 16px; }
.mb-2 { margin-bottom: 8px; }
.mb-4 { margin-bottom: 16px; }
.flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.gap-2 { gap: 8px; }
.gap-4 { gap: 16px; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.hidden { display: none; }

/* Impersonation banner */
.impersonation-banner {
    background: #d32f2f;
    color: #fff;
    padding: 8px 20px;
    font-size: 0.85rem;
    text-align: center;
    font-weight: 500;
}
.impersonation-banner strong {
    font-weight: 700;
}

/* Milestone alert banner */
.milestone-alert-placeholder {
    min-height: 38px;
}
/* Collapse when there is nothing to show. This used to also reserve 38px, so
   three usually-empty banners held ~114px of dead space on every page and
   pushed .page-wrapper down past where the sticky panes thought it was. */
.milestone-alert-placeholder:empty {
    min-height: 0;
}
.milestone-alert-banner {
    background: #fef3c7;
    border-bottom: 1px solid #f59e0b;
    padding: 8px 20px;
    font-size: 13px;
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}
.milestone-alert-banner.has-overdue {
    background: #fee2e2;
    border-bottom-color: #ef4444;
}
/* Background-refresh banner — subtle blue/neutral variant of the alert banner.
   Shown only when a live change was deferred because the user was mid-edit. */
.bg-refresh-banner {
    background: #eff6ff;
    border-bottom: 1px solid #93c5fd;
    color: #1e3a5f;
}
.bg-refresh-banner .milestone-alert-label {
    font-weight: 600;
}
.bg-refresh-apply {
    color: #2563eb;
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
}
.bg-refresh-apply:hover {
    color: #1d4ed8;
}
.bg-refresh-dismiss {
    margin-left: auto;
    background: none;
    border: none;
    color: #64748b;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 0 4px;
}
.bg-refresh-dismiss:hover {
    color: #1e293b;
}

/* Always-on freshness badge: "Refreshed 4:55pm · 13 changes". Small, fixed in
   the bottom-right, subtle until it flashes on a change. */
.bg-refreshed-badge {
    position: fixed;
    right: 10px;
    bottom: 10px;
    z-index: 900;
    padding: 3px 9px;
    border-radius: 10px;
    background: rgba(30, 41, 59, 0.82);
    color: #e2e8f0;
    font-size: 11px;
    line-height: 1.4;
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    transition: background 0.4s, color 0.4s;
}
.bg-refreshed-badge--flash {
    background: #1a7f37;
    color: #fff;
}

/* Year badge on the detail Dates rows — surfaces the real year hidden behind
   the M/D display. Non-current years turn red so stale dates are obvious. */
.date-year-badge {
    font-size: 11px;
    font-weight: 700;
    color: #6b7280;
    min-width: 52px;
    padding: 1px 6px;
    border-radius: 6px;
    background: #f3f4f6;
    text-align: center;
    flex-shrink: 0;
}
.date-year-badge.year-old {
    background: #fee2e2;
    color: #b91c1c;
}

/* Kanban "+ Add column" — creates a private per-user status column. */
.kb-col-add {
    min-width: 120px;
    background: transparent;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 8px;
}
.kb-add-col-btn {
    border: 1px dashed #9ca3af;
    background: transparent;
    color: #6b7280;
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 12px;
    cursor: pointer;
    white-space: nowrap;
}
.kb-add-col-btn:hover {
    border-color: #4b5563;
    color: #374151;
    background: #f9fafb;
}

/* Project-search boxes (dashboard + calendar Find-Project) turn highlighter
   yellow while focused, so it's obvious you're in search mode (Ctrl/Cmd-F). */
#dashSearchBar:focus,
#calSearchInput:focus {
    background: #fff275;
}

/* Red border on the element the user is focused in when an update is waiting on
   it (applied via the "apply updates" banner, or on blur). outline = no reflow. */
.bg-focus-pending {
    outline: 2px solid #e11d48 !important;
    outline-offset: -1px;
    border-radius: 3px;
}
.milestone-alert-item {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 4px;
    background: rgba(0,0,0,0.06);
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}
.milestone-alert-item:hover {
    background: rgba(0,0,0,0.1);
}
.milestone-alert-item.overdue {
    color: #dc2626;
    font-weight: 600;
}
.milestone-alert-item.alerting {
    color: #d97706;
    font-weight: 600;
}
.milestone-alert-label {
    font-weight: 700;
    margin-right: 4px;
    white-space: nowrap;
}

/* Watch button on dashboard */
.dash-watch-btn {
    cursor: pointer;
    font-size: 14px;
    color: #d1d5db;
    flex-shrink: 0;
    user-select: none;
    transition: color 0.15s;
    line-height: 1;
}
.dash-watch-btn:hover {
    color: #f59e0b;
}
.dash-watch-btn.watched {
    color: #f59e0b;
}

/* Snooze / don't-care (level-of-attention) row controls */
.dash-snooze-btn,
.dash-dontcare-btn {
    cursor: pointer;
    font-size: 12px;
    flex-shrink: 0;
    user-select: none;
    line-height: 1;
    opacity: 0.4;
    filter: grayscale(1);
    transition: opacity 0.15s, filter 0.15s;
}
.dash-snooze-btn:hover,
.dash-dontcare-btn:hover,
.dash-dontcare-btn.active {
    opacity: 1;
    filter: none;
}
.dash-attention-badge {
    font-size: 11px;
    font-style: italic;
    color: var(--text-muted);
    flex-shrink: 0;
}
.dash-attention-badge:empty {
    display: none;
}
/* Revealed-but-muted rows (snoozed / don't-care) render greyed when
   "Show snoozed/ignored" is on; hidden entirely otherwise (JS filter). */
.dash-row-muted {
    opacity: 0.5;
}
.dash-row-muted:hover {
    opacity: 0.85;
}
/* Just-cancelled row: greyed + faint red wash so it reads as "cancelled,
   pending removal on next refresh" (distinct from the plain muted state).
   Kept visible on purpose so the click doubles as undo. */
.dash-row-cancelled {
    opacity: 0.55;
    background: #fef2f2;
}
.dash-row-cancelled:hover {
    opacity: 0.85;
}
.dash-cancel-btn {
    cursor: pointer;
    opacity: 0.55;
    font-size: 15px;
    line-height: 1;
}
.dash-cancel-btn:hover { opacity: 1; }
.dash-cancel-btn.active {
    opacity: 1;
    color: #b91c1c;
}
/* Project-detail header "Don't care" toggle (active = marked) */
.dontcare-toggle.active {
    background: #fee2e2;
    border-color: #ef4444;
    color: #b91c1c;
}

/* ---- Import page ---- */
.import-section h2 {
    font-size: 16px;
    font-weight: 600;
    margin-bottom: 8px;
}
.import-initials-table {
    border-collapse: collapse;
    font-size: 13px;
}
.import-initials-table th {
    text-align: left;
    padding: 4px 12px 4px 0;
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    color: var(--text-muted);
}
.import-initials-table td {
    padding: 4px 12px 4px 0;
}
.import-summary {
    background: var(--bg-alt);
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 13px;
    display: flex;
    align-items: center;
    gap: 4px;
}
.import-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-bottom: 1px solid #f0f0f0;
    font-size: 13px;
    transition: opacity 0.15s;
}
.import-row:hover {
    background: #fafafa;
}
.import-row-unchecked {
    opacity: 0.45;
}
.import-row-header {
    background: #f3f4f6;
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    color: var(--text-muted);
}
.import-check-label {
    display: flex;
    align-items: center;
    cursor: pointer;
    margin: 0;
    flex-shrink: 0;
}
.import-name {
    flex: 3;
    min-width: 200px;
    font-weight: 500;
}
.import-sales {
    flex: 1.5;
    min-width: 100px;
    color: var(--text-muted);
    font-size: 12px;
}
.import-status {
    width: 60px;
    font-size: 11px;
    color: var(--text-muted);
}
.import-job {
    width: 100px;
    font-size: 11px;
    font-family: monospace;
    color: var(--text-muted);
}
.import-dates-count,
.import-crew-count {
    width: 55px;
    font-size: 11px;
    color: var(--text-muted);
    text-align: right;
}
.import-match {
    width: 130px;
    text-align: right;
}
.import-badge {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}
.import-badge-new {
    background: #dcfce7;
    color: #15803d;
}
.import-badge-update {
    background: #fef3c7;
    color: #92400e;
}
.import-badge-header {
    background: #e5e7eb;
    color: #6b7280;
}
.import-badge-fuzzy {
    background: #dbeafe;
    color: #1d4ed8;
    margin-left: 4px;
}
.import-flags {
    width: 100%;
    padding-left: 28px;
}
.import-flag {
    display: inline-block;
    font-size: 11px;
    color: #d97706;
    background: #fffbeb;
    padding: 1px 6px;
    border-radius: 3px;
    margin-right: 4px;
}
.import-match-info {
    width: 100%;
    padding-left: 28px;
}
.import-results-box {
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    padding: 16px;
}
.import-errors {
    color: #dc2626;
    margin-top: 8px;
    font-size: 13px;
}
.import-details-list {
    max-height: 300px;
    overflow-y: auto;
    margin-top: 4px;
    padding: 4px 0;
}

/* ---- Kanban Board ---- */

.kanban-board {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding-bottom: 12px;
    align-items: flex-start;
}

.kb-col {
    flex: 0 0 220px;
    min-width: 220px;
    background: var(--bg-alt);
    border: 1px solid var(--border-light);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    overflow: visible;
}

.kb-col-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-light);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--text-muted);
}

.kb-col-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Borrowed column — a status you can see only because someone else's visible
   todo uses it. Greyed but fully usable (drag/drop into it works). */
.kb-col-foreign > .kb-col-header .kb-col-title {
    color: var(--text-muted);
    font-style: italic;
}
.kb-col-foreign > .kb-col-header {
    opacity: 0.85;
}

.kb-col-count {
    background: var(--border);
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 700;
    padding: 1px 7px;
    border-radius: 10px;
    min-width: 20px;
    text-align: center;
    flex-shrink: 0;
}

.kb-quick-add {
    padding: 6px 8px;
    border-bottom: 1px solid var(--border-light);
}

.kb-quick-input {
    width: 100%;
    border: 1px solid var(--border-light);
    border-radius: 4px;
    padding: 5px 8px;
    font-size: 12px;
    background: var(--bg);
    color: var(--text);
    outline: none;
    transition: border-color 0.15s;
}

.kb-quick-input:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

.kb-quick-input::placeholder {
    color: var(--text-light);
}

.kb-cards {
    flex: 1;
    overflow-y: visible;
    padding: 6px 8px;
    min-height: 40px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: background 0.15s;
}

.kb-cards:empty::after {
    content: '';
    display: block;
    min-height: 30px;
}

.kb-card {
    background: var(--bg);
    border: 1px solid var(--border-light);
    border-radius: 6px;
    padding: 8px 10px;
    cursor: grab;
    transition: box-shadow 0.15s, opacity 0.15s, border-color 0.15s;
    user-select: none;
    position: relative;
    overflow: visible;
}

.kb-card:hover {
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
    border-color: var(--border);
}

.kb-card:active {
    cursor: grabbing;
}

/* The dragging card stays in the DOM as a "placeholder" — it's moved live
 * to the cursor's insertion point so siblings reflow around it. Style it as
 * an outline/ghost so the drop target is visually obvious. */
.kb-card-dragging {
    opacity: 0.45;
    border-style: dashed !important;
    border-color: var(--primary, #3b82f6) !important;
    background: rgba(59, 130, 246, 0.06) !important;
    box-shadow: none !important;
}
.kb-card-dragging * { pointer-events: none; }

.kb-drop-over {
    background: rgba(59, 130, 246, 0.06);
    border-radius: 0 0 8px 8px;
}

.kb-card-selected {
    border-color: var(--primary);
    background: #eff6ff;
    box-shadow: 0 0 0 1px var(--primary);
}

.kb-card-top {
    display: flex;
    align-items: flex-start;
    gap: 5px;
}

.kb-drag-handle {
    cursor: grab;
    color: var(--text-light);
    font-size: 14px;
    line-height: 1;
    padding: 1px 0;
    flex-shrink: 0;
    user-select: none;
    transition: color 0.15s;
}

.kb-drag-handle:hover {
    color: var(--text-muted);
}

.kb-drag-handle:active {
    cursor: grabbing;
}

.kb-card-cb {
    margin-top: 2px;
    flex-shrink: 0;
    cursor: pointer;
}

.kb-card-text {
    font-size: 13px;
    line-height: 1.4;
    word-break: break-word;
    flex: 1;
    cursor: pointer;
}

.kb-card-link {
    color: var(--primary);
    text-decoration: none;
    font-weight: 600;
}

.kb-card-link:hover {
    text-decoration: underline;
}

.kb-card-assign {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 4px;
    padding: 2px 4px;
    border-radius: 3px;
    cursor: pointer;
    position: relative;
    transition: background 0.15s;
    user-select: none;
}

.kb-card-assign:hover {
    background: var(--bg-hover);
}

.kb-assign-icon {
    font-size: 11px;
}

.kb-assign-input {
    border: 1px solid var(--primary);
    border-radius: 4px;
    padding: 3px 8px;
    font-size: 12px;
    outline: none;
    box-shadow: 0 2px 8px rgba(0,0,0,0.12);
    background: var(--bg);
    color: var(--text);
}

.kb-card-text.completed {
    text-decoration: line-through;
    color: var(--text-muted);
}

.kb-card-text-edit {
    font-size: 13px;
    line-height: 1.4;
    word-break: break-word;
    white-space: pre-wrap;
    flex: 1;
    border: 1px solid transparent;
    padding: 2px 4px;
    border-radius: 3px;
    background: transparent;
    font-family: inherit;
    width: 100%;
    min-height: 22px;
    /* Default: collapse to 5 lines. Focused/hovered-to-edit = expand fully. */
    max-height: calc(1.4em * 5 + 4px);
    overflow: hidden;
    resize: none;
    transition: max-height 0.15s ease;
}
.kb-card-text-edit:hover { border-color: var(--border-light); background: var(--bg-alt); }
.kb-card-text-edit:focus {
    outline: none;
    border-color: var(--primary);
    background: var(--bg);
    box-shadow: 0 0 0 2px rgba(59,130,246,.15);
    max-height: none;
    overflow: auto;
    resize: vertical;
}
.kb-card-text-edit.completed { text-decoration: line-through; color: var(--text-muted); }

.kb-card-meta {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 5px;
    line-height: 1.3;
}

/* Kanban card urgency backgrounds */
.kb-card-week {
    background: #fefce8;
    border-color: #fde68a;
}
.kb-card-soon {
    background: #fff7ed;
    border-color: #fdba74;
}
.kb-card-today {
    background: #fef2f2;
    border-color: #fca5a5;
}
.kb-card-overdue {
    background: #fef2f2;
    border: 3px solid #f87171;
}

/* ---- Todo Status Badges ---- */

.todo-status-badge {
    display: inline-block;
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 3px;
    font-weight: 600;
    letter-spacing: 0.2px;
}

.todo-status-not_started {
    background: #f3f4f6;
    color: #6b7280;
}
.todo-status-in_progress {
    background: #dbeafe;
    color: #1d4ed8;
}
.todo-status-on_hold {
    background: #fef3c7;
    color: #92400e;
}
.todo-status-waiting {
    background: #f3e8ff;
    color: #7c3aed;
}
.todo-status-complete {
    background: #dcfce7;
    color: #15803d;
}

/* One-shot highlight for a newly created project row pinned to top. */
.dash-row-pin-flash {
    animation: dashPinFlash 2s ease-out;
}
@keyframes dashPinFlash {
    0%   { background-color: #fff7d6; }
    100% { background-color: transparent; }
}

/* Hidden schedule day — visually muted + strikethrough so the user can see
 * at a glance which days are excluded from calendar/schedule rendering.
 * Inputs stay clickable; they're how the user un-hides the row. */
.sched-day-hidden { opacity: 0.55; }
.sched-day-hidden .inline-edit,
.sched-day-hidden > span.text-sm { text-decoration: line-through; }

/* Watch toggle on project detail header. */
.btn.watch-toggle { display:inline-flex; align-items:center; gap:4px; }
.btn.watch-toggle .watch-star { font-size: 14px; line-height: 1; color: #9ca3af; }
.btn.watch-toggle.is-watched { background:#fff7d6; border-color:#f59e0b; }
.btn.watch-toggle.is-watched .watch-star { color: #f59e0b; }

/* Kanban status pills — consistent colored label on every card. */
.kb-status-pill {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    letter-spacing: 0.2px;
    border: 1px solid transparent;
}
.kb-status-pill.status-not_started { background: #f3f4f6; color: #4b5563; border-color: #e5e7eb; }
.kb-status-pill.status-in_progress { background: #dbeafe; color: #1d4ed8; border-color: #bfdbfe; }
.kb-status-pill.status-on_hold     { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.kb-status-pill.status-waiting     { background: #ede9fe; color: #5b21b6; border-color: #ddd6fe; }
.kb-status-pill.status-complete    { background: #dcfce7; color: #15803d; border-color: #bbf7d0; }

/* Reports: column-major requirements — columns of flex cards, stacked 8 tall
 * then overflow to the next column. Each card auto-widths around its content
 * (name + inline flag chips). Column width is fixed so the layout stays
 * readable in print. */
.rpt-req-cols {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: flex-start;
}
.rpt-req-col {
    flex: 0 0 auto;
    width: 320px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.rpt-req-card {
    border: 1px solid var(--border-light, #e5e7eb);
    border-radius: 4px;
    padding: 4px 8px;
    background: #fff;
    font-size: 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.rpt-req-card-title { line-height: 1.3; }
.rpt-req-card-flags { display: flex; flex-wrap: wrap; gap: 6px 10px; }
.rpt-req-flag {
    font-size: 11px;
    color: #4b5563;
    white-space: nowrap;
}

/* Do Next status pill */
.kb-status-pill.status-do_next { background: #fee2e2; color: #991b1b; border-color: #fecaca; }

/* Status pill is clickable — opens kb-status-menu (see below). */
.kb-status-pill-clickable { user-select: none; }
.kb-status-pill-clickable:hover { filter: brightness(0.95); }

.kb-status-menu {
    background: #fff;
    border: 1px solid var(--border, #d1d5db);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 120px;
}
.kb-status-menu-item {
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    border: 1px solid transparent;
    cursor: pointer;
    text-align: center;
    line-height: 1.4;
}
.kb-status-menu-item:hover { filter: brightness(0.95); }
.kb-status-menu-item.kb-status-menu-active { box-shadow: 0 0 0 2px rgba(0,0,0,0.15) inset; }

/* Import: daily-update per-row category checkboxes + owner tag. */
.import-owner-tag {
    display: inline-block;
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 10px;
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #fde68a;
    margin-left: 6px;
}
.import-daily-cats {
    margin-top: 4px;
    padding: 4px 8px;
    background: rgba(59, 130, 246, 0.05);
    border-left: 2px solid #3b82f6;
    border-radius: 0 3px 3px 0;
    font-size: 12px;
}

/* Admin: Todo Statuses tab */
.todostatus-table th, .todostatus-table td {
    padding: 4px 6px;
    border-bottom: 1px solid var(--border-light, #e5e7eb);
    vertical-align: middle;
    text-align: left;
}
.todostatus-table th { font-size: 11px; color: #6b7280; font-weight: 600; }
.todostatus-row.ts-dragging { opacity: 0.4; }

/* Todos: Personal toggle chip (click on Generic/Personal label). */
.kb-personal-toggle {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid #d1d5db;
    background: #f3f4f6;
    color: #4b5563;
    user-select: none;
    transition: all 0.12s;
}
.kb-personal-toggle:hover { background: #e5e7eb; }
.kb-personal-toggle.is-on {
    background: #ecfeff;
    color: #0e7490;
    border-color: #a5f3fc;
}

/* Public/private toggle for PROJECT todos (🌐 public / 🔒 private). */
.todo-pub-toggle {
    cursor: pointer;
    font-size: 12px;
    line-height: 1;
    padding: 0 2px;
    user-select: none;
    opacity: 0.85;
}
.todo-pub-toggle:hover { opacity: 1; }
.todo-pub-toggle.is-private { opacity: 0.7; }

/* Private (non-public) project todos read muted across every surface. */
.todos-list-dense .todo-row-private .todo-row-text {
    color: #6b7280;
    font-style: italic;
}
.todo-item.todo-row-private > div .inline-edit { font-style: italic; color: #6b7280; }
.kb-card-private { border-left: 2px dashed #9ca3af; }
.dash-feed-private { font-size: 10px; margin-left: 3px; opacity: 0.65; vertical-align: middle; }

/* Snoozed cards */
.kb-card-snoozed { opacity: 0.2; }
.kb-card-snoozed:hover { opacity: 0.8; }

/* A status-change save that failed on the server — the optimistic move stayed
   put; red flags it so the user knows to retry. */
.kb-card-error {
    border: 1.5px solid #dc2626 !important;
    background: #fef2f2 !important;
    animation: kb-card-error-flash 0.5s ease-in-out;
}
@keyframes kb-card-error-flash {
    0% { background: #fee2e2; }
    100% { background: #fef2f2; }
}
.kb-snooze-btn {
    font-size: 11px; cursor: pointer; padding: 1px 6px;
    border: 1px solid var(--border-light); border-radius: 3px;
    background: #f9fafb; color: var(--text-muted);
    font-style: italic;
}
.kb-snooze-btn:hover { background: #eef2ff; border-color: #6366f1; color: #6366f1; }
.kb-snoozed-label { font-style: italic; }
.kb-snooze-menu {
    background: #fff; border: 1px solid var(--border-light);
    border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12);
    min-width: 100px; padding: 4px 0;
}
.kb-snooze-option {
    padding: 6px 12px; font-size: 13px; cursor: pointer;
}
.kb-snooze-option:hover { background: #f0f4ff; }

/* Denser list view — single grid row per todo. Columns:
   select | done | project | pub-toggle | text (flex) | priority | status | due | snooze-btn | snoozed-label | assigned | created | delete */
.todos-list-dense .todo-row-dense {
    display: grid;
    grid-template-columns:
        18px         /* select checkbox */
        18px         /* done checkbox */
        140px        /* project */
        20px         /* public/private toggle (project todos only) */
        minmax(120px, 1fr)  /* text */
        60px         /* priority badge */
        110px        /* status */
        110px        /* due date */
        40px         /* zzz */
        60px         /* snoozed label */
        120px        /* assigned */
        50px         /* created date */
        28px;        /* delete */
    column-gap: 6px;
    align-items: center;
    padding: 3px 8px;
    border-bottom: 1px solid var(--border-light, #e5e7eb);
    font-size: 12px;
    line-height: 1.3;
}
.todos-list-dense .todo-row-dense:hover { background: #f9fafb; }
.todos-list-dense .todo-row-project {
    font-weight: 600;
    font-size: 11px;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.todos-list-dense .todo-row-text {
    border: 1px solid transparent;
    background: transparent;
    padding: 2px 4px;
    width: 100%;
    font-size: 13px;
}
.todos-list-dense .todo-row-text:hover,
.todos-list-dense .todo-row-text:focus {
    border-color: var(--border-light);
    background: #fff;
    outline: none;
}
.todos-list-dense .todo-row-text.completed {
    text-decoration: line-through;
    color: #9ca3af;
}
.todos-list-dense .todo-row-status,
.todos-list-dense .todo-row-due,
.todos-list-dense .todo-row-assigned {
    font-size: 11px;
    padding: 1px 4px;
    width: 100%;
}
.todos-list-dense .todo-row-delete {
    padding: 0 6px;
    font-size: 14px;
    line-height: 1;
}
/* Snoozed → dim and sort-to-bottom is handled in JS; CSS does the dim. */
.todos-list-dense .todo-row-snoozed { opacity: 0.5; }
.todos-list-dense .todo-row-snoozed:hover { opacity: 0.85; }

/* Reports: schedule summary + flex grid of day blocks. */
.rpt-sched-summary {
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px dashed #e5e7eb;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.rpt-sched-summary-row { font-size: 13px; line-height: 1.4; }
.rpt-sched-dot {
    display: inline-block;
    width: 10px; height: 10px;
    border-radius: 50%;
    margin-right: 4px;
    vertical-align: middle;
}
.rpt-sched-range { color: #1f2937; font-variant-numeric: tabular-nums; }

/* Day blocks — flex wrap with a large row-gap so wrapped rows are clearly
 * separated; horizontal gap is modest so blocks pack tightly across. */
.rpt-sched-grid {
    display: flex;
    flex-wrap: wrap;
    column-gap: 8px;
    row-gap: 36px;
    align-items: stretch;
}

/* Schedule Spreadsheet section — one fixed-layout table per day with
 * identical column widths, so the columns line up day-to-day for
 * readability and printing. Day boundary is shown with a 2px black
 * border around the whole day's table — easier on the eyes (and
 * printer ink) than a dark header bar. */
.rpt-sched-sheet-day {
    margin-bottom: 18px;
    page-break-inside: avoid;
    border: 2px solid #000;
}
.rpt-sched-sheet-day-head {
    font-weight: 600;
    font-size: 13px;
    margin-bottom: 4px;
}
.rpt-sched-sheet-table {
    border-collapse: collapse;
    table-layout: fixed;
    width: 100%;
    font-size: 11px;
}
.rpt-sched-sheet-table col.rpt-ss-w-date    { width: 5%; }
.rpt-sched-sheet-table col.rpt-ss-w-start   { width: 6%; }
.rpt-sched-sheet-table col.rpt-ss-w-dur     { width: 6%; }
.rpt-sched-sheet-table col.rpt-ss-w-end     { width: 6%; }
.rpt-sched-sheet-table col.rpt-ss-w-name    { width: 11%; }
.rpt-sched-sheet-table col.rpt-ss-w-desc    { width: 13%; }
.rpt-sched-sheet-table col.rpt-ss-w-team    { width: 12%; }
.rpt-sched-sheet-table col.rpt-ss-w-loc     { width: 7%; }
.rpt-sched-sheet-table col.rpt-ss-w-loc-min { width: 5%; }
.rpt-sched-sheet-table col.rpt-ss-w-vendor  { width: 6%; }
.rpt-sched-sheet-table col.rpt-ss-w-contact { width: 6%; }
.rpt-sched-sheet-table col.rpt-ss-w-heq     { width: 5%; }
.rpt-sched-sheet-table col.rpt-ss-w-leads   { width: 5%; }
.rpt-sched-sheet-table td.rpt-ss-cell-leads { text-align: center; }
.rpt-sched-sheet-table col.rpt-ss-w-sh      { width: 6%; }
.rpt-sched-sheet-table th,
.rpt-sched-sheet-table td {
    /* Tight padding — vertical compressed to 1px so each row barely
     * spends any wasted space, horizontal kept to 4px so text isn't
     * jammed against the cell edge. line-height 1.3 keeps multi-line
     * cells legible without ballooning the row. */
    padding: 1px 4px;
    line-height: 1.3;
    border: 1px solid #d1d5db;
    text-align: left;
    vertical-align: top;
    /* Wrap long content instead of pushing the column wider. With
     * table-layout: fixed columns can't grow, so word-wrap is the
     * only way to handle a 30-char description. */
    overflow-wrap: break-word;
    word-break: break-word;
}
.rpt-sched-sheet-table thead th {
    background: #f3f4f6;
    font-weight: 600;
}
/* Excel-style stacked rows at the top of each day:
 *  1. banner — merged date + date_type + description
 *  2. goals — merged EOD goals
 *  3. column headers (inline as a <tr> in tbody)
 */
.rpt-sched-sheet-table .rpt-ss-row-banner td {
    /* Plain banner — no dark fill. The 2px black outline on the
     * .rpt-sched-sheet-day wrapper marks the day boundary instead, so
     * print output isn't dominated by ink-heavy headers. */
    background: transparent;
    color: #000;
    font-weight: 700;
    font-size: 12px;
    padding: 2px 6px;
    border-bottom: 1px solid #000;
}
.rpt-sched-sheet-table .rpt-ss-row-goals td {
    background: #fef3c7;
    font-size: 11px;
    padding: 1px 6px;
}
.rpt-sched-sheet-table .rpt-ss-goals-label {
    font-weight: 600;
    text-transform: uppercase;
    font-size: 10px;
    letter-spacing: 0.04em;
    color: #92400e;
    margin-right: 6px;
}
.rpt-sched-sheet-table .rpt-ss-row-summary td {
    background: #dbeafe;
    font-weight: 600;
    font-size: 10px;
    padding: 1px 6px;
    color: #1e40af;
}
.rpt-sched-sheet-table .rpt-ss-row-headers th {
    background: #f3f4f6;
    font-weight: 600;
}
.rpt-sched-sheet-table .rpt-ss-row-meal,
.rpt-sched-sheet-table .rpt-ss-row-meal td {
    background: #e5e7eb;
    color: #6b7280;
    font-weight: 600;
}
/* Overnight marker for report tables — red + bold. Applied to the date
   cell (after a "+" suffix is appended) when a row's start is past
   midnight, and to the end cell when the row's end runs past midnight. */
.rpt-cell-overnight {
    color: #c81e1e;
    font-weight: 700;
}

/* Trucking rows tint a soft green — slight enough to catch the eye
   without overpowering the row text. */
.rpt-sched-sheet-table .rpt-ss-row-trucking,
.rpt-sched-sheet-table .rpt-ss-row-trucking td {
    background: #ecfdf5;
    font-weight: 600;
}
/* Trucking line items in the simple Schedule day-block list. */
.rpt-day-tasks li.rpt-day-task-trucking {
    background: #ecfdf5;
    font-weight: 600;
    padding: 1px 4px;
    border-radius: 2px;
}
.rpt-sched-sheet-table .rpt-ss-row-note,
.rpt-sched-sheet-table .rpt-ss-row-note td {
    background: #fffbeb;
}
.rpt-sched-sheet-table .rpt-ss-note-label {
    font-style: italic;
    color: #6b7280;
}
.rpt-day-block {
    flex: 0 0 160px;
    width: 160px;
    border: 1px solid #e5e7eb;
    border-left: 4px solid #6b7280;
    border-radius: 4px;
    padding: 6px 8px;
    background: #fff;
    font-size: 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.rpt-day-head { line-height: 1.3; }
.rpt-day-date { font-weight: 700; font-size: 14px; font-variant-numeric: tabular-nums; }
.rpt-day-dow { font-size: 11px; margin-left: 2px; }
.rpt-day-type { font-weight: 600; font-size: 12px; color: #374151; margin-top: 1px; }
.rpt-day-desc { margin-top: 1px; line-height: 1.2; }
.rpt-day-times {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    border-top: 1px solid #f3f4f6;
    border-bottom: 1px solid #e5e7eb;
    padding-top: 3px;
    padding-bottom: 4px;
    margin-bottom: 4px;
}
.rpt-day-tasks {
    margin: 0;
    padding-left: 0;
    list-style: none;
    font-size: 11px;
    line-height: 1.35;
}
.rpt-day-tasks li { padding: 1px 0; }

/* Import: sticky action bar at the top of step 2. */
.import-action-bar {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    margin: 0 -12px 12px;
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}
.import-action-bar.is-busy {
    background: #fef3c7;
    border-color: #fcd34d;
}
.import-action-bar.is-busy #executeBtn {
    cursor: wait;
}

/* Import: Finesse modal — per-day decisions + diff highlights. */
.finesse-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}
.finesse-table th, .finesse-table td {
    padding: 6px 8px;
    border-bottom: 1px solid #e5e7eb;
    vertical-align: top;
    text-align: left;
}
.finesse-table th {
    font-size: 11px;
    color: #6b7280;
    font-weight: 600;
    position: sticky;
    top: 0;
    background: #fff;
    z-index: 1;
    border-bottom: 1px solid #d1d5db;
}
.finesse-date { white-space: nowrap; min-width: 80px; font-variant-numeric: tabular-nums; }
.finesse-type { white-space: nowrap; }
.finesse-crew { min-width: 180px; }
.finesse-crew-item { line-height: 1.35; }

.finesse-radios { display: flex; flex-direction: column; gap: 2px; }
.finesse-radio { font-size: 11px; white-space: nowrap; }

/* Row tint per change type — muted so the table stays readable. */
.finesse-row.finesse-both    { background: rgba(168,  85, 247, 0.08); }  /* purple */
.finesse-row.finesse-day     { background: rgba(234, 179,   8, 0.10); }  /* amber */
.finesse-row.finesse-crew    { background: rgba( 59, 130, 246, 0.08); }  /* blue */
.finesse-row.finesse-added   { background: rgba( 34, 197,  94, 0.10); }  /* green */
.finesse-row.finesse-removed { background: rgba(239,  68,  68, 0.10); }  /* red */

/* Tiny swatches in the legend. Named .finesse-sw-* so they don't collide
 * with the cell-width classes like .finesse-crew (min-width:180px) that
 * live elsewhere — the legend chips were getting stretched to 180px. */
.finesse-sw {
    display: inline-block !important;
    width: 10px !important;
    height: 10px !important;
    min-width: 0 !important;
    border-radius: 2px;
    margin-right: 2px;
    vertical-align: middle;
}
.finesse-sw.finesse-sw-both    { background: rgba(168,  85, 247, 0.5); }
.finesse-sw.finesse-sw-day     { background: rgba(234, 179,   8, 0.6); }
.finesse-sw.finesse-sw-crew    { background: rgba( 59, 130, 246, 0.5); }
.finesse-sw.finesse-sw-added   { background: rgba( 34, 197,  94, 0.6); }
.finesse-sw.finesse-sw-removed { background: rgba(239,  68,  68, 0.6); }

/* Incoming type edit input inside the finesse modal. */
.finesse-incoming-edit {
    font: inherit;
    width: 14ch;
    padding: 2px 4px;
    border: 1px solid #d1d5db;
    border-radius: 3px;
    background: #fff;
}
.finesse-incoming-edit:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 2px rgba(59,130,246,.15);
}

/* Import: change-summary tags on the preview row. */
.import-change-tag {
    display: inline-block;
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 10px;
    border: 1px solid transparent;
    margin-left: 6px;
    letter-spacing: 0.3px;
    font-weight: 600;
    white-space: nowrap;
}
.import-change-tag.import-change-none {
    background: #e0f2fe; color: #075985; border-color: #bae6fd;
}
.import-change-tag.import-change-some {
    background: #fef3c7; color: #92400e; border-color: #fde68a;
}
/* Hash-matched against the previous import (ImportedDay) — distinct from
 * NO CHANGE, which compares against the schedule. Surfaced separately
 * because the schedule may legitimately differ from the import (manual
 * edits) while the import itself hasn't moved. */
.import-change-tag.import-change-imported {
    background: #ede9fe; color: #5b21b6; border-color: #ddd6fe;
}
.import-change-tag.import-change-mixed {
    background: #f1f5f9; color: #334155; border-color: #cbd5e1;
}

/* Daily-update row whose Finesse decisions have been saved — the per-row
 * Schedule / Crew Names / Crew Assignments checkboxes are server-bypassed,
 * so we mute them here to match. The "(N days customised)" badge spells
 * out what's happening for the user. */
.import-row-finesse-locked input.import-cat {
    opacity: 0.5;
    cursor: not-allowed;
}
.import-row-finesse-locked label.cal-check {
    text-decoration: line-through;
    opacity: 0.7;
}

/* Finesse modal: inline "NO CHANGE" pill on unchanged day rows. */
.finesse-nochange-tag {
    display: inline-block;
    font-size: 9px;
    padding: 0 5px;
    border-radius: 8px;
    background: #e5e7eb;
    color: #6b7280;
    letter-spacing: 0.3px;
    font-weight: 600;
    vertical-align: middle;
    margin-left: 4px;
}

/* Hitlist */
.hitlist-table {
    width: auto;
    border-collapse: collapse;
    font-size: 13px;
    margin-top: 8px;
}
.hitlist-table th,
.hitlist-table td {
    border: 1px solid var(--border-light, #e5e7eb);
    padding: 4px 10px;
    text-align: left;
    vertical-align: middle;
    white-space: nowrap;
}
.hitlist-table th {
    background: #f9fafb;
    font-weight: 600;
}
.hitlist-table tr.hitlist-row-done td { color: #9ca3af; }
.hitlist-table tr.hitlist-row-done td:nth-child(2) { text-decoration: line-through; }

.hitlist-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 11px; cursor: pointer;
    font-size: 13px; border: 2px solid #d1d5db; color: #9ca3af;
    transition: all .15s;
}
.hitlist-toggle:hover { border-color: #6366f1; color: #6366f1; }
.hitlist-toggle.hitlist-complete {
    background: #6366f1; border-color: #6366f1; color: #fff;
}

/* Fixture row highlight on focus */
/* Focused fixture row — distinct background plus a left accent bar and a
   subtle outline so it stays obvious even when scanning a long sheet. */
.fixture-row:focus-within {
    background: #dbeafe;
    box-shadow: inset 4px 0 0 #2563eb, 0 0 0 1px #93c5fd;
}
.fixture-row:focus-within td { border-color: #93c5fd; }

/* Quick-wins batch: date countdown, RW deal number, status "last changed" */
.dash-date-label-group {
    display: flex;
    align-items: center;
    gap: 3px;
    min-width: 0;
    flex-shrink: 0;
}

.dash-date-countdown {
    font-size: 10px;
    color: var(--text-muted, #9ca3af);
    white-space: nowrap;
    flex-shrink: 0;
}

/* Countdown urgency color-coding (reuses the req-dot red/orange/green palette):
   past = today or already passed, soon = within the next 7 days, later = beyond. */
.dash-date-countdown.dash-countdown--past { color: var(--danger); font-weight: 600; }
.dash-date-countdown.dash-countdown--soon { color: var(--orange); font-weight: 600; }
.dash-date-countdown.dash-countdown--later { color: var(--success); }

.dash-rw-deal {
    font-size: 11px;
    margin-left: 6px;
    white-space: nowrap;
}

a.dash-rw-deal {
    text-decoration: none;
}

a.dash-rw-deal:hover {
    text-decoration: underline;
}

/* Order/quote kind chip — soft green for orders, soft yellow for quotes. */
.rw-kind-chip {
    display: inline-flex;
    align-items: center;
    padding: 1px 7px;
    border-radius: 10px;
    font-weight: 600;
    text-decoration: none;
}

.rw-kind-chip:hover {
    text-decoration: underline;
}

.rw-kind-order {
    background: #d1fae5;
    color: #065f46;
}

.rw-kind-quote {
    background: #fef3c7;
    color: #92400e;
}

.status-ago {
    font-size: 10px;
    font-weight: 400;
    color: var(--text-muted, #9ca3af);
    margin-left: 4px;
}

.detail-field-header {
    display: flex;
    align-items: baseline;
    gap: 6px;
}

/* Project detail subnav split: internal TOC anchors (left) vs external
   page jumps (right, arrow-marked). Wave-2 menu bar cleanup. */
.detail-subnav-toc {
    display: flex;
    gap: 4px;
    align-items: center;
    flex: 0 1 auto;
    min-width: 0;
    overflow-x: auto;
    white-space: nowrap;
}

.detail-subnav-external {
    display: flex;
    gap: 4px;
    align-items: center;
    flex-shrink: 0;
    margin-left: 12px;
    padding-left: 12px;
    border-left: 1px solid var(--border);
    white-space: nowrap;
}

.detail-subnav-ext-link {
    padding: 4px 8px;
    color: var(--text-muted);
    text-decoration: none;
    border-radius: 3px;
}

.detail-subnav-ext-link:hover {
    background: var(--bg-hover);
    color: var(--text);
}

.detail-subnav-ext-arrow {
    margin-left: 3px;
    opacity: 0.6;
    font-size: 11px;
}

/* ==========================================================================
   Project detail: subnav as a FIXED LEFT COLUMN (scroll-spy TOC).
   Wide screens only — below 1025px it falls back to the default horizontal
   sticky bar defined above (mobile/narrow fallback). Everything is scoped to
   body[data-url-name="project_detail"] so no other page's .main-content or
   .detail-subnav is affected.
   ========================================================================== */
@media (min-width: 1025px) {
    body[data-url-name="project_detail"] {
        --detail-toc-width: 190px;
    }

    /* The nav bar leaves normal flow and pins down the left edge, below the
       sticky top-nav. Fixed (not sticky) so it stays put while the page
       scrolls and can scroll internally if the list exceeds the viewport. */
    body[data-url-name="project_detail"] .detail-subnav {
        position: fixed;
        top: var(--chrome-h);
        left: 0;
        width: var(--detail-toc-width);
        height: calc(100vh - var(--chrome-h));
        flex-direction: column;
        align-items: stretch;
        gap: 1px;
        padding: 10px 8px;
        overflow-y: auto;
        overflow-x: hidden;
        border-right: 1px solid var(--border-light);
        border-bottom: none;
        z-index: 90;
    }

    /* External page jumps move to the TOP of the column (order:-1), separated
       from the internal section links by a rule. */
    body[data-url-name="project_detail"] .detail-subnav-external {
        order: -1;
        flex-direction: column;
        align-items: stretch;
        gap: 1px;
        margin: 0 0 6px;
        padding: 0 0 6px;
        border-left: none;
        border-bottom: 1px solid var(--border);
        white-space: normal;
    }

    body[data-url-name="project_detail"] .detail-subnav-toc {
        order: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 1px;
        overflow: visible;
        white-space: normal;
    }

    body[data-url-name="project_detail"] .detail-subnav a,
    body[data-url-name="project_detail"] .detail-subnav-ext-link {
        display: block;
        padding: 4px 8px;
        border-radius: 4px;
        line-height: 1.3;
    }

    /* Scroll-spy active section: bold + subtle highlight (not the solid blue
       pill the horizontal bar uses). */
    body[data-url-name="project_detail"] .detail-subnav-toc a.active {
        background: var(--bg-hover);
        color: var(--text);
        font-weight: 700;
    }

    /* Shift the detail page's content right to clear the fixed column. */
    body[data-url-name="project_detail"] .main-content {
        padding-left: calc(var(--detail-toc-width) + 20px);
    }

    /* No horizontal subnav sits above the content now, so section anchors
       only need to clear the top nav (not the old header+subnav stack). */
    body[data-url-name="project_detail"] .detail-section[id^="section-"] {
        scroll-margin-top: calc(var(--header-height) + 10px);
    }
}

/* ---- Crew Schedule: Hands (manual-vs-rollup) summary — Wave 2 ---- */
.sheet-hands-summary {
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 0;
}
.sheet-hands-total {
    font-weight: 700;
    min-width: 14px;
}
.sheet-hands-manual-input {
    width: 52px;
    font-size: 11px;
    padding: 2px 4px;
    border: 1px solid var(--border-light, #e5e7eb);
    border-radius: 3px;
}
.sheet-hands-breakdown {
    padding: 2px 0 0;
}
.sheet-hands-mismatch {
    padding: 1px 0 0;
    font-style: italic;
}

/* ---- Crew Schedule: find-crew modal previous/next gig — Wave 2 ---- */
.find-crew-gig-row {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 2px 0;
}
.find-crew-gig-dates {
    font-size: 11px;
    color: var(--text-muted);
}
.find-crew-gigs-empty {
    font-size: 12px;
    color: var(--text-muted);
    padding: 2px 0 6px;
}

/* ---- Dashboard: Milestones expand panel — Wave 2 ---- */
.dash-ms-row-overdue > td {
    background: #fee2e2;
}
.dash-ms-row-alerting > td {
    background: #fff7ed;
}
.dash-ms-row-complete > td {
    opacity: 0.6;
}

/* ---- RW status chip (read-only, next to name/order-type) ---- */
.rw-status-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    background: var(--bg-alt);
    color: var(--text-muted);
    border: 1px solid var(--border-light);
    white-space: nowrap;
}

/* ---- Dashboard: requirements block header ---- */
.dash-req-header {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    color: var(--text-muted);
    margin-bottom: 2px;
}

/* ---- Project detail: requirements quick-add (ported from dashboard) ---- */
.detail-req-quickadd {
    display: flex;
    gap: 4px;
    align-items: center;
    margin-left: auto;
}
select.detail-quickadd-select {
    font-size: 12px;
    padding: 2px 6px;
    max-width: 160px;
}

/* ==========================================================================
   Daily-journal Notes page
   ========================================================================== */

.journal-page {
    /* Fill the width .main-content actually has left over between the
       journal's own date margin (left) and the post-it sidebar (right) —
       there's no reason to cap it mid-page like a prose column. */
    max-width: 100%;
}

.journal-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 8px;
}
.journal-head h1 {
    font-size: 20px;
    font-weight: 700;
    margin: 0;
}
.journal-status {
    font-size: 12px;
    color: var(--text-light);
    opacity: 0;
    transition: opacity 0.25s;
}
.journal-status.show { opacity: 1; }
.journal-status.is-error { color: #dc2626; }

.journal-loading,
.journal-end {
    text-align: center;
    color: var(--text-light);
    font-size: 12px;
    padding: 20px 0;
}

/* One day = ruled page block. Grid: [margin date] [body].
   Auto-flow lays the date + journal on row 1, then each project note as a
   check(col1)+note(col2) row beneath. A faint vertical rule = notepad margin. */
.journal-day {
    display: grid;
    grid-template-columns: 66px 1fr;
    column-gap: 16px;
    align-items: start;
    padding: 16px 0 20px;
    border-top: 1px solid var(--border);
    background:
        linear-gradient(to right,
            transparent 0, transparent 74px,
            rgba(220, 38, 38, 0.18) 74px, rgba(220, 38, 38, 0.18) 75px,
            transparent 75px);
}
.journal-day:first-child { border-top: none; }

.jd-date {
    text-align: right;
    padding-right: 10px;
    position: sticky;
    top: calc(var(--header-height) + 8px);
    line-height: 1.05;
    user-select: none;
}
.jd-date-dow {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-light);
}
.jd-date-day {
    font-size: 26px;
    font-weight: 700;
    color: var(--text);
}
.jd-date-mon {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
}

/* Free journal area — borderless, ruled, endless-notepad feel. */
.jd-journal { min-width: 0; }
.jd-journal-note,
.jd-note-text {
    display: block;
    width: 100%;
    border: none;
    background: transparent;
    resize: none;
    overflow: hidden;
    font-family: inherit;
    font-size: 14px;
    line-height: 26px;
    color: var(--text);
    padding: 0 4px;
    outline: none;
    white-space: pre-wrap;
    background-image: linear-gradient(to bottom,
        transparent 0, transparent 25px,
        var(--border-light) 25px, var(--border-light) 26px);
    background-size: 100% 26px;
    min-height: 26px;
}
.jd-journal-note:focus,
.jd-note-text:focus {
    background-color: rgba(59, 130, 246, 0.04);
}
.jd-new::placeholder { color: var(--text-light); font-style: italic; }

/* Project / other notes attached to a day. */
.jd-note {
    min-width: 0;
    padding: 2px 0 4px;
}
.jd-note-tag {
    display: block;
    font-size: 11px;
    font-weight: 600;
    text-decoration: none;
    margin-bottom: 1px;
}
.jd-note-tag:hover { text-decoration: underline; }
.jd-note-time {
    display: block;
    font-size: 10px;
    color: var(--text-light);
    padding-left: 4px;
}
.jd-note.note-handled .jd-note-text { color: var(--text-light); text-decoration: line-through; }

/* Specs interleaved into the day stream (same row shape as a project note, so
   the two read as one chronological record — see jProjectSpec in
   notes_list.js), tagged with a (SPEC) pill so they're distinguishable from
   prose at a glance. Both fields edit in place. */
.jd-spec-body {
    display: flex;
    align-items: center;
    /* Room for the (transparent-until-focus) field borders without the fields
       touching each other or the pill. */
    gap: 4px;
    padding: 0 4px;
    font-size: 14px;
    line-height: 26px;
    background-image: linear-gradient(to bottom,
        transparent 0, transparent 25px,
        var(--border-light) 25px, var(--border-light) 26px);
    background-size: 100% 26px;
    min-height: 26px;
}

.jd-spec-pill {
    flex-shrink: 0;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.06em;
    line-height: 1;
    padding: 3px 6px;
    border-radius: 9px;
    background: var(--border-light);
    color: var(--text-muted);
    text-transform: uppercase;
    user-select: none;
}

/* Borderless at rest so the row still reads as a record rather than a form.
   The border is always present but TRANSPARENT — colouring it on focus instead
   of adding it means the text never shifts by a pixel when you click in. */
.jd-spec-field {
    font-family: inherit;
    font-size: 14px;
    line-height: 20px;
    color: var(--text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 3px;
    padding: 0 4px;
    min-width: 0;
    outline: none;
}
.jd-spec-field:hover { border-color: var(--border-light); }
.jd-spec-field:focus {
    border-color: var(--primary);
    background: var(--bg);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
}
.jd-spec-field::placeholder { color: var(--text-light); font-style: italic; }
/* Flash on a rejected save (e.g. no project_specs edit permission). */
.jd-spec-field-error { border-color: #dc2626 !important; }

/* The label is the shorter of the two, so it sizes to a sensible share and the
   value takes the rest. Deliberately NOT bold: bold reflows the moment the
   field takes focus, which is why the colon is its own element below. */
.jd-spec-label {
    flex: 0 1 30%;
    min-width: 5em;
}
.jd-spec-colon {
    flex-shrink: 0;
    color: var(--text-muted);
    user-select: none;
}
.jd-spec-value { flex: 1 1 auto; }

/* Margin placeholder that keeps a spec row aligned to the notes grid without
   offering a handled toggle it doesn't have. */
.jd-check-none {
    justify-self: end;
    width: 20px;
    height: 20px;
    margin-top: 4px;
}

/* Stylized handled check in the margin (circled tick — not a native box). */
.jd-check {
    justify-self: end;
    width: 20px;
    height: 20px;
    margin-top: 4px;
    border: 2px solid var(--border);
    border-radius: 50%;
    cursor: pointer;
    box-sizing: border-box;
    transition: background 0.15s, border-color 0.15s;
    position: relative;
}
.jd-check:hover { border-color: var(--primary); }
.jd-check.checked {
    background: #16a34a;
    border-color: #16a34a;
}
.jd-check.checked::after {
    content: '';
    position: absolute;
    left: 5px;
    top: 1px;
    width: 5px;
    height: 10px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

/* Right sidebar: yellow post-its. */
.journal-sidebar h3 { margin-top: 0; }
.postit {
    background: #fffde7;
    border: 1px solid #fff176;
    border-radius: 8px;
    padding: 10px;
    margin-bottom: 12px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.postit-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: #b7791f;
    margin-bottom: 4px;
    display: flex;
    align-items: center;
    gap: 4px;
}
.postit-name {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.postit-controls {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 1px;
    opacity: 0.45;
    transition: opacity 0.15s;
}
.postit:hover .postit-controls { opacity: 1; }
.postit-btn {
    border: none;
    background: transparent;
    cursor: pointer;
    padding: 1px 3px;
    font-size: 11px;
    line-height: 1;
    color: #b7791f;
    border-radius: 3px;
}
.postit-btn:hover { background: rgba(0, 0, 0, 0.06); }
.postit-pinned { border-color: #f6b73c; background: #fff8d6; }
.postit-text {
    width: 100%;
    border: none;
    background: transparent;
    resize: none;
    overflow: hidden;
    font-family: inherit;
    font-size: 13px;
    line-height: 1.4;
    color: #4b3f1a;
    outline: none;
    min-height: 40px;
    white-space: pre-wrap;
}

/* Used by several admin-tab toolbars (e.g. "flex gap-2 mb-4 items-end")
   that reference this utility class without it having been defined. */
.items-end { align-items: flex-end; }

/* ── Holidays (calendar tint + step-on warnings) ──────────────────────────
   Appended by the Holiday feature. Spreadsheet holiday columns get a green
   tint (header + day cells) via the ss-holiday class baked into each day's
   class list — green so it can't blur into the orange "today" column; a
   scheduled day landing on a holiday gets a corner marker via
   ss-holiday-hit. Standard month grid gets a small holiday label. Dashboard
   date groups get a ⚠ badge; the date-range editor toasts on save. */
.spreadsheet-calendar .ss-holiday {
    background-color: #e3f5e9;
}
.spreadsheet-calendar .ss-head-cell.ss-holiday,
.spreadsheet-calendar div.ss-holiday.ss-head-cell {
    color: #15803d;
}
.ss-holiday-hit {
    position: relative;
}
.ss-holiday-hit::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 0 0 7px 7px;
    border-color: transparent transparent #15803d transparent;
    pointer-events: none;
}

.calendar-cell-holiday {
    background-color: #e3f5e9 !important;
}
.cal-holiday-label {
    font-size: 10px;
    font-weight: 600;
    color: #15803d;
    background: #c9ecd4;
    border-radius: 3px;
    padding: 0 4px;
    margin-bottom: 3px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dash-holiday-badge {
    color: #b45309;
    font-size: 11px;
    margin-left: 2px;
    cursor: help;
}

.dash-holiday-toast {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%) translateY(12px);
    background: #7c2d12;
    color: #fff;
    padding: 10px 16px;
    border-radius: 6px;
    font-size: 13px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
    z-index: 10000;
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    cursor: pointer;
    max-width: 90vw;
}
.dash-holiday-toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

/* ---- Crew Blackout (PTO) — phase 2: availability search + warnings ---- */

/* Crew schedule grid: roster row whose crew member is on PTO that day. */
.crew-roster-row.crew-roster-blackout {
    background: #fee2e2;
    border-left: 3px solid #dc2626;
    padding-left: 4px;
    border-radius: 2px;
}

/* Find-crew modal: "PTO 6/15-6/20" badge in the results list + a dedicated
   sort/dim tier (6) below the existing busy tiers (1-5). */
.find-crew-pto-badge {
    font-size: 10px;
    font-weight: 700;
    color: #991b1b;
    background: #fee2e2;
    border: 1px solid #fecaca;
    padding: 1px 6px;
    border-radius: 3px;
    white-space: nowrap;
}
.find-crew-avail.tier-6 { background: #fecdd3; color: #9f1239; }

/* Admin Blackouts tab: "booked project" collision warnings shown under a
   newly added/edited blackout row (crew_blackouts.js). */
.blackout-warning-list {
    margin: 2px 0 6px;
}
.blackout-warning-row {
    font-size: 11px;
    color: #b45309;
    padding: 1px 0;
}

/* Optimistic timeline task creation (crew_schedule.js _insertPendingTimelineTask).
   A task the user just added/is naming but hasn't been confirmed by the
   server yet — feedback-pending (loaded earlier) supplies the yellow
   tint; this just marks it as not-yet-interactive (no drag handles are
   ever rendered on it, so there's nothing to grab either way) and keeps
   the name-edit popup, which IS allowed while pending, readable on top. */
.crew-pending-task {
    cursor: default;
}
.crew-pending-task .crew-task-name-popup {
    cursor: text;
}

/* ============================================================
   Linked-projects combined view (crew schedule).
   The toggle bar merges a linked project's schedule into the
   page for READ-ONLY context; foreign day blocks are color-coded
   by the linked project's own Project.color and kept structurally
   separate from the main project's editable data.
   ============================================================ */
.crew-linked-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    margin-bottom: 10px;
    background: var(--bg-alt);
    border: 1px solid var(--border-light);
    border-radius: 6px;
}
.crew-linked-bar-label {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.crew-linked-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    padding: 2px 8px 2px 4px;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    cursor: pointer;
    user-select: none;
    background: #fff;
}
.crew-linked-chip:hover { background: var(--bg-hover); }
.crew-linked-swatch {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    flex: 0 0 auto;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15);
}
.crew-linked-chip-name {
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.crew-linked-all-chip { font-weight: 600; }
.crew-linked-hint { margin-left: auto; }

/* Per-column project identifier (multi-project view only). Subtle/small,
   accented with the owning project's color. Disabled this chunk — Chunk 3
   may wire it for reorder. Hidden entirely in single-project view (the
   template omits it when only one project is in the view). */
.crew-col-project {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin: 0 0 2px;
    padding: 1px 4px;
    font-size: 10px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--col-project-color, #4b5563);
    background: color-mix(in srgb, var(--col-project-color, #4b5563) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--col-project-color, #4b5563) 40%, transparent);
    border-left: 3px solid var(--col-project-color, #4b5563);
    border-radius: 3px;
    text-overflow: ellipsis;
    /* Disabled selects otherwise render greyed-out; keep it legible. */
    opacity: 1;
    cursor: default;
    -webkit-appearance: none;
    appearance: none;
}

/* ==========================================================================
   Role reconcile table (project detail #section-role-reconcile) — one row per
   (day, role); columns RW Labor / Lasso / Manual; click a source to promote.
   ========================================================================== */
/* Light-only, matching the rest of the detail page (the lc- labor tables are
   pastel-on-dark-text; no dark-mode overrides — those were what made the table
   read as odd dark green in a dark-OS browser). */
.rr-note { font-size: 12px; color: #64748b; font-weight: 600; }
.rr-sortopt { font-size: 12px; color: #334155; display: inline-flex; align-items: center; gap: 3px; }
.rr-empty { padding: 14px; color: #64748b; font-size: 13px; }
.rr-empty.rr-err { color: #b91c1c; }
.rr-block { margin-bottom: 16px; }
/* Readable role header: dark text on a light pastel bar with an accent edge. */
.rr-block-title {
    font-size: 13px; font-weight: 700; color: #0f172a;
    background: #eef2f7; border-left: 4px solid #94a3b8; border-radius: 3px;
    padding: 5px 10px; margin-bottom: 4px;
}
.rr-block-n {
    display: inline-block; min-width: 16px; text-align: center;
    font-size: 11px; font-weight: 700; color: #475569;
    background: #fff; border-radius: 8px; padding: 0 6px; margin-left: 6px;
}
.rr-table { border-collapse: collapse; width: 100%; max-width: 900px; font-size: 13px; }
/* Grid lines all around (owner). */
.rr-table th, .rr-table td { border: 1px solid #e2e8f0; }
.rr-table th {
    text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .03em;
    color: #64748b; font-weight: 600; padding: 4px 8px; background: #f8fafc;
}
.rr-table td { padding: 5px 8px; vertical-align: middle; }
/* Day cell all on ONE line: "9/2 · Tour (4 of 33) [clear]". */
.rr-day { white-space: nowrap; }
.rr-date { font-weight: 600; color: #0f172a; }
.rr-sep { color: #cbd5e1; margin: 0 5px; }
.rr-daytype { font-size: 12px; color: #64748b; }
.rr-ord { color: #94a3b8; }
/* Independently clickable role / crew within a source cell. */
.rr-click { cursor: pointer; border-radius: 3px; padding: 0 2px; }
.rr-click:hover { background: #dbeafe; box-shadow: inset 0 0 0 1px #93c5fd; }
/* DATE RANGE / HOURS mismatch badge in an RW Labor cell. */
.rr-hwarn {
    display: inline-block; font-size: 9px; font-weight: 700; letter-spacing: .02em;
    color: #991b1b; background: #fee2e2; border: 1px solid #fca5a5;
    border-radius: 3px; padding: 0 4px; margin-left: 4px; cursor: help;
    white-space: nowrap; vertical-align: middle;
}
/* Per-row slot label so you always know which block you're in. */
.rr-slot {
    display: inline-block; font-size: 10px; font-weight: 700; color: #475569;
    background: #eef2f7; border-radius: 3px; padding: 0 5px; margin-right: 7px;
}
/* Current (live schedule) column — the reference the sources compare against. */
.rr-current-h { background: #f1f5f9 !important; }
.rr-cell.rr-current { background: #f8fafc; font-weight: 600; }
@keyframes rr-flash-kf { 0% { background: #fde68a; } 100% { background: #f8fafc; } }
.rr-cell.rr-current.rr-flash { animation: rr-flash-kf 0.7s ease-out; }
.rr-refresh { padding: 2px 8px; }
/* Soft verdict tint on the day cell's left edge (pastel, not loud). */
.rr-day.rr-v-create { box-shadow: inset 3px 0 0 0 #86efac; }
.rr-day.rr-v-edit   { box-shadow: inset 3px 0 0 0 #fcd34d; }
.rr-day.rr-v-delete { box-shadow: inset 3px 0 0 0 #fca5a5; }
.rr-cell-empty { color: #cbd5e1; }
.rr-role { font-weight: 600; color: #1e293b; }
.rr-crew { color: #475569; }
.rr-cell.rr-promotable { cursor: pointer; border-radius: 4px; }
.rr-cell.rr-promotable:hover { background: #eff6ff; box-shadow: inset 0 0 0 1px #bfdbfe; }
.rr-cell.rr-onsched { background: #ecfdf5; }
.rr-cell.rr-onsched .rr-role::before { content: "✓ "; color: #059669; font-weight: 700; }
/* A blank source cell on a row that IS on the schedule: click to CLEAR the role
   from the day. Dashed outline on hover cues "click to remove". */
.rr-cell.rr-clearable { cursor: pointer; border-radius: 4px; color: #cbd5e1; }
.rr-cell.rr-clearable:hover { background: #fff1f2; box-shadow: inset 0 0 0 1px #fecdd3; color: #be123c; }
.rr-cell.rr-clearable:hover::after { content: "clear"; font-size: 10px; }
.rr-clearbtn {
    border: 1px solid #fecaca; background: #fff5f5; color: #b91c1c; font-size: 10px;
    line-height: 1; cursor: pointer; padding: 1px 6px; border-radius: 4px;
    margin-left: 8px; vertical-align: middle;
}
.rr-clearbtn:hover { background: #fee2e2; }
.rr-x {
    border: none; background: none; color: #b91c1c; font-size: 15px; line-height: 1;
    cursor: pointer; padding: 0 4px;
}
.rr-x:hover { color: #7f1d1d; }

/* Settled (AGREE) rows in the role review — shown for completeness but dimmed
   so the eye lands on the rows that need attention. */
.rr-row.rr-settled { opacity: 0.62; }
.rr-row.rr-settled:hover { opacity: 1; }

/* ==========================================================================
   Role intents audit panel (project detail #section-role-intents).
   ========================================================================== */
.ri-table { border-collapse: collapse; width: 100%; max-width: 900px; font-size: 13px; }
.ri-table th {
    text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .03em;
    color: #64748b; font-weight: 600; padding: 4px 8px; border-bottom: 1px solid #e2e8f0;
}
.ri-table td { padding: 5px 8px; border-bottom: 1px solid #f1f5f9; vertical-align: middle; }
.ri-role { font-weight: 600; color: #1e293b; }
.ri-open { color: #64748b; font-style: italic; }
.ri-cov, .ri-src { color: #475569; }
.ri-row.ri-inactive { opacity: 0.5; }
.ri-retired { font-size: 10px; text-transform: uppercase; color: #94a3b8; }
/* State badges — pastel, dark text, matching the labor tables' palette. */
.ri-badge {
    display: inline-block; font-size: 11px; font-weight: 700; padding: 1px 8px;
    border-radius: 10px; color: #1a1a1a;
}
.ri-badge.ri-full    { background: #d1fae5; }   /* soft green */
.ri-badge.ri-partial { background: #e2e8f0; }   /* slate */
.ri-badge.ri-broken  { background: #fecaca; }   /* soft red */
.ri-badge.ri-dormant { background: #fde68a; }   /* soft amber */
.ri-actions { white-space: nowrap; text-align: right; }
.ri-btn {
    font-size: 11px; padding: 2px 8px; margin-left: 4px; border-radius: 4px;
    border: 1px solid #cbd5e1; background: #fff; color: #334155; cursor: pointer;
}
.ri-btn:hover { background: #f1f5f9; }
.ri-btn-danger { border-color: #fecaca; color: #b91c1c; }
.ri-btn-danger:hover { background: #fef2f2; }

/* ---------------------------------------------------------------------------
   DATE RANGE / HOURS mismatch callout (LaborDateRangeMismatch).
   Moved here from the inline <style> in templates/tracker/reconcile.html so
   BOTH reconcile surfaces can render the table: the detailed page
   (/reconcile/detailed/) and the landing page (/reconcile/). The markup is
   built by static/js/labor_mismatch.js, which both templates load.
   --------------------------------------------------------------------------- */
.rec-mismatch-section { border:2px solid #f87171; border-radius:6px; background:#fef2f2;
                        margin-bottom:10px; }
.rec-mismatch-section > summary { cursor:pointer; padding:8px 12px; font-size:15px;
                        font-weight:700; color:#991b1b; list-style:none; }
.rec-mismatch-section > summary::-webkit-details-marker { display:none; }
.rec-mismatch-section > summary::before { content:'▸ '; }
.rec-mismatch-section[open] > summary::before { content:'▾ '; }
.rec-mismatch-count { font-weight:600; color:#b91c1c; font-size:13px; }
.rec-mismatch-body { padding:0 12px 10px; }
.rec-mismatch-hint { font-size:12px; color:#7f1d1d; margin:0 0 8px; }
.rec-mismatch-proj { margin-bottom:8px; }
.rec-mismatch-projhdr { display:flex; align-items:center; gap:6px; }
.rec-mismatch-projname { font-weight:600; font-size:13px; color:#1e293b; text-decoration:none; }
.rec-mismatch-projname:hover { text-decoration:underline; }
.rec-mismatch-tbl { border-collapse:collapse; font-size:12px; margin-top:2px; }
.rec-mismatch-tbl td { padding:2px 10px 2px 0; color:#334155; vertical-align:middle; }
.rec-mm-role { font-weight:600; color:#1e293b; }
.rec-mismatch-hours { color:#64748b; white-space:nowrap; }
.rec-mismatch-range { color:#64748b; white-space:nowrap; }
.rec-mm-care { color:#64748b; white-space:nowrap; }
.rec-mm-care label { cursor:pointer; display:inline-flex; align-items:center; gap:3px; font-size:11px; }
.rec-mm-dismissed td { color:#94a3b8; }
.rec-mm-dismissed .rec-mm-role { color:#94a3b8; font-weight:500; }
.rec-mm-dismissed-wrap { margin-top:4px; }
.rec-mm-dismissed-wrap > summary { cursor:pointer; font-size:11px; color:#94a3b8; list-style:none; }
.rec-mm-dismissed-wrap > summary::-webkit-details-marker { display:none; }
.rec-mm-dismissed-wrap > summary::before { content:'▸ '; }
.rec-mm-dismissed-wrap[open] > summary::before { content:'▾ '; }

/* Disagreement note on a mismatch row: shown when only ONE of the two
   detectors fired (methods !== 'both'), naming which one and what it tripped
   on. Muted and italic — it is a caveat on the flag, not a second flag. */
.rec-mm-note { color:#94a3b8; white-space:nowrap; }
.rec-mm-disagree { font-size:11px; font-style:italic; color:#94a3b8; }
.rec-mm-dismissed .rec-mm-disagree { color:#cbd5e1; }

/* Fully-dismissed projects collect into one container at the BOTTOM of the
   mismatch callout, below the last live row. Only regrouped on page load —
   dismissing a row mid-session hides it in place (see stickyActive in
   labor_mismatch.js) so nothing jumps under the cursor. Inside, rows render
   directly rather than behind a second per-project disclosure. */
.rec-mm-alldismissed { margin-top:10px; border-top:1px dashed #fca5a5; padding-top:8px; }
.rec-mm-alldismissed > summary { cursor:pointer; font-size:12px; font-weight:600;
                                 color:#b91c1c; list-style:none; user-select:none; }
.rec-mm-alldismissed > summary::-webkit-details-marker { display:none; }
.rec-mm-alldismissed > summary::before { content:'▸ '; }
.rec-mm-alldismissed[open] > summary::before { content:'▾ '; }
.rec-mm-alldismissed-count { font-weight:400; color:#94a3b8; }
.rec-mm-alldismissed-body { padding-top:6px; }

/* ---------------------------------------------------------------------------
   Rich-text editor (static/js/rich_text.js) — Project Questions, Initial
   Questions, Initial Notes. Deliberately shaped like .form-control so it sits
   in the Project Info grid without looking like a different kind of control.
   --------------------------------------------------------------------------- */
.rt-editor {
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface, #fff);
    overflow: hidden;
    /* Drag-resizable like the textarea it replaced. Flex column so the body
       takes the leftover height and the toolbar stays pinned to the top. */
    resize: both;
    display: flex;
    flex-direction: column;
    min-height: 90px;
    min-width: 180px;
    height: 190px;
}
.rt-editor:focus-within { border-color: var(--primary); }

.rt-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    padding: 3px 4px;
    background: var(--bg-subtle, #f8f9fa);
    border-bottom: 1px solid var(--border-light);
}
.rt-btn {
    font-size: 12px;
    line-height: 1;
    padding: 4px 7px;
    min-width: 26px;
    border: 1px solid transparent;
    border-radius: 3px;
    background: transparent;
    color: var(--text);
    cursor: pointer;
}
.rt-btn:hover { background: #fff; border-color: var(--border-light); }
.rt-btn:active { background: var(--border-light); }

.rt-body {
    padding: 6px 8px;
    font-family: inherit;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text);
    outline: none;
    /* Fills whatever height the (resizable) .rt-editor has and scrolls its own
       overflow — no max-height, or dragging taller would stop having an
       effect past the cap. */
    flex: 1;
    min-height: 0;
    overflow: auto;
}
.rt-body ul, .rt-body ol { margin: 4px 0; padding-left: 22px; }
.rt-body li { margin: 1px 0; }
.rt-body a { color: var(--primary); }
.rt-body p { margin: 0 0 6px; }

/* contenteditable has no placeholder attribute — .rt-empty is set by
   _syncPlaceholder() in rich_text.js. */
.rt-body.rt-empty::before {
    content: attr(data-rt-placeholder);
    color: var(--text-light);
    font-style: italic;
    pointer-events: none;
}

/* Printing/"clean view": the toolbar is chrome, never content. */
@media print {
    .rt-toolbar { display: none; }
    .rt-editor { border: none; }
    .rt-body { max-height: none; overflow: visible; }
}

/* ---------------------------------------------------------------------------
   Quick Todos column (Notes page) — sits between the journal and the scratch
   pads. Derived entirely from bracket lines in the note text; see jqRebuild()
   in notes_list.js. Narrower than the post-it sidebar: it's a glanceable list,
   not a writing surface.
   --------------------------------------------------------------------------- */
.journal-quicktodo-sidebar {
    width: 240px;
    background: var(--bg-alt);
}
.jq-hint {
    font-size: 11px;
    color: var(--text-light);
    margin: -4px 0 8px;
    line-height: 1.4;
}
.jq-hint code {
    background: var(--border-light);
    border-radius: 3px;
    padding: 0 3px;
}

.jq-item {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    padding: 4px 0;
    border-bottom: 1px solid var(--border-light);
    font-size: 12px;
    line-height: 1.45;
}
.jq-box {
    flex-shrink: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    font-weight: 700;
    color: var(--text-muted);
    cursor: pointer;
    user-select: none;
    border-radius: 3px;
    padding: 0 1px;
}
.jq-box:hover { color: var(--primary); background: var(--border-light); }
.jq-box:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

.jq-text {
    flex: 1;
    min-width: 0;
    color: var(--text);
    overflow-wrap: anywhere;
}
.jq-day {
    flex-shrink: 0;
    font-size: 10px;
    color: var(--text-light);
    white-space: nowrap;
}

/* Priority — the second axis on a bracket todo (^ high, v low). The control is
   always present so a row can be demoted without opening the note; at normal it
   is a faint dot that only comes forward on hover, because most todos have no
   priority and a column of live controls would drown the two that do. */
.jq-prio {
    flex-shrink: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    line-height: 1.45;
    color: var(--border);
    cursor: pointer;
    user-select: none;
    border-radius: 3px;
    padding: 0 3px;
}
.jq-prio:hover { color: var(--primary); background: var(--border-light); }
.jq-prio:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.jq-high .jq-prio { color: #dc2626; font-weight: 700; }
.jq-low .jq-prio { color: var(--text-light); }

/* HIGH LEANS IN, LOW STEPS BACK. The sort already does the work; this is only
   so the top and bottom of the list are recognisable at a glance without
   reading the markers. */
.jq-high .jq-text { color: var(--text); font-weight: 600; }
.jq-low .jq-text { color: var(--text-light); }

/* Empty state — the column stays put so the legend above it does too. */
.jq-empty {
    font-size: 11px;
    color: var(--text-light);
    font-style: italic;
    padding: 4px 0;
}

.jq-wip .jq-box { color: #d97706; }
/* Done rows only linger until the next reload — muted, so it's obvious the
   click landed without them competing with what's still outstanding. */
.jq-done .jq-text { color: var(--text-light); text-decoration: line-through; }
.jq-done .jq-box { color: #16a34a; }

@media print { .journal-quicktodo-sidebar { display: none !important; } }

/* ---------------------------------------------------------------------------
   Drag-to-resize plumbing (see initResizableFields in generic_preload.js).
   --------------------------------------------------------------------------- */

/* A grid item holding a resizable field must be allowed to shrink below its
   content's min-content width, or the drag can widen a cell but never narrow
   it again. */
.detail-fields > .form-group { min-width: 0; }

/* A resized dashboard cell stops being flex:1 — an explicit height has to win
   over "fill the column". */
.dash-col-field .dash-inline-area.is-resized { flex: none; }

/* Make the native resize corner findable. Purely a hint; the handle itself is
   drawn by the browser. */
.detail-fields textarea.form-control,
.rt-editor,
.dash-inline-area {
    background-clip: padding-box;
}

/* ---------------------------------------------------------------------------
   DEEP reconcile table (static/js/deep_reconcile.js). One flat table, projects
   separated by two blank rows and nothing else — the point is to scan a long
   list in one pass, not to navigate per-project cards.
   --------------------------------------------------------------------------- */
.deep-wrap {
    margin: 18px 0 8px;
    border-top: 2px solid var(--border);
    padding-top: 10px;
}
.deep-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.deep-controls {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin: 10px 0 4px;
}
.deep-sep { width: 1px; height: 20px; background: var(--border-light); margin: 0 4px; }
.deep-showign { font-size: 12px; display: inline-flex; align-items: center; gap: 4px; }
.deep-hint { margin-bottom: 8px; }
.deep-empty { padding: 14px; color: var(--text-muted); font-size: 13px; }

.deep-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}
.deep-table th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border);
    padding: 4px 6px;
    position: sticky;
    top: 0;
    background: var(--bg);
    z-index: 1;
}
.deep-table td { padding: 3px 6px; vertical-align: top; }
.deep-row { border-bottom: 1px solid var(--border-light); }
.deep-row:hover { background: var(--bg-alt); }

/* The two-blank-row project separator. */
.deep-gap td { height: 9px; border: none; padding: 0; }

.deep-proj { font-weight: 600; max-width: 260px; }
.deep-num { font-weight: 400; color: var(--text-light); font-size: 11px; }
.deep-rw { color: var(--text-muted); font-size: 11px; }
.deep-date { white-space: nowrap; }
.deep-subj { display: block; color: var(--text-muted); font-size: 11px; }
.deep-src, .deep-cur { max-width: 260px; overflow-wrap: anywhere; }
.deep-cur { font-weight: 600; }
/* "reports nothing" must not read the same as "reports an empty value". */
.deep-none { color: var(--text-light); }

/* Sources are unanimous and the schedule is simply stale — the safe promote. */
.deep-precheck .deep-cur { color: #b45309; }
.deep-ignored { opacity: 0.45; }
.deep-chk-h, .deep-chk { text-align: center; width: 56px; }
.deep-chk input { cursor: pointer; }

/* ---------------------------------------------------------------------------
   "Items on this page changed" as an inline pill instead of a full-width
   banner strip. A page opts in by providing a #bgRefreshPill mount point next
   to its heading (see notes_list.html); pages without one keep the banner.
   Costing a whole horizontal band — and a chunk of --chrome-h — for a one-line
   notice was the wrong trade on a page whose panes are full-height.
   --------------------------------------------------------------------------- */
.bg-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    line-height: 1;
    padding: 3px 8px;
    border-radius: 11px;
    background: #fef3c7;
    border: 1px solid #f59e0b;
    color: #92400e;
    white-space: nowrap;
}
.bg-pill a { color: #92400e; font-weight: 600; text-decoration: underline; cursor: pointer; }
.bg-pill button {
    border: none;
    background: transparent;
    color: #92400e;
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    padding: 0 0 0 2px;
}
#bgRefreshPill:empty { display: none; }

@media print { #appChrome { display: none !important; } }

/* A dismissed alias raw: excluded from the reconcile COMPARISON only — the
   data still shows in the UI and reports (see SourceAlias.dismissed). Muted
   and struck so it reads as "not compared", not as "deleted". */
.rec-alias-row-dismissed .rec-alias-raw {
    text-decoration: line-through;
    color: var(--text-light);
}
.rec-alias-row-dismissed { opacity: 0.65; }
.rec-alias-row-dismissed .rec-alias-input { font-style: italic; }


/* ---------------------------------------------------------------------------
   Deep reconcile: ONE spreadsheet-style grid. Every row — project header and
   data alike — uses the same columns, so it reads top-to-bottom like Excel
   rather than as a stack of little per-project tables.
   --------------------------------------------------------------------------- */
.deep-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg);
    border-bottom: 2px solid var(--border);
    text-align: left;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    padding: 5px 6px;
    white-space: nowrap;
}
.deep-c-key { min-width: 220px; }
.deep-c-src { min-width: 130px; }
.deep-c-cur { min-width: 130px; font-weight: 600; }
.deep-c-prev { min-width: 150px; }
.deep-c-ign { width: 70px; text-align: center; }

/* Project row: same grid, just heavier — it is a band across the table. */
.deep-phead td {
    background: var(--bg-alt);
    border-top: 2px solid var(--border);
    border-bottom: 1px solid var(--border);
    font-weight: 700;
    font-size: 11px;
    padding: 5px 6px;
}
.deep-phead-name { white-space: nowrap; }
.deep-phead-src {
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.deep-link { margin-left: 8px; font-weight: 400; font-size: 11px; }
.deep-rw { margin-left: 6px; font-weight: 400; font-size: 11px; color: var(--text-muted); }

/* Any cell that WRITES when clicked. Deliberately obvious. */
.deep-click { cursor: pointer; }
.deep-click:hover {
    background: rgba(59, 130, 246, 0.14);
    outline: 1px solid var(--primary);
    outline-offset: -1px;
}
.deep-phead .deep-click:hover { background: rgba(59, 130, 246, 0.22); }
.deep-c-ign.deep-click { color: var(--primary); font-weight: 600; font-size: 10px; }

/* Change preview: what promoting would actually do. */
.deep-add { color: #16a34a; font-weight: 600; }
.deep-rem { color: #dc2626; font-weight: 600; text-decoration: line-through; }
.deep-prev-num { color: #b45309; font-weight: 600; }

/* Deep reconcile: the SELECTED source for a row. Clicking arms a change and
   highlights where it comes from; nothing is written until "Promote selected".
   Without this the Change Preview column was unattributable — you could see
   the proposed change but not which source proposed it. */
.deep-armed {
    background: rgba(22, 163, 74, 0.16) !important;
    outline: 2px solid #16a34a;
    outline-offset: -2px;
    font-weight: 600;
}
.deep-count {
    font-weight: 700;
    color: var(--text-muted);
    margin-left: 2px;
}

/* Deep reconcile: a pre-selected row whose promote would DELETE a hand-entered
   crew member. Lasso wins the crew plane by decision, so these stay selected —
   but a destructive row must never be invisible inside a batch of harmless
   ones. */
.deep-manual-conflict td { background: rgba(217, 119, 6, 0.07); }
.deep-warn { color: #b45309; font-weight: 700; white-space: nowrap; }
.deep-btn-warn { background: #b45309 !important; border-color: #b45309 !important; }

/* Deep reconcile: cells whose SOURCES contradict each other — the rows that
   need a human. Deliberately not "differs from Current": on a pre-checked row
   the schedule is stale by definition, so tinting that would paint the safe
   majority red and make the colour say nothing. */
.deep-conflict { background: rgba(220, 38, 38, 0.10); }
.deep-conflict.deep-armed { background: rgba(22, 163, 74, 0.16) !important; }

.deep-summary {
    padding: 6px 8px;
    margin-bottom: 8px;
    background: var(--bg-alt);
    border: 1px solid var(--border-light);
    border-radius: 4px;
    font-size: 12px;
    color: var(--text-muted);
}
.deep-sum-ok { color: #16a34a; }
.deep-sum-bad { color: #dc2626; }
.deep-sum-warn { color: #b45309; }

/* Deep reconcile: crew a promote will REFUSE to delete (published/confirmed,
   or carrying notes, manual times, actuals, a team or tasks). Showing "-Name"
   for these was a lie — the promote reported success and nothing moved. */
.deep-prot { color: #6d28d9; font-weight: 600; }
/* A row where NOTHING can change by promoting. */
.deep-stuck td { background: rgba(109, 40, 217, 0.06); }
.deep-sum-lock { color: #6d28d9; }

/* Deep reconcile promote result — persists past the re-scan that follows it. */
.deep-result { font-size: 12px; font-weight: 600; color: #16a34a; margin-left: 6px; }
.deep-result-bad { color: #dc2626; }

/* Deep reconcile: why the auto-apply loop declined this row. */
.deep-hold { color: #6b7280; font-style: italic; font-size: 11px; white-space: nowrap; }

/* ---- Reconcile matrix -----------------------------------------------------
   One row per fact, one column per source, then MANUAL, then CURRENT. The two
   right-hand columns are visually separated because they are different kinds
   of thing: manual is a SOURCE (highest precedence, usually empty), current is
   the OUTCOME. Rows where manual disagrees with every source are tinted — that
   is manual gone stale, the case this page exists for. */
.mx-wrap { padding: 8px 12px 40px; }
/* NOT sticky. It was, and it kept covering whatever sat directly beneath it —
   first the top data row on mobile, then the hours-mismatch panel. Two fixes
   in and the offset was still wrong, because a sticky element's height is not
   knowable from CSS when its controls wrap. The TABLE head is the part worth
   pinning while you scroll a long list; the title and the plane buttons are
   not, and nothing is lost by letting them scroll away. */
.mx-head { background: #fff; padding: 6px 0 8px;
           border-bottom: 1px solid #e5e7eb; }
.mx-head h2 { margin: 0 0 6px; font-size: 18px; }
.mx-proj { font-weight: 400; color: #6b7280; font-size: 14px; margin-left: 8px; }
.mx-controls { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.mx-planes { display: flex; gap: 4px; }
.mx-plane { padding: 4px 12px; border: 1px solid #d1d5db; background: #fff;
            border-radius: 4px; cursor: pointer; font-size: 13px; }
.mx-plane.is-on { background: #2563eb; color: #fff; border-color: #2563eb; }
.mx-only { font-size: 13px; color: #374151; display: flex; align-items: center; gap: 5px; }
.mx-totals { font-size: 13px; color: #6b7280; margin-left: auto; }
.mx-proj-block { margin: 18px 0 26px; }
.mx-proj-title { font-size: 14px; margin: 0 0 6px; font-weight: 600; }
.mx-proj-title a { color: #1f2937; text-decoration: none; }
.mx-proj-title a:hover { text-decoration: underline; }
.mx-count { font-weight: 400; color: #9ca3af; font-size: 12px; margin-left: 6px; }
/* FIXED layout, deliberately. Owner: "I want my eye to move up and down per
   source, not left and right." Each project is its own <table>, so with the
   default auto layout every table sized its columns to its own content and
   `lasso` sat at a different x-position in each one — you had to re-find the
   column on every block. `table-layout: fixed` plus a 100% width makes the
   unsized columns share the remaining space equally, and since the source
   list is constant every table lands on the same grid.

   Cells WRAP rather than truncate: a long label growing the row taller is
   readable, whereas an ellipsis hides exactly the value being compared. */
.mx-table { border-collapse: collapse; font-size: 12.5px; width: 100%;
            table-layout: fixed; }
.mx-table th, .mx-table td { border: 1px solid #e5e7eb; padding: 3px 7px;
                             text-align: left; vertical-align: top;
                             overflow-wrap: anywhere; }
/* Only these are pinned; the source/manual/current columns divide what is
   left, which is what keeps them identical across every block. */
.mx-table th.mx-when, .mx-table td.mx-when { width: 92px; }
.mx-table th.mx-subj, .mx-table td.mx-subj { width: 150px; }
.mx-table td.mx-apply-cell, .mx-table th.mx-apply-cell { width: 116px; }
.mx-table thead th { background: #f9fafb; font-weight: 600; position: sticky;
                     /* Sticks under the site chrome only — .mx-head scrolls
                        away with the page, so there is nothing else to clear
                        and no measured offset to get wrong. */
                     top: var(--chrome-h, 52px); z-index: 4; }
.mx-when { white-space: nowrap; color: #6b7280; }
.mx-subj { white-space: nowrap; font-weight: 500; }
/* manual + current sit apart from the sources */
.mx-table th.mx-man, .mx-table td.mx-man { border-left: 2px solid #9ca3af; background: #fffbeb; }
.mx-table th.mx-cur, .mx-table td.mx-cur { border-left: 2px solid #111827;
                                           background: #f3f4f6; font-weight: 600; }
.mx-disagree td.mx-when, .mx-disagree td.mx-subj { box-shadow: inset 3px 0 0 #dc2626; }
.mx-stale td.mx-man { background: #fee2e2; }
.mx-take-cell, .mx-take-col { border: 0; background: transparent; padding: 0;
                              font: inherit; color: #1d4ed8; cursor: pointer;
                              text-align: left; }
.mx-take-cell:hover, .mx-take-col:hover { text-decoration: underline; }
.mx-take-col { font-weight: 600; color: #111827; }
.mx-none { color: #d1d5db; }
.mx-loading, .mx-empty, .mx-err { padding: 24px; color: #6b7280; }
.mx-err { color: #b91c1c; }
/* Column actions. Duplicated from the header row on purpose — the header is
   what card mode removes, and "take the whole column" must outlive it. */
/* One chip per thing a source says. Tick to include it in the day's types. */
.mx-chip { display: inline-block; border: 1px solid #d1d5db; background: #fff;
           border-radius: 4px; padding: 1px 6px; margin: 1px 3px 1px 0;
           font: inherit; font-size: 12px; line-height: 1.5; color: #1f2937;
           cursor: pointer; }
.mx-chip:hover { border-color: #2563eb; color: #1d4ed8; }
.mx-chip.is-on { background: #2563eb; border-color: #2563eb; color: #fff; }
.mx-chip-ro { cursor: default; background: #f9fafb; color: #374151; }
.mx-chip-ro:hover { border-color: #d1d5db; color: #374151; }
/* MARKERS (Ship, Deliver, Start, Stop). Drawn as notes, not as candidates:
   a non-working RW date rides beside the day's type and cannot contradict it,
   so it never votes and is never what a disagreement is about. Still shown —
   "why is that there" is a real question and hiding it answers nothing. */
.mx-chip-marker { border-style: dashed; color: #6b7280; background: #fafafa;
                  cursor: default; padding-right: 3px; }
.mx-chip-marker:hover { border-color: #d1d5db; color: #6b7280; }
/* Removed by hand. Kept visible and struck rather than dropped: the source
   still reports it, and you need to see that to put it back. */
.mx-chip-marker.is-hidden { text-decoration: line-through; opacity: .55; }
.mx-marker-x { border: 0; background: transparent; color: #9ca3af;
               cursor: pointer; font: inherit; font-size: 12px;
               line-height: 1; padding: 0 2px 0 5px; }
.mx-marker-x:hover { color: #b91c1c; }
.mx-chip-marker.is-hidden .mx-marker-x { color: #2563eb; }
.mx-apply-cell:empty { padding: 0; border: 0; }
.mx-apply { border: 1px solid #16a34a; background: #16a34a; color: #fff;
            border-radius: 4px; padding: 2px 9px; font-size: 12px;
            cursor: pointer; white-space: nowrap; }
.mx-apply:hover { background: #15803d; }
.mx-cancel { border: 0; background: transparent; color: #9ca3af; font-size: 15px;
             cursor: pointer; padding: 0 4px; line-height: 1; }
.mx-cancel:hover { color: #b91c1c; }
/* Out-links: the project detail page, and RW. `.rw-kind-chip` carries the
   order/quote colouring already used on the dashboard and detail page. */
.mx-outlinks { margin: 0 0 6px; }
.mx-outlink { font-size: 12px; text-decoration: none; margin-left: 8px;
              white-space: nowrap; }
a.mx-outlink { color: #2563eb; }
a.mx-outlink:hover { text-decoration: underline; }
.mx-outlink-dead { color: #9ca3af; }
/* DEMOTE. Deliberately quieter than promote: taking a source's value is the
   common act, undoing it is the correction. ⤒ mirrors promote's ⤓. */
.mx-drop-col { border: 0; background: transparent; color: #9ca3af;
               cursor: pointer; font: inherit; padding: 0 3px; }
.mx-drop-col:hover { color: #b91c1c; }
.mx-colbar .mx-drop-col { border: 1px solid #e5e7eb; border-radius: 999px;
                          padding: 3px 10px; font-size: 12px; }
.mx-demote-one { border: 0; background: transparent; color: #9ca3af;
                 cursor: pointer; font: inherit; font-size: 12px;
                 line-height: 1; padding: 0 2px 0 5px; }
.mx-demote-one:hover { color: #b91c1c; }
.mx-legacy { font-size: 12px; color: #6b7280; text-decoration: none; }
.mx-legacy:hover { text-decoration: underline; }
.mx-colbar { display: none; gap: 6px; flex-wrap: wrap; align-items: center;
             margin: 0 0 6px; }
.mx-colbar-lab { font-size: 11px; color: #6b7280; text-transform: uppercase;
                 letter-spacing: .04em; }
.mx-colbar .mx-take-col { border: 1px solid #d1d5db; border-radius: 999px;
                          padding: 3px 10px; font-size: 12px; background: #fff; }

/* ---------------------------------------------------------------------------
   Mobile: the matrix stops being a table.

   One column per source is the whole point of this page and also the reason it
   cannot survive a phone — six or seven columns can only be reached by scrolling
   sideways, and a sideways-scrolling table hides the very comparison the page
   exists to show. So below 720px each ROW becomes a card and each CELL becomes
   a labelled line: everything is reachable by scrolling down only.

   Empty cells are dropped entirely rather than rendered blank. On a typical row
   most sources say nothing, and printing "excel —" on every card is precisely
   the wasted space that made this unusable. What is left is only what a source
   actually claims.
   --------------------------------------------------------------------------- */
@media (max-width: 720px) {
  .mx-wrap { padding: 6px 8px 40px; }
  /* NOT sticky on mobile. Two stacked sticky layers (this, plus the table
     head) were positioned with a hardcoded 74px offset measured on desktop;
     the controls wrap to two or three lines on a phone, so the real height is
     larger and the header sat ON TOP of the first data row. A phone has too
     little vertical space to give a header a permanent third of it anyway. */
  .mx-head { padding: 4px 0 6px; }
  .mx-head h2 { font-size: 15px; }
  .mx-proj { display: block; margin-left: 0; font-size: 12px; }
  .mx-controls { gap: 8px; }
  .mx-totals { margin-left: 0; flex-basis: 100%; font-size: 12px; }
  .mx-plane { padding: 5px 10px; }
  .mx-proj-block { margin: 12px 0 18px; }
  .mx-colbar { display: flex; }

  /* Header row carries no data in card mode — the labels move onto the cells. */
  .mx-table, .mx-table tbody, .mx-table tr, .mx-table td { display: block; width: auto; }
  .mx-table thead { display: none; }

  .mx-table tr { border: 1px solid #e5e7eb; border-radius: 8px; padding: 5px 8px;
                 margin: 0 0 6px; background: #fff; }
  .mx-table td { border: 0; padding: 1px 0; display: flex; gap: 8px;
                 align-items: baseline; font-size: 13px; }
  .mx-table td.mx-empty-cell { display: none; }
  .mx-table td::before {
      content: attr(data-label); flex: 0 0 5.2em; color: #6b7280;
      font-size: 11px; text-transform: uppercase; letter-spacing: .03em;
      line-height: 1.5;
  }
  /* The date is the card's title, so it loses its label and gains weight. */
  .mx-table td.mx-when { font-weight: 600; font-size: 13.5px; color: #111827;
                         margin-bottom: 2px; }
  .mx-table td.mx-when::before { content: none; }
  .mx-table td.mx-subj { font-weight: 600; }
  .mx-table td.mx-subj::before { content: none; }

  /* Keep manual/current visually apart from the sources without the borders,
     which read as clutter once every cell is its own line. */
  .mx-table td.mx-man, .mx-table td.mx-cur { border-left: 0; background: none;
                                             padding-left: 0; }
  .mx-table td.mx-cur { margin-top: 3px; padding-top: 3px;
                        border-top: 1px dashed #e5e7eb; font-weight: 600; }
  .mx-table td.mx-man::before { color: #b45309; }
  .mx-table td.mx-cur::before { color: #111827; }
  .mx-stale td.mx-man { background: none; color: #b91c1c; }
  .mx-stale td.mx-man::before { color: #b91c1c; }
  .mx-disagree { border-color: #fca5a5; box-shadow: inset 3px 0 0 #dc2626; }
  .mx-disagree td.mx-when, .mx-disagree td.mx-subj { box-shadow: none; }

  /* Tap targets: a source value is a button and has to be reachable by thumb
     without becoming a full-width block that re-inflates the card. */
  .mx-take-cell { padding: 2px 0; min-height: 22px; }
  .mx-chip { padding: 3px 9px; margin: 2px 4px 2px 0; font-size: 12.5px; }
  /* The row's cells are flex lines, so the chip column needs to wrap rather
     than push the card wider — a card that scrolls sideways is the bug. */
  .mx-table td { flex-wrap: wrap; }
  .mx-table td.mx-apply-cell { justify-content: flex-end; gap: 4px;
                               margin-top: 4px; }
  .mx-table td.mx-apply-cell::before { content: none; }
  .mx-apply { padding: 5px 14px; font-size: 13px; }
  .mx-cancel { font-size: 18px; padding: 0 8px; }
}

/* ---------------------------------------------------------------------------
   Reconcile: at-a-glance state.

   Owner: "Columns that are new need a light blue highlight. Cells that
   conflict need a yellow highlight. I want to see at a glance: we got new rw
   labor data, this cell in particular doesn't agree with lasso."

   Two INDEPENDENT signals, so they must not use the same channel: blue is
   "this moved", yellow is "this is what disagrees". A cell can be both — new
   data that also conflicts is the most interesting cell on the page — so the
   conflict background wins and freshness shows as a left edge, rather than
   the two colours blending into a third meaning nobody defined.
   --------------------------------------------------------------------------- */
.mx-table td.mx-cell-new { background: #eff6ff; }            /* light blue */
.mx-table td.mx-cell-conflict { background: #fef9c3; }       /* yellow */
.mx-table td.mx-cell-conflict.mx-cell-new { box-shadow: inset 3px 0 0 #3b82f6; }
/* Muted by a stance: still shown, deliberately — hiding it entirely would
   make "why is nothing here" unanswerable — but visibly out of the running. */
.mx-table td.mx-cell-muted { opacity: .45; }

.mx-col-head { vertical-align: top; }
.mx-col-head.is-new { background: #dbeafe; }
.mx-col-name { font-weight: 600; }
.mx-pill { display: inline-block; margin-left: 5px; padding: 0 6px;
           border-radius: 999px; font-size: 10px; font-weight: 700;
           letter-spacing: .03em; text-transform: uppercase; vertical-align: 1px; }
.mx-pill-new { background: #2563eb; color: #fff; }
.mx-pill-old { background: #e5e7eb; color: #6b7280; }
.mx-pill-stance { background: #111827; color: #fff; }
.mx-stance-trust .mx-pill-stance { background: #16a34a; }
.mx-stance-wrong .mx-pill-stance { background: #b91c1c; }
.mx-stance-ignore .mx-pill-stance { background: #6b7280; }
.mx-col-acts { display: block; margin-top: 3px; }
.mx-stance-btn { border: 0; background: transparent; color: #9ca3af;
                 cursor: pointer; font: inherit; padding: 0 3px; }
.mx-stance-btn:hover { color: #111827; }
/* An ignored column is greyed wholesale — the point of the stance is that you
   stop reading it, so it should stop competing for attention. */
.mx-stance-ignore { opacity: .6; }

/* Reconcile embedded in the project page: one table per data type, both keyed
   by date so a misalignment between the days and the roles is visible without
   switching between them. */
.mx-sub { font-size: 13px; font-weight: 600; color: #374151;
          margin: 14px 0 6px; padding-bottom: 3px;
          border-bottom: 1px solid #e5e7eb; }
.mx-sub .mx-totals { font-weight: 400; margin-left: 8px; }
#section-reconcile-matrix .mx-body { margin-top: 4px; }

/* --- ITEM-ATTRIBUTE CELLS — attr_cells.js, TWO SCREENS ---------------------
   AUTOQUOTEMAKER_PLAN §32.13, owner: "The Boolean style from the inventory
   attributes page should carry over to the gear list on the quote maker."

   These rules live in the site stylesheet rather than in a template's own
   <style> block because BOTH screens draw the cell — /inventory-attributes/
   and the quote page's gear list — and the two are different templates. A copy
   per template is exactly how one of them ends up with a checkbox that swallows
   the drag while the other paints.

   THE CHECKBOX IS SCENERY. `pointer-events:none` is load-bearing: the CELL
   handles every press, so a drag across a column paints instead of being eaten
   by the control it crosses. INDETERMINATE IS NOT DECORATION — it is "nobody
   has said", which is a different fact from "No", and it is the state the whole
   sparse-attribute model rests on. */
.ac-cb { pointer-events: none; margin: 0; vertical-align: middle; cursor: cell; }
.ac-cb:indeterminate { opacity: .3; }
td.ac-bool { text-align: center; }

/* THE NAMED STATE — owner ruling, 2026-08-17. A project with no assembled
   schedule reaches the dashboard only through the stored-dates fallback in
   `utils.projects_only_in_stored_dates`. He ruled it stays VISIBLE and says so,
   rather than the fallback quietly serving old rows. Amber, not red: the
   project is fine, the derived data is missing. */
.dash-no-sched {
    font-size: 10px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .03em;
    padding: 1px 5px;
    border-radius: 3px;
    color: var(--warning, #b45309);
    border: 1px solid var(--warning, #b45309);
    opacity: .85;
    cursor: help;
}
