/* ==========================================================================
   BE Platform - Custom Styles
   Works alongside Bootstrap 5.3.0
   ========================================================================== */

/* --------------------------------------------------------------------------
   CSS Variables
   -------------------------------------------------------------------------- */

:root {
    --brand-green: #202d26;
    --brand-black: #000000;
    --brand-green-light: #2d3f35;
    --brand-green-lighter: #3a5144;
    --border-color: #dee2e6;
}

/* --------------------------------------------------------------------------
   Base / Typography
   -------------------------------------------------------------------------- */

body {
    font-size: 0.875rem;
}

a {
    color: var(--brand-green);
}

a:hover {
    color: var(--brand-green-light);
}

.small-text {
    font-size: 0.8rem;
}

/* --------------------------------------------------------------------------
   Brand Color Utility Classes
   -------------------------------------------------------------------------- */

.bg-brand-dark {
    background-color: var(--brand-green) !important;
}

.bg-brand-light {
    background-color: var(--brand-green-light) !important;
}

.text-brand-green {
    color: var(--brand-green) !important;
}

.border-brand {
    border-color: var(--brand-green) !important;
}

/* --------------------------------------------------------------------------
   Navbar
   -------------------------------------------------------------------------- */

.navbar.bg-brand-dark {
    background-color: var(--brand-green) !important;
    padding: 0.5rem 1rem;
}

.navbar .nav-link {
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.875rem;
}

.navbar .nav-link:hover {
    color: #ffffff;
}

.navbar .nav-link.active {
    color: #ffffff;
    font-weight: 600;
}

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

.btn-brand {
    background-color: var(--brand-green);
    border-color: var(--brand-green);
    color: #ffffff;
}

.btn-brand:hover,
.btn-brand:focus {
    background-color: var(--brand-green-light);
    border-color: var(--brand-green-light);
    color: #ffffff;
}

.btn-brand:active {
    background-color: var(--brand-green-lighter);
    border-color: var(--brand-green-lighter);
    color: #ffffff;
}

.btn-outline-brand {
    border-color: var(--brand-green);
    color: var(--brand-green);
}

.btn-outline-brand:hover,
.btn-outline-brand:focus {
    background-color: var(--brand-green);
    color: #ffffff;
}

.btn-outline-brand:active {
    background-color: var(--brand-green-light);
    border-color: var(--brand-green-light);
    color: #ffffff;
}

/* --------------------------------------------------------------------------
   Form Focus States
   -------------------------------------------------------------------------- */

.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--brand-green);
    box-shadow: 0 0 0 0.2rem rgba(32, 45, 38, 0.25);
}

.form-check-input:checked {
    background-color: var(--brand-green);
    border-color: var(--brand-green);
}

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */

.table thead th {
    background-color: var(--brand-green);
    color: #ffffff;
    font-weight: 600;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.table tbody tr:hover {
    background-color: #f8f9fa;
}

.table {
    font-size: 0.875rem;
}

.table .badge {
    font-size: 0.75rem;
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.app-footer {
    background-color: #f8f9fa;
    padding: 1rem;
    text-align: center;
    color: #6c757d;
    font-size: 0.8rem;
    margin-top: 2rem;
    border-top: 1px solid var(--border-color);
}

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

.login-card {
    padding: 3rem;
}

.login-title {
    color: var(--brand-green);
    font-weight: 700;
}

.login-btn {
    width: 100%;
}

/* --------------------------------------------------------------------------
   Page Headers (title + action buttons)
   -------------------------------------------------------------------------- */

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.page-header-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* --------------------------------------------------------------------------
   Table action buttons — keep inline on all screen sizes
   -------------------------------------------------------------------------- */

.table .btn {
    width: auto;
    margin-bottom: 0;
}

.table td form.d-inline {
    display: inline !important;
}

/* --------------------------------------------------------------------------
   Responsive Design — Tablet (≤992px)
   -------------------------------------------------------------------------- */

@media (max-width: 992px) {
    .container-fluid {
        padding-left: 1rem;
        padding-right: 1rem;
    }
}

/* --------------------------------------------------------------------------
   Responsive Design — Mobile (≤768px)
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
    .card-body {
        padding: 1rem;
    }

    .card-body.p-4 {
        padding: 1rem !important;
    }

    .table-responsive {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .login-card {
        padding: 1.5rem;
    }

    /* Page headers stack vertically on mobile */
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        margin-bottom: 2.5rem;
    }

    .page-header-actions {
        width: 100%;
    }

    .page-header-actions .btn {
        flex: 1;
        text-align: center;
    }

    /* Filter forms — stack inputs and buttons */
    .filter-form .btn {
        width: auto;
    }

    /* Card headers with action buttons */
    .card-header.d-flex {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    /* Footer area in material tests table */
    .table tfoot .d-flex {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    /* Detail view inner columns — stack on mobile */
    .detail-grid .row > [class*="col-"] {
        margin-bottom: 0.5rem;
    }
}

/* --------------------------------------------------------------------------
   Responsive Design — Small Mobile (≤576px)
   -------------------------------------------------------------------------- */

@media (max-width: 576px) {
    body {
        font-size: 0.8125rem;
    }

    h2 {
        font-size: 1.375rem;
    }

    h5 {
        font-size: 1rem;
    }

    .container-fluid,
    .container {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }

    .card-body.p-4 {
        padding: 0.75rem !important;
    }

    .navbar-brand {
        font-size: 1rem;
    }

    /* Table font size reduction */
    .table {
        font-size: 0.8rem;
    }

    .table thead th {
        font-size: 0.7rem;
        padding: 0.4rem 0.35rem;
    }

    .table td {
        padding: 0.4rem 0.35rem;
    }

    .table .btn-sm {
        font-size: 0.7rem;
        padding: 0.2rem 0.4rem;
    }

    /* Badge sizing on small screens */
    .badge {
        font-size: 0.65rem;
    }

    /* Pagination responsive */
    .pagination .page-link {
        font-size: 0.8rem;
        padding: 0.35rem 0.65rem;
    }
}

/* --------------------------------------------------------------------------
   Wizard Step Indicator
   -------------------------------------------------------------------------- */

.wizard-indicator {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem 1rem;
    background: #fff;
    border-bottom: 1px solid var(--border-color);
    margin-bottom: 1.5rem;
}

.wizard-dot {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 2px solid var(--border-color);
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    font-weight: 600;
    color: #6c757d;
    cursor: pointer;
    transition: all 0.2s ease;
    flex-shrink: 0;
}

.wizard-dot.active {
    border-color: var(--brand-green);
    border-width: 2.5px;
    color: var(--brand-green);
}

.wizard-dot.completed {
    background-color: var(--brand-green);
    border-color: var(--brand-green);
    color: #fff;
    cursor: pointer;
}

.wizard-connector {
    height: 2px;
    flex: 1;
    background: var(--border-color);
    margin: 0 4px;
    max-width: 60px;
    transition: background 0.2s ease;
}

.wizard-connector.completed {
    background: var(--brand-green);
}

.wizard-step-label {
    text-align: center;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--brand-green);
    margin-top: 0.5rem;
}

.wizard-step {
    display: none;
}

.wizard-step.active {
    display: block;
}

.wizard-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 0;
    margin-top: 1.5rem;
    border-top: 1px solid var(--border-color);
}

.wizard-nav .wizard-nav-center {
    flex: 1;
    text-align: center;
}

.review-card {
    margin-bottom: 1rem;
}

.review-card .card-header {
    background: var(--brand-green);
    color: #fff;
    font-weight: 600;
    font-size: 0.85rem;
    padding: 0.5rem 1rem;
}

.review-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.35rem 0;
    border-bottom: 1px solid #f0f0f0;
}

.review-row:last-child {
    border-bottom: none;
}

.review-label {
    color: #6c757d;
    font-size: 0.85rem;
}

.review-value {
    font-weight: 500;
    font-size: 0.85rem;
    text-align: right;
}

.review-value.calculated {
    color: var(--brand-green);
    font-weight: 600;
}

.review-badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: 0.75rem;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    color: #fff;
}

.review-badge.pass { background: #198754; }
.review-badge.fail { background: #dc3545; }
.review-badge.pending { background: #6c757d; }

@media (max-width: 576px) {
    .wizard-dot {
        width: 28px;
        height: 28px;
        font-size: 0.7rem;
    }
    .wizard-connector {
        max-width: 30px;
    }
}

/* --------------------------------------------------------------------------
   Density Readings Table — mobile-friendly
   -------------------------------------------------------------------------- */

#density-readings-table th,
#density-readings-table td {
    white-space: nowrap;
}

#density-readings-table input.form-control-sm {
    min-width: 65px;
}

/* Scroll hint shadow on the right edge */
@media (max-width: 768px) {
    #density-readings-table input.form-control-sm {
        min-width: 75px;
        font-size: 16px; /* prevents iOS zoom on focus */
    }
}

/* ==========================================================================
   PHASE 1 — Lab-manual design system
   --------------------------------------------------------------------------
   IBM Plex typography, warm paper background, hairline rules. Layered on top
   of Bootstrap; nothing existing is removed. New components live under
   class names like .ledger / .section-header / .attention / .status-dot —
   adopted page-by-page in subsequent phases.
   ========================================================================== */

:root {
    --paper:        #faf9f5;
    --paper-2:      #f1efe8;
    --rule:         #d9d6cb;
    --rule-soft:    #e8e5dc;
    --ink:          #1a221d;
    --ink-soft:     #2d3a32;
    --muted:        #6b6f66;
    --pass:         #2f6e3d;
    --fail:         #b8331f;
    --warn:         #b76e0b;
    --info:         #3a5a8c;
    --slate-green:  #466e5a;
    --dusty-plum:   #6b4c6e;
    --soil-umber:   #7a5a3a;
    --muted-plum:   #6b4a6c;
    --green-tint:   rgba(32, 45, 38, 0.06);

    --font-sans:    'IBM Plex Sans', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    --font-mono:    'IBM Plex Mono', ui-monospace, 'JetBrains Mono', monospace;
    --font-serif:   'IBM Plex Serif', Georgia, serif;
}

/* Body — paper background, Plex Sans, slightly more confident size. */
body {
    font-family: var(--font-sans);
    background: var(--paper);
    color: var(--ink);
    font-size: 0.9375rem; /* 15px */
    font-feature-settings: 'tnum' 1, 'lnum' 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Reserve serif for the H1/H2 page-header headings — keeps existing h5/h6
   block headers (used by cards and table sections) on Plex Sans for
   consistency with the body. */
.page-header h1,
.page-header h2,
.meta-strip h1 {
    font-family: var(--font-serif);
    font-weight: 600;
    letter-spacing: -0.01em;
}

/* Tabular figures everywhere — measurements and numeric tables align. */
table, .ledger, .meta {
    font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Top navbar refresh — piggybacks on existing _navbar.html markup.
   No HTML change required; same Bootstrap collapse behavior on mobile.
   -------------------------------------------------------------------------- */

.navbar.bg-brand-dark {
    border-bottom: 3px solid var(--ink);
    padding: 0.4rem 1rem;
}
.navbar .navbar-brand {
    font-family: var(--font-serif);
    font-weight: 700;
    letter-spacing: 0.005em;
    font-size: 1.05rem;
}
.navbar .nav-link {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: rgba(255, 255, 255, 0.72);
    padding: 0.6rem 0.85rem;
}
.navbar .nav-link:hover {
    color: #ffffff;
}
.navbar .nav-link.active {
    color: #ffffff;
    border-bottom: 2px solid #ffffff;
    margin-bottom: -2px;
    font-weight: 600;
}
.navbar .dropdown-toggle.nav-link {
    text-transform: none;
    letter-spacing: 0.01em;
    font-size: 0.85rem;
}

/* --------------------------------------------------------------------------
   Page meta strip — replaces the old big <h2> + button-row pattern on the
   dashboard. Other pages keep using .page-header for now.
   -------------------------------------------------------------------------- */

.meta-strip {
    display: flex;
    align-items: baseline;
    gap: 1.5rem;
    margin: 0.5rem 0 2.25rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--rule);
    flex-wrap: wrap;
}
.meta-strip h1 {
    font-size: 2.1rem;
    line-height: 1.05;
    margin: 0;
}
.meta-strip .timestamp {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--muted);
}
.meta-strip .quicklinks {
    margin-left: auto;
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.quicklinks a, .quicklinks button {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    border: 1px solid var(--rule);
    padding: 0.4rem 0.8rem;
    border-radius: 2px;
    text-decoration: none;
    transition: border-color 0.15s, color 0.15s;
    background: none;
    cursor: pointer;
}
.quicklinks a:hover, .quicklinks button:hover {
    border-color: var(--brand-green);
    color: var(--brand-green);
}
/* Active page in a sub-nav — solid border + ink color so the user can
   tell at a glance which page they're on. Used by the Temperatures
   sub-nav (charts / sensors / alerts / notifications / settings). */
.quicklinks a.current {
    border-color: var(--ink);
    color: var(--ink);
    background: rgba(45, 63, 53, 0.04);
}

/* --------------------------------------------------------------------------
   Section headers — numbered editorial style with a horizontal rule.
   -------------------------------------------------------------------------- */

.section-header {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    margin: 2.25rem 0 1rem;
}
.section-header:first-child {
    margin-top: 0;
}
.section-num {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--muted);
    letter-spacing: 0.14em;
}
.section-title {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-weight: 600;
    color: var(--ink);
    margin: 0;
}
.section-rule {
    flex: 1;
    height: 1px;
    background: var(--rule);
}
.section-count {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--muted);
    letter-spacing: 0.08em;
    white-space: nowrap;
}
.section-action {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--brand-green);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
}
.section-action:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Domain tabs — used on the project view (testing_type=both) to switch
   between the asphalt material-tests ledger and the concrete pours table
   without reloading the page. Hash-based so links can deep-link to a tab
   and back/forward navigation preserves selection.
   -------------------------------------------------------------------------- */

.domain-tabs {
    display: flex;
    gap: 0;
    margin: 0 0 1.25rem;
    border-bottom: 1px solid var(--rule);
}
.domain-tab {
    appearance: none;
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    padding: 0.6rem 1.1rem;
    margin-bottom: -1px;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-weight: 500;
    color: var(--muted);
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.domain-tab:hover {
    color: var(--ink);
    background: var(--green-tint);
}
.domain-tab[aria-selected="true"] {
    color: var(--brand-green);
    border-bottom-color: var(--brand-green);
    font-weight: 600;
}
.domain-tab .tab-count {
    margin-left: 0.45rem;
    color: var(--muted);
    font-weight: 400;
}
.domain-tab[aria-selected="true"] .tab-count {
    color: var(--brand-green);
    opacity: 0.7;
}
.domain-pane {
    display: none;
}
.domain-pane.is-active {
    display: block;
}

/* Cylinder-set test chips — small click-through links from the pour view's
   cylinder-set table to each grouped material_test (one per age bucket).
   Status colour cues mirror the status-dot palette used elsewhere. */
.cyl-test-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 0.25rem;
    padding: 0.18rem 0.55rem;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 999px;
    background: var(--surface, #f7f6f1);
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-decoration: none;
    line-height: 1.4;
    transition: background 0.12s, border-color 0.12s;
}
.cyl-test-chip:hover {
    background: var(--green-tint);
    border-color: var(--brand-green);
    color: var(--brand-green);
    text-decoration: none;
}
.cyl-test-chip small {
    color: var(--muted);
    font-size: 0.85em;
}
.cyl-test-chip.status-complete,
.cyl-test-chip.status-reviewed,
.cyl-test-chip.status-delivered {
    background: rgba(47, 110, 61, 0.10);
    border-color: rgba(47, 110, 61, 0.45);
    color: #2f6e3d;
}
.cyl-test-chip.status-in_progress {
    background: rgba(58, 90, 140, 0.10);
    border-color: rgba(58, 90, 140, 0.45);
    color: #3a5a8c;
}
.cyl-test-chip.status-scheduled {
    background: rgba(184, 138, 31, 0.10);
    border-color: rgba(184, 138, 31, 0.45);
    color: #8a6817;
}

/* --------------------------------------------------------------------------
   Batch-weight calculator — collapsible panel under the Total weight
   input on a fresh-property event form. Each row reproduces a line off
   the batch-plant ticket; the calculator sums them and writes the
   result back to the Total field. See static/js/batch_weight.js.
   -------------------------------------------------------------------------- */

/* Edit panel for a fresh-property event. Rendered as a sibling of
 * the data table (not inside it), so the wide form-grid +
 * batch-weight calculator inside don't blow out the table's column
 * layout and trigger horizontal scrolling in the .table-responsive
 * wrapper. See templates/lab/pours/view.html for the structural
 * change that pulled this out of the table. */
.fresh-event-edit-panel {
    margin-top: 1.25rem;
    padding: 1rem 1.25rem 1.25rem;
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 6px;
    max-width: 100%;
    overflow-x: auto;
}
.fresh-event-edit-panel-header {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    margin-bottom: 1rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--rule-soft);
}
.fresh-event-edit-panel-header strong {
    color: var(--ink);
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.95rem;
}

.batch-weight-calc {
    margin-top: 0.4rem;
    border-top: 1px dashed var(--rule);
    padding-top: 0.4rem;
}
.batch-weight-toggle {
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--brand-green);
    user-select: none;
    padding: 0.25rem 0;
}
.batch-weight-toggle:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}
details[open] .batch-weight-toggle {
    margin-bottom: 0.5rem;
}
.batch-weight-body {
    padding: 0.5rem 0.75rem 0.75rem;
    background: var(--surface, #fbf9f3);
    border: 1px solid var(--rule);
    border-radius: 4px;
}
.batch-weight-hint {
    margin: 0 0 0.6rem;
    line-height: 1.4;
}
.batch-weight-hint code {
    background: rgba(0, 0, 0, 0.04);
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    font-size: 0.95em;
}

/* Unit legend — collapsed reference card under the hint paragraph.
   Two-column term/definition layout that reflows to one column on
   narrow screens. */
.batch-weight-units-help {
    margin: 0 0 0.75rem;
    font-size: 0.78rem;
}
.batch-weight-units-help > summary {
    cursor: pointer;
    color: var(--brand-green);
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    padding: 0.25rem 0;
    user-select: none;
}
.batch-weight-units-help > summary:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}
.batch-weight-units-help[open] > summary {
    margin-bottom: 0.5rem;
}
.batch-weight-units-legend {
    display: grid;
    grid-template-columns: minmax(5rem, max-content) 1fr;
    gap: 0.35rem 1rem;
    margin: 0;
    padding: 0.6rem 0.85rem;
    background: rgba(0, 0, 0, 0.02);
    border-left: 2px solid var(--rule);
    border-radius: 0 4px 4px 0;
    line-height: 1.45;
}
.batch-weight-units-legend dt {
    font-weight: 600;
    color: var(--ink);
    margin: 0;
}
.batch-weight-units-legend dt code {
    background: rgba(0, 0, 0, 0.05);
    padding: 0.05rem 0.35rem;
    border-radius: 3px;
    font-size: 0.95em;
}
.batch-weight-units-legend dd {
    margin: 0;
    color: var(--muted);
}
.batch-weight-units-legend dd code {
    background: rgba(0, 0, 0, 0.04);
    padding: 0.02rem 0.25rem;
    border-radius: 3px;
    font-size: 0.92em;
    color: var(--ink);
}
@media (max-width: 600px) {
    .batch-weight-units-legend {
        grid-template-columns: 1fr;
        gap: 0.15rem 0;
    }
    .batch-weight-units-legend dt {
        margin-top: 0.4rem;
    }
    .batch-weight-units-legend dt:first-child {
        margin-top: 0;
    }
}
.batch-weight-rows {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}
.batch-weight-rows thead th {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    font-weight: 500;
    padding: 0.35rem 0.4rem;
    border-bottom: 1px solid var(--rule);
    text-align: left;
}
.batch-weight-rows tbody td {
    padding: 0.25rem 0.35rem;
    vertical-align: middle;
}
.batch-weight-rows .bw-math {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--muted);
}
.batch-weight-rows .form-input-sm,
.batch-weight-rows .form-select-sm {
    padding: 0.25rem 0.4rem;
    font-size: 0.82rem;
    width: 100%;
}
.batch-weight-rows .bw-toggle-adv,
.batch-weight-rows .bw-remove {
    background: transparent;
    border: 0;
    padding: 0.1rem 0.3rem;
    color: var(--muted);
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1;
}
.batch-weight-rows .bw-toggle-adv:hover {
    color: var(--brand-green);
}
.batch-weight-rows .bw-toggle-adv.is-open {
    color: var(--brand-green);
}
.batch-weight-rows .bw-remove:hover {
    color: var(--fail, #b8331f);
}
.batch-weight-row-adv td.bw-adv-cell {
    background: rgba(0, 0, 0, 0.025);
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--rule);
}
.bw-adv-grid {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) auto minmax(10rem, 1fr);
    gap: 0.75rem 1rem;
    align-items: end;
}
.bw-adv-grid label {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin: 0;
}
.bw-cementitious-wrap {
    flex-direction: row !important;
    align-items: center;
    gap: 0.4rem !important;
}
.bw-cementitious-wrap input {
    margin: 0;
}
.batch-weight-actions {
    margin: 0.6rem 0 0.4rem;
}
.batch-weight-actions .btn-link {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--brand-green);
    background: transparent;
    border: 1px dashed var(--rule);
    padding: 0.3rem 0.7rem;
    border-radius: 4px;
    cursor: pointer;
}
.batch-weight-actions .btn-link:hover {
    border-color: var(--brand-green);
    background: var(--green-tint);
}
.batch-weight-summary {
    margin-top: 0.6rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--rule);
}
.batch-weight-cwt-line {
    font-family: var(--font-mono);
    margin-bottom: 0.4rem;
}
.batch-weight-total-line {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.batch-weight-total-label {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
}
.batch-weight-total-display {
    font-family: var(--font-mono);
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--ink);
}
.batch-weight-use-total {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    padding: 0.4rem 0.9rem;
    background: var(--brand-green);
    color: #fff;
    border: 0;
    border-radius: 4px;
    cursor: pointer;
}
.batch-weight-use-total:hover:not(:disabled) {
    background: var(--brand-green-light);
}
.batch-weight-use-total:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* --------------------------------------------------------------------------
   Ledger rows — replaces flat tables for list-of-tests displays.
   Each row is a structured composition; reflows on tablet/mobile.
   -------------------------------------------------------------------------- */

.ledger {
    display: flex;
    flex-direction: column;
    margin-bottom: 0;
}
.ledger-row {
    display: grid;
    /* minmax(0, 1fr) lets the context column actually shrink and truncate
     * its title with ellipsis. Vanilla `1fr` would refuse to collapse
     * below the content's intrinsic min-content size, producing the
     * "narrow but not narrow enough to reflow" dead zone around 900-1100px.
     * Engineer column trimmed from 200px -> minmax(140px, 180px). Status
     * column uses max-content since it's only a dot + 1-2 short words. */
    grid-template-columns:
        110px
        minmax(0, 1fr)
        minmax(140px, 180px)
        max-content
        120px;
    gap: 1.5rem;
    align-items: center;
    padding: 1rem 0.5rem;
    border-bottom: 1px solid var(--rule-soft);
    color: inherit;
    text-decoration: none;
    transition: background 0.12s;
}
.ledger-row:hover {
    background: var(--green-tint);
    text-decoration: none;
    color: inherit;
}
.ledger-row:last-child {
    border-bottom: 0;
}

.test-id {
    font-family: var(--font-mono);
    font-weight: 500;
    color: var(--ink);
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.test-id .num {
    font-size: 1.35rem;
    line-height: 1;
    letter-spacing: -0.02em;
}
.test-id .code {
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

.test-context {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}
.test-context .project {
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.test-context .meta {
    font-size: 0.83rem;
    color: var(--muted);
    display: flex;
    gap: 0.75rem;
    align-items: center;
    flex-wrap: wrap;
    /* Hide overflow so a single very long meta string truncates instead
     * of spilling outside the column. Individual spans stay nowrap so
     * "CDOT TAP M455-123" never breaks mid-token. */
    overflow: hidden;
}
.test-context .meta > * {
    white-space: nowrap;
}
.test-context .meta .sample {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--ink-soft);
}

.test-engineer {
    font-size: 0.88rem;
}
.test-engineer .name {
    font-weight: 500;
}
.test-engineer .role {
    display: block;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-top: 0.1rem;
}

.test-status {
    display: flex;
    align-items: center;
    /* Two flex children — .test-status-text (dot+label) on the left,
     * .pass-fail-pill on the right. space-between pins the pill to
     * the cell's right edge no matter how wide the status text is,
     * so pills line up across rows even when "In Progress" is wider
     * than "Complete". The pill never overflows because it's its own
     * flex item now (the previous flat structure made it spill past
     * the cell edge when content exceeded the column width). */
    justify-content: space-between;
    gap: 0.6rem;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-weight: 500;
    /* Allow the text-side group to shrink + ellipsis if a future
     * longer status label needs more room than the column gives. */
    min-width: 0;
}
.test-status .test-status-text {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}
.status-dot.in-progress { background: var(--info); box-shadow: 0 0 0 3px rgba(58,90,140,0.18); }
.status-dot.complete    { background: var(--pass); }
.status-dot.scheduled   { background: var(--muted); }
.status-dot.setup       { background: var(--rule); border: 1px solid var(--muted); }
.status-dot.overdue     { background: var(--fail); box-shadow: 0 0 0 3px rgba(184,51,31,0.18); }
.status-dot.reviewed    { background: var(--pass); }
.status-dot.delivered   { background: var(--info); }

/* Pass/fail rollup pill — additive to the status dot. Renders next to it
 * on each test row in the project view (Material Tests section) so the
 * spec result is scannable at a glance without opening the test detail.
 * Hidden when no rollup exists (test not performed yet, or insufficient
 * data to evaluate). */
.pass-fail-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid currentColor;
    border-radius: 999px;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}
.pass-fail-pill.pass {
    color: var(--pass);
    background: rgba(76, 122, 78, 0.08);
}
.pass-fail-pill.fail {
    color: var(--fail);
    background: rgba(184, 51, 31, 0.10);
}
.pass-fail-pill i {
    font-size: 0.75rem;
}

/* "Ready to close" pill — surfaced alongside (not instead of) the status
 * dot on in_progress tests whose per-type summary data is filled in. The
 * signal is computed by test_readiness::ready_to_close_ids — see that
 * module for the per-type predicates.
 *
 * Visually distinct from .pass-fail-pill (which conveys a verdict) and
 * from status dots (which convey workflow position): blue-purple to read
 * as "action prompt, not result." */
.ready-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid #5b6fb3;
    background: rgba(91, 111, 179, 0.10);
    color: #3d4d8f;
    border-radius: 999px;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}
.ready-pill i {
    font-size: 0.75rem;
}

/* Firmware-version pill on the Sensors page next to each node's FW
 * tag. Green = node reports the same version as the highest .bin in
 * the OTA dir; yellow = a newer version is available. Built on the
 * same dimensions as .pass-fail-pill so the two read consistently. */
.fw-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.05rem 0.45rem;
    border: 1px solid currentColor;
    border-radius: 999px;
    font-family: var(--font-mono);
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    line-height: 1.4;
    margin-left: 0.35rem;
    white-space: nowrap;
}
.fw-pill.latest {
    color: #2f6f31;
    background: rgba(76, 122, 78, 0.10);
}
.fw-pill.outdated {
    color: #8a6d1a;
    background: rgba(212, 167, 31, 0.14);
}

.test-due {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--ink);
    text-align: right;
}
.test-due small {
    display: block;
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-top: 0.15rem;
}
/* Inline delete button on a project ledger row — small + subtle until
   hover so it doesn't dominate the row visually. Sized to fit the
   schedule-date column without breaking the row's grid template. */
.ledger-row-delete {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    color: var(--muted);
    font-size: 0.85rem;
    line-height: 1;
    padding: 0.25rem 0.45rem;
    border-radius: 2px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.ledger-row-delete:hover {
    color: var(--fail);
    border-color: rgba(184, 51, 31, 0.4);
    background: rgba(184, 51, 31, 0.06);
}

/* --------------------------------------------------------------------------
   Attention block — overdue tests + expired certs + urgent equipment in
   one prioritized panel above the rest.
   -------------------------------------------------------------------------- */

.attention {
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-left: 3px solid var(--fail);
    padding: 1.25rem 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
}
.attention-row {
    display: grid;
    grid-template-columns: 140px 1fr auto;
    gap: 1.25rem;
    align-items: baseline;
    padding-bottom: 0.85rem;
    border-bottom: 1px dashed var(--rule);
}
.attention-row:last-child {
    padding-bottom: 0;
    border-bottom: 0;
}
.attention-tag {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-weight: 600;
    padding: 0.15rem 0;
    white-space: nowrap;
}
.attention-tag.overdue { color: var(--fail); }
.attention-tag.urgent  { color: var(--warn); }
.attention-tag.info    { color: var(--info); }

.attention-body {
    font-size: 0.95rem;
    min-width: 0;
}
.attention-body .label {
    font-weight: 600;
    color: var(--ink);
}
.attention-body small {
    color: var(--muted);
    margin-left: 0.5rem;
}

.attention-action {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--brand-green);
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
}
.attention-action:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Needs-Attention filter strip — sits between the section header and the
   .attention panel. Pure GET form: every chip is a submit button, every
   dropdown auto-submits onchange. URL stays bookmarkable + shareable; no
   client-side filter state to debug.

   Two rows: row 1 = primary toggles (Type, Scope) as chips with counts;
   row 2 = secondary multi-value pickers (Project / Engineer / PM / Sort).
   -------------------------------------------------------------------------- */

.attention-filters {
    margin-bottom: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}
.attention-filters-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.5rem;
}
.attention-filters-secondary {
    padding-top: 0.55rem;
    border-top: 1px dashed var(--rule);
    gap: 0.4rem 1.1rem;
}
.attention-filters-label {
    font-family: var(--font-mono);
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--muted);
    margin-right: 0.25rem;
}
.attention-chip {
    /* Rendered as an <a> link so each chip can preserve other active
     * filters via its own href, sidestepping the form-button-vs-hidden-
     * input value-conflict that broke chip clicks earlier. */
    display: inline-block;
    text-decoration: none;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--ink);
    background: transparent;
    border: 1px solid var(--rule);
    border-radius: 999px;
    padding: 0.3rem 0.75rem;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
    white-space: nowrap;
}
.attention-chip:hover {
    background: var(--paper-2);
}
.attention-chip.is-active {
    background: var(--ink);
    color: var(--paper);
    border-color: var(--ink);
}
.attention-chip small {
    margin-left: 0.35rem;
    font-size: 0.68rem;
    color: var(--muted);
    font-weight: 500;
}
.attention-chip.is-active small {
    color: var(--paper-2);
}
.attention-select {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
}
.attention-select span {
    font-weight: 600;
}
.attention-select select {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: none;
    letter-spacing: 0;
    color: var(--ink);
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--rule);
    border-radius: 0;
    padding: 0.15rem 0.25rem;
    cursor: pointer;
    transition: border-color 0.12s;
}
.attention-select select:hover,
.attention-select select:focus {
    border-bottom-color: var(--brand-green);
    outline: none;
}
.attention-clear {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    text-decoration: none;
    margin-left: auto;
}
.attention-clear:hover {
    color: var(--fail);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.attention-empty {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--muted);
    padding: 0.85rem 0;
    text-align: center;
}
.attention-empty a {
    color: var(--brand-green);
    text-decoration: none;
    margin-left: 0.5rem;
}
.attention-empty a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* --------------------------------------------------------------------------
   Weather strip — tighter version of the existing horizontal scroll.
   Used by the dashboard "Forecast" section.
   -------------------------------------------------------------------------- */

.weather-strip {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
    background: var(--rule);
    border: 1px solid var(--rule);
}
.weather-day {
    background: var(--paper);
    padding: 0.85rem 0.5rem;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}
.weather-day.today    { background: var(--green-tint); }
.weather-day.weekend  { background: rgba(0, 0, 0, 0.02); }
.weather-day .day-label {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink);
    font-weight: 500;
}
.weather-day .day-date {
    font-family: var(--font-mono);
    font-size: 0.62rem;
    color: var(--muted);
}
.weather-day .day-icon {
    color: var(--ink-soft);
    margin: 0.1rem 0;
}
.weather-day .day-icon i {
    font-size: 1.4rem;
}
.weather-day .day-temps {
    font-family: var(--font-mono);
    font-size: 0.92rem;
    font-weight: 500;
}
.weather-day .day-temps .lo {
    color: var(--muted);
    margin-left: 0.4rem;
}
.weather-day .day-precip,
.weather-day .day-wind {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--info);
}
.weather-day .day-wind { color: var(--muted); }
/* Visual cue for forecast amounts: snow gets stronger ink + bold so a
 * "1.2″ snow" day pops vs. a 60%-chance day; rain stays in the
 * standard info-blue tone. Both still feel restrained against the
 * lab-manual aesthetic. */
.weather-day .day-precip.snow {
    color: var(--ink);
    font-weight: 600;
}
.weather-day .day-precip.rain {
    color: var(--info);
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   Empty-state block — for sections with no rows. Lighter than a card.
   -------------------------------------------------------------------------- */

.empty-row {
    padding: 1rem 0.5rem;
    color: var(--muted);
    font-size: 0.9rem;
    font-style: italic;
    border-bottom: 1px solid var(--rule-soft);
}

/* --------------------------------------------------------------------------
   Responsive — tablet and phone reflows for the new ledger/attention/weather
   components. The classes above remain unchanged at desktop width.
   -------------------------------------------------------------------------- */

/* Intermediate breakpoint — kicks in BEFORE the squeeze zone (around
 * 900-1100px content width) where the 5-column desktop grid runs out
 * of room for the title cell. Reflows into a 3-column / 2-row layout:
 *   row 1: [id] [context        ] [status]
 *   row 2: [id] [engineer (client)] [due  ]
 * id spans both rows. Context gets the full row width minus status,
 * so the title has room to breathe even when client is verbose. */
@media (max-width: 1100px) {
    .ledger-row {
        grid-template-columns: 95px minmax(0, 1fr) max-content;
        grid-template-areas:
            "id  context   status"
            "id  engineer  due";
        row-gap: 0.45rem;
        column-gap: 1.25rem;
        align-items: start;
        padding: 1rem 0.4rem;
    }
    .ledger-row .test-id        { grid-area: id; padding-top: 0.15rem; }
    .ledger-row .test-context   { grid-area: context; }
    .ledger-row .test-engineer  { grid-area: engineer; font-size: 0.82rem; }
    .ledger-row .test-status    { grid-area: status; justify-self: end; }
    .ledger-row .test-due       { grid-area: due; justify-self: end; text-align: right; }
    .test-id .num               { font-size: 1.2rem; }
}

@media (max-width: 900px) {
    .ledger-row {
        grid-template-columns: 80px minmax(0, 1fr) auto;
        column-gap: 1rem;
        padding: 1rem 0.25rem;
    }
    .ledger-row .test-engineer  { font-size: 0.8rem; }
    .test-id .num               { font-size: 1.1rem; }
    .meta-strip h1              { font-size: 1.6rem; }
    .attention-row {
        grid-template-columns: 100px 1fr;
        grid-template-areas:
            "tag  body"
            "tag  action";
        row-gap: 0.4rem;
        column-gap: 1rem;
    }
    .attention-row .attention-tag    { grid-area: tag; }
    .attention-row .attention-body   { grid-area: body; }
    .attention-row .attention-action { grid-area: action; justify-self: start; }
}

@media (max-width: 640px) {
    .meta-strip {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
    }
    .meta-strip .quicklinks { margin-left: 0; }
    .ledger-row {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas:
            "id        status"
            "context   context"
            "engineer  due";
        row-gap: 0.4rem;
    }
    .test-id          { flex-direction: row; align-items: baseline; gap: 0.5rem; }
    .test-id .num     { font-size: 1rem; }
    .test-id .code    { font-size: 0.68rem; }
    .weather-strip {
        grid-template-columns: repeat(7, minmax(56px, 1fr));
        overflow-x: auto;
    }
    .navbar .nav-link {
        font-size: 0.85rem;
        letter-spacing: 0.08em;
        padding: 0.6rem 0;
    }
    .navbar .nav-link.active {
        border-bottom: 0;
    }
}

/* --------------------------------------------------------------------------
   Filter bar — slim, paper-flat replacement for the boxed Bootstrap form
   pattern. Used on /lab/projects (and any future list page).
   -------------------------------------------------------------------------- */

.filter-bar {
    margin: 0 0 1.25rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--rule-soft);
}
.filter-bar-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.filter-input {
    font-family: var(--font-sans);
    font-size: 0.85rem;
    color: var(--ink);
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.45rem 0.7rem;
    line-height: 1.2;
    outline: none;
    transition: border-color 0.15s, background 0.15s;
}
.filter-input::placeholder { color: var(--muted); }
.filter-input:focus {
    border-color: var(--brand-green);
    background: #ffffff;
}
.filter-search { flex: 1 1 240px; min-width: 200px; }
.filter-input.filter-year { max-width: 110px; }
select.filter-input {
    appearance: none;
    -webkit-appearance: none;
    background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                      linear-gradient(135deg, var(--muted) 50%, transparent 50%);
    background-position: calc(100% - 14px) center, calc(100% - 9px) center;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: 2rem;
    cursor: pointer;
}
.filter-btn {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #ffffff;
    background: var(--ink);
    border: 1px solid var(--ink);
    border-radius: 2px;
    padding: 0.55rem 1rem;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.filter-btn:hover { background: var(--brand-green); border-color: var(--brand-green); }
.filter-clear {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    text-decoration: none;
    padding: 0.55rem 0.4rem;
}
.filter-clear:hover { color: var(--ink); }

@media (max-width: 640px) {
    .filter-bar-row { flex-direction: column; align-items: stretch; }
    .filter-search, .filter-input, .filter-input.filter-year { max-width: 100%; }
    .filter-btn, .filter-clear { width: 100%; text-align: center; }
}

/* --------------------------------------------------------------------------
   Ledger pagination — slim mono prev/next footer that fits the design.
   Overrides the Bootstrap .pagination styling inside the lab list pages.
   -------------------------------------------------------------------------- */

.ledger-pagination {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule);
}
.ledger-pagination .page-meta {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
}
.ledger-pagination .page-controls {
    display: flex;
    gap: 0.5rem;
}
.ledger-pagination .page-link {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--ink);
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.5rem 0.9rem;
    text-decoration: none;
    transition: border-color 0.15s, color 0.15s;
}
.ledger-pagination .page-link:hover {
    border-color: var(--brand-green);
    color: var(--brand-green);
}
.ledger-pagination .page-link.disabled {
    color: var(--muted);
    pointer-events: none;
    background: transparent;
}

/* Stage-specific status dots used on the project ledger */
.status-dot.stage-initial    { background: var(--muted); opacity: 0.55; }
.status-dot.stage-proposal   { background: var(--rule); border: 1px solid var(--muted); }
.status-dot.stage-fieldwork  { background: var(--info); box-shadow: 0 0 0 3px rgba(58,90,140,0.18); }
.status-dot.stage-delivery   { background: var(--warn); box-shadow: 0 0 0 3px rgba(183,110,11,0.2); }
.status-dot.stage-completed  { background: var(--pass); }
.status-dot.stage-cancelled  { background: var(--fail); opacity: 0.55; }

/* --------------------------------------------------------------------------
   Phase 3 — project detail / edit / create patterns
   Ribbon header, definition-list brief, fieldbook form, sticky bulk bar.
   -------------------------------------------------------------------------- */

/* Ribbon header — single-line page header used on the project detail page */
.ribbon-meta {
    margin: 0.5rem 0 2rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--rule);
}
.ribbon-meta-top {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    flex-wrap: wrap;
}
.ribbon-num {
    font-family: var(--font-mono);
    /* Sized to read as a paired heading element with .ribbon-title
     * so the test number and the test type name carry equal visual
     * weight (mono at slightly smaller px reads similarly to serif). */
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--ink);
    letter-spacing: 0.04em;
}
.ribbon-title {
    font-family: var(--font-serif);
    font-weight: 600;
    font-size: 1.75rem;
    line-height: 1.05;
    margin: 0;
    letter-spacing: -0.01em;
    flex: 1;
    min-width: 280px;
    color: var(--ink);
}
.ribbon-actions {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
    flex-wrap: wrap;
}
.ribbon-meta-sub {
    margin-top: 0.6rem;
    display: flex;
    gap: 0.6rem 1.4rem;
    flex-wrap: wrap;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--muted);
    letter-spacing: 0.04em;
    align-items: center;
}
.ribbon-meta-sub strong {
    color: var(--ink);
    font-weight: 500;
}
.ribbon-meta-sub .delivery {
    margin-left: auto;
    color: var(--ink);
}
.ribbon-meta-sub .delivery small {
    color: var(--muted);
    margin-right: 0.3rem;
}
@media (max-width: 640px) {
    .ribbon-num { font-size: 1.3rem; }
    .ribbon-title { font-size: 1.5rem; min-width: auto; }
    .ribbon-meta-sub .delivery { margin-left: 0; }
}

/* Definition list — used for the project Brief section */
.detail-list {
    display: grid;
    grid-template-columns: 180px 1fr;
    gap: 0;
    margin-bottom: 0.5rem;
}
.detail-list dt,
.detail-list dd {
    margin: 0;
    padding: 0.75rem 0.5rem 0.75rem 0;
    border-bottom: 1px solid var(--rule-soft);
}
.detail-list dt {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--ink);
    padding-right: 1rem;
    display: flex;
    align-items: center;
}
.detail-list dd {
    color: var(--ink);
    padding-left: 0;
}
.detail-list dd small { color: var(--muted); }
.detail-list .detail-row-wide {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: 180px 1fr;
    border-bottom: 1px solid var(--rule-soft);
}
.detail-list .detail-row-wide dt,
.detail-list .detail-row-wide dd {
    border-bottom: 0;
}
.detail-mono {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--ink);
}
@media (max-width: 640px) {
    .detail-list { grid-template-columns: 1fr; }
    .detail-list dt {
        padding-bottom: 0;
        border-bottom: 0;
    }
    .detail-list dd {
        padding-top: 0.2rem;
        padding-bottom: 0.85rem;
    }
    .detail-list .detail-row-wide { grid-template-columns: 1fr; }
}

/* Ledger row variants — checkbox-led tests, mix designs, daily logs */
.ledger-row.with-check {
    grid-template-columns: 22px 110px 1fr 200px 200px 120px;
}
.ledger-check {
    width: 16px;
    height: 16px;
    accent-color: var(--brand-green);
    cursor: pointer;
}

.ledger-row.mix {
    grid-template-columns: 110px 1fr 220px 60px;
}
.ledger-row.mix .mix-aside {
    font-family: var(--font-mono);
    font-size: 0.83rem;
    color: var(--muted);
}
.ledger-row.mix .mix-arrow {
    text-align: right;
    font-family: var(--font-mono);
    color: var(--muted);
}

.ledger-row.log {
    grid-template-columns: 130px 80px 1fr 130px 200px;
}
.ledger-row.log .log-date {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 1.05rem;
    color: var(--ink);
}
.ledger-row.log .log-date small {
    display: block;
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-top: 0.15rem;
}
.ledger-row.log .log-tag {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    border: 1px solid var(--rule);
    padding: 0.15rem 0.45rem;
    border-radius: 2px;
    display: inline-block;
    justify-self: start;
    align-self: center;
}
.ledger-row.log .log-quantity {
    font-family: var(--font-mono);
    font-size: 0.9rem;
    color: var(--ink);
}
.ledger-row.log .log-quantity small {
    color: var(--muted);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    display: block;
}
.ledger-row.log .log-actions {
    display: flex;
    gap: 0.6rem;
    justify-content: flex-end;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    flex-wrap: wrap;
}
.ledger-row.log .log-actions a,
.ledger-row.log .log-actions button {
    color: var(--muted);
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    font: inherit;
    text-decoration: none;
}
.ledger-row.log .log-actions a:hover,
.ledger-row.log .log-actions button:hover {
    color: var(--brand-green);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.ledger-row.log .log-actions .reviewed { color: var(--pass); }

@media (max-width: 900px) {
    .ledger-row.with-check {
        grid-template-columns: 22px 80px 1fr auto;
        grid-template-areas:
            "check id   context  due"
            "check id   engineer status";
        column-gap: 0.75rem;
    }
    .ledger-row.with-check > .ledger-check { grid-area: check; align-self: center; }
    .ledger-row.mix {
        grid-template-columns: 80px 1fr auto;
        grid-template-areas:
            "id      context  arrow"
            "id      mixaside arrow";
        row-gap: 0.4rem;
    }
    .ledger-row.mix .test-id     { grid-area: id; }
    .ledger-row.mix .test-context { grid-area: context; }
    .ledger-row.mix .mix-aside    { grid-area: mixaside; }
    .ledger-row.mix .mix-arrow    { grid-area: arrow; align-self: center; }
    .ledger-row.log {
        grid-template-columns: 130px 1fr auto;
        grid-template-areas:
            "date    context  quantity"
            "tag     context  actions";
        row-gap: 0.4rem;
    }
    .ledger-row.log > .log-date     { grid-area: date; }
    .ledger-row.log > .log-tag      { grid-area: tag; }
    .ledger-row.log > .log-quantity { grid-area: quantity; text-align: right; }
    .ledger-row.log > .log-actions  { grid-area: actions; }
    .ledger-row.log > div:nth-child(3) { grid-area: context; }
}

/* Sticky bulk-action toolbar — appears beneath a section's ledger */
.bulk-bar {
    position: sticky;
    bottom: 1rem;
    margin-top: 1rem;
    padding: 0.75rem 1rem;
    background: var(--ink);
    color: #fff;
    display: flex;
    align-items: center;
    gap: 1rem;
    border-radius: 2px;
    box-shadow: 0 8px 24px rgba(26,34,29,0.18);
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    z-index: 10;
    flex-wrap: wrap;
}
.bulk-bar .bar-count { color: rgba(255,255,255,0.6); }
.bulk-bar .bar-context {
    color: rgba(255,255,255,0.6);
    flex: 1;
    min-width: 120px;
}
.bulk-bar .bar-actions {
    margin-left: auto;
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
}
.bulk-bar .bar-btn {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: #fff;
    background: rgba(255,255,255,0.08);
    border: 1px solid rgba(255,255,255,0.2);
    border-radius: 2px;
    padding: 0.45rem 0.8rem;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.bulk-bar .bar-btn:hover {
    background: var(--brand-green-light);
    text-decoration: none;
    color: #fff;
}
.bulk-bar .bar-btn[disabled],
.bulk-bar .bar-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.bulk-bar .bar-dropdown {
    position: relative;
}
.bulk-bar .bar-menu {
    position: absolute;
    bottom: calc(100% + 0.4rem);
    right: 0;
    background: #fff;
    color: var(--ink);
    border: 1px solid var(--rule);
    border-radius: 2px;
    min-width: 240px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.18);
    padding: 0.4rem 0;
    display: none;
}
.bulk-bar .bar-menu.show { display: block; }
.bulk-bar .bar-menu a {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.85rem;
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: 0.85rem;
    text-transform: none;
    letter-spacing: 0;
    text-decoration: none;
}
.bulk-bar .bar-menu a:hover {
    background: var(--green-tint);
    color: var(--brand-green);
}
.bulk-bar .bar-menu a.hidden { display: none; }

/* --------------------------------------------------------------------------
   Fieldbook form — used on project create/edit and reusable for other forms
   -------------------------------------------------------------------------- */

.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem 1.25rem;
    margin-bottom: 0.5rem;
}
.form-grid .full { grid-column: 1 / -1; }
@media (max-width: 640px) {
    .form-grid { grid-template-columns: 1fr; }
}

.form-label-mono {
    /* flex (vs the previous block) so single-line labels can stay
     * vertically centered in the reserved 2-line area, matching
     * .calc-field .calc-label so rows that mix the two cell patterns
     * keep their box-tops aligned even when one label wraps. */
    display: flex;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--ink);
    margin-bottom: 0.35rem;
    /* Reserve two lines so a long label (e.g. "Average Gmb
     * (Corrected)" on a 4-up grid at narrow widths) doesn't push
     * its sibling cells' inputs out of alignment. */
    min-height: 2lh;
}
.form-required {
    color: var(--brand-green);
    font-weight: 600;
    margin-left: 0.15rem;
}
.form-input,
.form-select,
.form-textarea {
    /* width: 100% + padding + border means the rendered box exceeds
     * the grid column unless we shift to border-box. Without this the
     * <input type="date"> on the ASPH-RICE perform page (and probably
     * every other 2-column form-grid) bleeds into the next column. */
    box-sizing: border-box;
    width: 100%;
    font-family: var(--font-sans);
    font-size: 0.95rem;
    color: var(--ink);
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.55rem 0.75rem;
    line-height: 1.3;
    outline: none;
    transition: border-color 0.15s, background 0.15s;
    /* Match .calc-field .calc-value min-height so a row that mixes
     * regular inputs with calc-fields (e.g. Basket Weight, Basket+Mix
     * Initial, Mix Initial Weight) lines up vertically instead of one
     * cell sitting taller than the others. */
    min-height: 44px;
}
/* Textareas wrap content vertically; min-height should not pin them
 * to a single-line size. Drop the constraint added above for them. */
.form-textarea { min-height: auto; }
.form-input::placeholder { color: var(--muted); }
.form-input:focus,
.form-select:focus,
.form-textarea:focus {
    border-color: var(--brand-green);
    background: #fff;
}
.form-textarea {
    min-height: 84px;
    resize: vertical;
    font-family: var(--font-sans);
}
.form-select {
    appearance: none;
    -webkit-appearance: none;
    background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                      linear-gradient(135deg, var(--muted) 50%, transparent 50%);
    background-position: calc(100% - 14px) center, calc(100% - 9px) center;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    padding-right: 2rem;
    cursor: pointer;
}
.form-help {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    margin-top: 0.3rem;
}

/* Address row — five small inputs in one row at desktop */
.address-row {
    display: grid;
    grid-template-columns: 1.4fr 0.8fr 1fr 70px 100px;
    gap: 0.5rem;
}
@media (max-width: 720px) {
    .address-row {
        grid-template-columns: 1fr 1fr;
        grid-template-areas:
            "s1 s1"
            "s2 s2"
            "city city"
            "state zip";
    }
    .address-row > :nth-child(1) { grid-area: s1; }
    .address-row > :nth-child(2) { grid-area: s2; }
    .address-row > :nth-child(3) { grid-area: city; }
    .address-row > :nth-child(4) { grid-area: state; }
    .address-row > :nth-child(5) { grid-area: zip; }
}

/* Inline subform panel — for the new-contact mini-form on project create */
.subform-panel {
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 1rem;
    margin-top: 0.5rem;
}
.subform-panel .form-grid { gap: 0.65rem 1rem; margin-bottom: 0; }
.subform-actions {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    margin-top: 0.6rem;
    align-items: center;
}
.subform-error {
    color: var(--fail);
    font-size: 0.8rem;
    margin-right: auto;
}

/* Inline label + button row */
.label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.35rem;
    flex-wrap: wrap;
}
.label-row .form-label-mono { margin-bottom: 0; }

/* Form action footer — sticky on mobile */
.form-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--rule);
    gap: 0.5rem;
}
@media (max-width: 640px) {
    .form-actions {
        position: sticky;
        bottom: 0;
        background: var(--paper);
        padding-bottom: 1rem;
        margin-bottom: 0;
        box-shadow: 0 -8px 16px -10px rgba(0,0,0,0.08);
    }
}

/* --------------------------------------------------------------------------
   Lab-manual button set — used alongside the existing Bootstrap btn-* set.
   Names are namespaced so they don't collide with .btn-primary etc.
   -------------------------------------------------------------------------- */

.btn-ink,
.btn-ghost,
.btn-quiet {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    border-radius: 2px;
    padding: 0.55rem 1rem;
    cursor: pointer;
    border: 1px solid transparent;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn-ink {
    background: var(--ink);
    color: #fff;
    border-color: var(--ink);
}
.btn-ink:hover {
    background: var(--brand-green);
    border-color: var(--brand-green);
    color: #fff;
    text-decoration: none;
}
.btn-ghost {
    background: transparent;
    color: var(--ink);
    border-color: var(--rule);
}
.btn-ghost:hover {
    border-color: var(--brand-green);
    color: var(--brand-green);
    text-decoration: none;
}
.btn-quiet {
    background: transparent;
    color: var(--muted);
    padding: 0.55rem 0.5rem;
    border-color: transparent;
}
.btn-quiet:hover {
    color: var(--ink);
    text-decoration: none;
}
.btn-tiny {
    font-size: 0.68rem;
    padding: 0.3rem 0.6rem;
    letter-spacing: 0.12em;
}

/* Narrow page container for forms (~760px reads better than full width) */
.container-narrow {
    max-width: 760px;
    margin: 0 auto;
    padding: 0 1rem;
}

/* --------------------------------------------------------------------------
   Phase 4 — client list / view / form patterns
   -------------------------------------------------------------------------- */

/* Client-list ledger row — name + address / contact / status / created */
.ledger-row.client {
    grid-template-columns: 1.5fr 1.2fr 130px 130px;
}
.ledger-row.client .client-id {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}
.ledger-row.client .client-name {
    font-weight: 600;
    font-size: 1.05rem;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ledger-row.client .client-loc {
    font-size: 0.83rem;
    color: var(--muted);
}
.ledger-row.client .client-contact {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    font-family: var(--font-mono);
    font-size: 0.82rem;
    color: var(--muted);
    min-width: 0;
}
.ledger-row.client .client-contact .email {
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ledger-row.client .client-contact .phone { color: var(--muted); }

@media (max-width: 900px) {
    .ledger-row.client {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "id      status"
            "contact created";
        row-gap: 0.4rem;
    }
    .ledger-row.client .client-id      { grid-area: id; }
    .ledger-row.client .client-contact { grid-area: contact; }
    .ledger-row.client .test-status    { grid-area: status; justify-self: end; }
    .ledger-row.client .test-due       { grid-area: created; }
}

/* Contact ledger row — used inside the client view */
.ledger-row.contact {
    grid-template-columns: 1.4fr 1fr 1fr 110px;
}
.ledger-row.contact .contact-name {
    font-weight: 600;
    color: var(--ink);
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.ledger-row.contact .contact-title {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    border: 1px solid var(--rule);
    padding: 0.1rem 0.4rem;
    border-radius: 2px;
    flex-shrink: 0;
}
.ledger-row.contact .contact-meta {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ledger-row.contact .contact-flag {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--brand-green);
    text-align: right;
}
.ledger-row.contact .contact-flag.inactive { color: var(--muted); }
@media (max-width: 720px) {
    .ledger-row.contact {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "name    flag"
            "email   email"
            "phone   phone";
        row-gap: 0.3rem;
    }
    .ledger-row.contact > div:nth-child(1) { grid-area: name; }
    .ledger-row.contact > div:nth-child(2) { grid-area: email; }
    .ledger-row.contact > div:nth-child(3) { grid-area: phone; }
    .ledger-row.contact .contact-flag      { grid-area: flag; }
}

/* Project ledger variant for the client-view "Projects" section.
   Reuses the standard .ledger-row grid; we just hide the engineer slot
   when not relevant. */
.ledger-row.project-on-client {
    grid-template-columns: 130px 1fr 150px 130px;
}

/* Inline duplicate-name hint shown on the client-create form */
.dup-hint {
    margin-top: 0.5rem;
    padding: 0.55rem 0.75rem;
    background: rgba(183,110,11,0.08);
    border: 1px solid rgba(183,110,11,0.4);
    border-radius: 2px;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--ink);
}
.dup-hint strong {
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--warn);
    font-size: 0.7rem;
    display: block;
    margin-bottom: 0.3rem;
}
.dup-hint ul {
    margin: 0;
    padding: 0;
    list-style: none;
}
.dup-hint li { padding: 0.15rem 0; }
.dup-hint a { color: var(--brand-green); }

/* Status pill — used inline in the ribbon for clients */
.status-pill {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}
.status-pill.active   { color: var(--pass); }
.status-pill.inactive { color: var(--muted); }

/* --------------------------------------------------------------------------
   Phase 5 — staff qualifications matrix + qualifications edit form
   -------------------------------------------------------------------------- */

.matrix-wrapper {
    overflow-x: auto;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
}
.matrix-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
    background: var(--paper);
}
.matrix-table thead th {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    font-weight: 500;
    background: var(--paper-2);
    border-bottom: 2px solid var(--rule);
    padding: 0.7rem 0.5rem;
    vertical-align: bottom;
    white-space: nowrap;
}
.matrix-table thead th.text-left { text-align: left; }
.matrix-table thead th.text-center { text-align: center; }
.matrix-table thead th.text-right { text-align: right; }
.matrix-table tbody td {
    padding: 0.55rem 0.5rem;
    border-bottom: 1px solid var(--rule-soft);
    vertical-align: middle;
}
.matrix-table tbody tr:last-child td { border-bottom: 0; }
.matrix-table tbody tr:hover { background: var(--green-tint); }
.matrix-table tbody tr.inactive { color: var(--muted); opacity: 0.55; }
.matrix-table .name-cell {
    font-weight: 600;
    color: var(--ink);
    min-width: 200px;
    max-width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.matrix-table .role-cell { white-space: nowrap; }

/* Sticky behavior:
   - The header row sticks to the top of the .matrix-wrapper so column
     labels stay visible as you scan down a long staff list.
   - The Name column sticks to the left edge so you can always see who
     the row belongs to while scrolling through capability + cert
     columns horizontally.
   The intersection cell (top-left) needs the highest z-index so it
   stays above both the row-sticky thead and the column-sticky tbody. */
.matrix-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    /* opaque background already set in the base thead rule above */
}
.matrix-table .name-cell {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--paper);
    /* Right-edge separator: 1px hairline + a soft shadow that fades
       outward, so the column visually divides from the scrolling
       content behind it without looking heavy. */
    box-shadow:
        inset -1px 0 0 var(--rule-soft),
        6px 0 6px -6px rgba(45, 63, 53, 0.18);
}
.matrix-table thead th:first-child {
    left: 0;
    z-index: 3;
    box-shadow:
        inset -1px 0 0 var(--rule-soft),
        6px 0 6px -6px rgba(45, 63, 53, 0.18);
}
/* Hover + inactive states need explicit overrides on the sticky cell
   because it carries its own background; without these the row-level
   background change wouldn't apply to the sticky column. */
.matrix-table tbody tr:hover .name-cell {
    background: var(--green-tint);
}
.matrix-table tbody tr.inactive .name-cell {
    background: var(--paper);
}

/* Capability dot (qualified vs not) */
.matrix-cap {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
}
.matrix-cap.yes { color: var(--brand-green); }
.matrix-cap.yes::before {
    content: "";
    width: 9px;
    height: 9px;
    background: var(--brand-green);
    border-radius: 50%;
}
.matrix-cap.no::before {
    content: "·";
    color: var(--rule);
    font-size: 1.1rem;
    line-height: 1;
}

/* Certification chip — color-coded by status */
.matrix-cert {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    padding: 0.18rem 0.5rem;
    border-radius: 2px;
    border: 1px solid var(--rule);
    background: var(--paper-2);
    white-space: nowrap;
}
.matrix-cert::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}
.matrix-cert.current  { color: var(--pass);  border-color: rgba(47,110,61,0.35);  background: rgba(47,110,61,0.06); }
.matrix-cert.current::before  { background: var(--pass); }
.matrix-cert.expiring { color: var(--warn);  border-color: rgba(183,110,11,0.4); background: rgba(183,110,11,0.06); }
.matrix-cert.expiring::before { background: var(--warn); }
.matrix-cert.expired  { color: var(--fail);  border-color: rgba(184,51,31,0.4); background: rgba(184,51,31,0.06); }
.matrix-cert.expired::before  { background: var(--fail); }
.matrix-cert.held     { color: var(--muted); }
.matrix-cert.held::before     { background: var(--muted); }

/* Role tag — small mono pill used inline */
.role-tag {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    border: 1px solid var(--rule);
    padding: 0.12rem 0.45rem;
    border-radius: 2px;
    background: var(--paper-2);
    white-space: nowrap;
}

/* Inline tiny edit link in matrix actions cell */
.matrix-edit {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    text-decoration: none;
}
.matrix-edit:hover { color: var(--brand-green); }

/* Legend block beneath the matrix */
.legend {
    margin-top: 1rem;
    padding: 0.75rem 0;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--muted);
    display: flex;
    gap: 1.25rem;
    flex-wrap: wrap;
    border-top: 1px solid var(--rule-soft);
}
.legend-item { display: inline-flex; align-items: center; gap: 0.4rem; }
.legend-item .dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
}
.legend-item .dot.current  { background: var(--pass); }
.legend-item .dot.expiring { background: var(--warn); }
.legend-item .dot.expired  { background: var(--fail); }
.legend-item .dot.held     { background: var(--muted); }

/* Capability checkbox grid — used on staff_edit */
.checkbox-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0.5rem 1rem;
}
.toggle-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.4rem 0;
    cursor: pointer;
}
.toggle-row input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--brand-green);
    flex-shrink: 0;
    margin: 0;
}
.toggle-row .toggle-label {
    font-size: 0.92rem;
    color: var(--ink);
}
.toggle-row .toggle-code {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-left: 0.4rem;
}

/* Certification edit row — checkbox label / date input / duration */
.cert-row {
    display: grid;
    grid-template-columns: 1fr 170px 100px;
    gap: 1rem;
    align-items: center;
    padding: 0.65rem 0;
    border-bottom: 1px solid var(--rule-soft);
}
.cert-row:last-child { border-bottom: 0; }
.cert-row .cert-duration {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
}
@media (max-width: 640px) {
    .cert-row {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "label duration"
            "date  date";
        row-gap: 0.4rem;
    }
    .cert-row .toggle-row     { grid-area: label; }
    .cert-row .form-input     { grid-area: date; max-width: none; }
    .cert-row .cert-duration  { grid-area: duration; justify-self: end; }
}

/* Section info — tiny mono helper text under section header */
.section-info {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--muted);
    margin: -0.3rem 0 1rem;
    line-height: 1.55;
    letter-spacing: 0.02em;
}

/* --------------------------------------------------------------------------
   Phase 6 — equipment list / view / create / edit, audit log restyle
   -------------------------------------------------------------------------- */

/* Equipment ledger row — type icon + name + assignee + status + added date */
.ledger-row.equipment {
    grid-template-columns: 32px 1fr 1fr 130px 130px;
}
.ledger-row.equipment .eq-icon {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.95rem;
    flex-shrink: 0;
}
.ledger-row.muted { opacity: 0.55; }
.ledger-row.equipment .eq-icon.truck {
    background: rgba(58,90,140,0.12);
    color: var(--info);
}
.ledger-row.equipment .eq-icon.gauge {
    background: rgba(183,110,11,0.12);
    color: var(--warn);
}
.ledger-row.equipment .eq-icon.tank {
    background: rgba(58,90,140,0.12);
    color: var(--info);
}
.ledger-row.equipment .eq-icon.oven {
    /* Terra-cotta — distinct from gauge (warn-amber) and from any
       --fail / --warn / --info / --pass slot, so an oven row doesn't
       read as an alert state next to its neighbors. */
    background: rgba(194,86,43,0.12);
    color: #c2562b;
}
.ledger-row.equipment .eq-icon.balance {
    /* Slate-green — distinct from gauge (warn-amber), tank/truck
       (info-blue), and any --fail / --warn / --pass slot, so a
       balance row doesn't read as an alert state next to its
       neighbors. */
    background: rgba(70,110,90,0.12);
    color: var(--slate-green);
}
.ledger-row.equipment .eq-icon.sg-rig {
    /* Deep cyan — reads as "water / submerged" without colliding
       with tank (info-blue) or any --fail / --warn / --pass slot.
       SG rigs sit submerged in a water bath; the hue echoes that
       use without aliasing the concrete-tank row. */
    background: rgba(33,103,128,0.12);
    color: #216780;
}
.ledger-row.equipment .eq-icon.gyratory {
    /* Muted teal — distinct from gauge (warn-amber), truck/tank
       (info-blue), and any --fail/--warn/--info/--pass slot, so a
       gyratory row doesn't read as an alert state next to its
       neighbors. */
    background: rgba(58,122,122,0.12);
    color: #3a7a7a;
}
.ledger-row.equipment .eq-icon.hveem {
    background: rgba(106,76,147,0.12);
    color: #6a4c93;
}
.ledger-row.equipment .eq-icon.bath {
    /* Brick red — leans warm to evoke heated baths (60°C Lottmans
       cure) while staying outside the tank (info-blue) and sg-rig
       (deep cyan) hues that already cover the cool-water lane.
       Distinct from --fail and from the oven terra-cotta. */
    background: rgba(184,51,31,0.10);
    color: #b8331f;
}
.ledger-row.equipment .eq-icon.se-shaker {
    /* Dusty-plum — distinct from balance (slate-green), oven
       (terra-cotta), gauge (warn-amber), and tank/truck (info-blue),
       and from any --fail / --warn / --pass slot so a shaker row
       doesn't read as an alert state next to its neighbors. */
    background: rgba(107,76,110,0.12);
    color: var(--dusty-plum);
}
.ledger-row.equipment .eq-icon.atterberg {
    /* Soil-umber — distinct from oven (terra-cotta), gauge
       (warn-amber), and balance (slate-green); evokes the soil-bench
       context where this apparatus lives. */
    background: rgba(122,90,58,0.12);
    color: var(--soil-umber);
}
.ledger-row.equipment .eq-icon.faa {
    /* Muted plum — distinct from every other equipment-type tile
       (truck/tank/gauge/oven/balance) and from any --fail / --warn
       / --info / --pass slot, so an FAA row doesn't read as an
       alert state next to its neighbors. */
    background: rgba(107,74,108,0.12);
    color: var(--muted-plum);
}
.ledger-row.equipment .eq-name {
    font-weight: 600;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ledger-row.equipment .eq-type {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    margin-top: 0.15rem;
}
.ledger-row.equipment .eq-assignee {
    font-size: 0.9rem;
    color: var(--ink);
}
.ledger-row.equipment .eq-assignee small {
    display: block;
    color: var(--muted);
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-top: 0.15rem;
}

@media (max-width: 900px) {
    .ledger-row.equipment {
        grid-template-columns: 32px 1fr auto;
        grid-template-areas:
            "icon  name      status"
            "icon  assignee  due";
        row-gap: 0.4rem;
    }
    .ledger-row.equipment > :nth-child(1) { grid-area: icon; }
    .ledger-row.equipment > :nth-child(2) { grid-area: name; }
    .ledger-row.equipment > :nth-child(3) { grid-area: assignee; }
    .ledger-row.equipment > :nth-child(4) { grid-area: status; justify-self: end; }
    .ledger-row.equipment > :nth-child(5) { grid-area: due; }
}

/* Inline input + unit suffix (e.g. mileage with "miles" suffix) */
.input-suffix {
    display: flex;
    align-items: stretch;
    gap: 0;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
    overflow: hidden;
}
.input-suffix:focus-within {
    border-color: var(--brand-green);
    background: #fff;
}
.input-suffix .form-input {
    border: 0;
    background: transparent;
    border-radius: 0;
    padding-right: 0.5rem;
}
.input-suffix .form-input:focus { background: transparent; }
.input-suffix .suffix-label {
    flex-shrink: 0;
    align-self: center;
    padding: 0 0.75rem;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    border-left: 1px solid var(--rule);
    background: var(--paper-2);
    height: 100%;
    display: flex;
    align-items: center;
}

/* Top-of-page alerts (overdue / expired / due-soon).
   Borrows the .attention-row pattern and applies it to a single row that
   sits above the page brief on truck_view / gauge_view. */
.alerts-stack {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0 0 1.5rem;
}
.alert-row {
    display: grid;
    grid-template-columns: 140px 1fr;
    gap: 1rem;
    align-items: center;
    padding: 0.75rem 1rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
    font-size: 0.9rem;
}
.alert-row .alert-tag {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.alert-row .alert-tag::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}
.alert-row.overdue { background: rgba(184,51,31,0.06); border-color: rgba(184,51,31,0.4); }
.alert-row.overdue .alert-tag { color: var(--fail); }
.alert-row.overdue .alert-tag::before { background: var(--fail); box-shadow: 0 0 0 3px rgba(184,51,31,0.18); }
.alert-row.warn { background: rgba(183,110,11,0.06); border-color: rgba(183,110,11,0.4); }
.alert-row.warn .alert-tag { color: var(--warn); }
.alert-row.warn .alert-tag::before { background: var(--warn); box-shadow: 0 0 0 3px rgba(183,110,11,0.18); }
/* When an alert row carries multiple issues (truck-view collapses
 * registration / insurance / oil-change into a single tagged row),
 * the body cell holds a <ul.alert-list> instead of a single span.
 * Tight vertical rhythm so 3 issues still fit visually as one row. */
.alert-row .alert-list {
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--ink);
}
.alert-row .alert-list li {
    margin: 0;
    padding: 0.1rem 0;
    line-height: 1.45;
}
.alert-row .alert-list li + li {
    margin-top: 0.1rem;
    border-top: 1px dashed rgba(0, 0, 0, 0.06);
    padding-top: 0.25rem;
}
/* When the alert row holds a list, drop the centered alignment so
 * the tag + list-top-edge land on the same baseline. */
.alert-row:has(.alert-list) {
    align-items: start;
}
.alert-row:has(.alert-list) .alert-tag {
    padding-top: 0.15rem;
}

/* Audit-log refresh — turns the partial into a numbered section ledger */
.audit-list {
    display: flex;
    flex-direction: column;
}
.audit-row {
    display: grid;
    grid-template-columns: 130px 90px 160px 1fr;
    gap: 1rem;
    align-items: start;
    padding: 0.75rem 0.5rem;
    border-bottom: 1px solid var(--rule-soft);
    font-size: 0.88rem;
}
.audit-row:last-child { border-bottom: 0; }
.audit-row .audit-when {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--muted);
}
.audit-row .audit-action {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    border: 1px solid var(--rule);
    padding: 0.15rem 0.5rem;
    border-radius: 2px;
    background: var(--paper-2);
    color: var(--ink);
    width: max-content;
    align-self: start;
}
.audit-row .audit-action.create { color: var(--pass); border-color: rgba(47,110,61,0.4); background: rgba(47,110,61,0.06); }
.audit-row .audit-action.update { color: var(--info); border-color: rgba(58,90,140,0.4); background: rgba(58,90,140,0.06); }
.audit-row .audit-action.delete { color: var(--fail); border-color: rgba(184,51,31,0.4); background: rgba(184,51,31,0.06); }
.audit-row .audit-user {
    color: var(--ink);
}
.audit-row .audit-detail {
    color: var(--ink);
}
.audit-row .audit-detail summary {
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    list-style: none;
}
.audit-row .audit-detail summary::-webkit-details-marker { display: none; }
.audit-row .audit-detail summary::before {
    content: "▸ ";
    margin-right: 0.2rem;
}
.audit-row .audit-detail[open] summary::before { content: "▾ "; }
.audit-row .audit-detail summary:hover { color: var(--brand-green); }
.audit-row .audit-detail pre {
    margin: 0.4rem 0 0;
    padding: 0.6rem 0.75rem;
    background: var(--paper-2);
    border: 1px solid var(--rule-soft);
    border-radius: 2px;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--ink);
    white-space: pre-wrap;
    max-height: 220px;
    overflow-y: auto;
}

@media (max-width: 720px) {
    .audit-row {
        grid-template-columns: 1fr 1fr;
        grid-template-areas:
            "when    action"
            "user    user"
            "detail  detail";
        row-gap: 0.4rem;
    }
    .audit-row .audit-when   { grid-area: when; }
    .audit-row .audit-action { grid-area: action; justify-self: end; }
    .audit-row .audit-user   { grid-area: user; }
    .audit-row .audit-detail { grid-area: detail; }
    .alert-row {
        grid-template-columns: 1fr;
    }
}

/* Service-card style mini stats — used on the truck "Oil Change Tracking" block */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 1rem;
    margin: 0.5rem 0 1.5rem;
}
.stat-tile {
    padding: 0.85rem 0.85rem 0.95rem;
    border: 1px solid var(--rule-soft);
    border-radius: 2px;
    background: var(--paper-2);
}
.stat-tile .stat-label {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    margin-bottom: 0.35rem;
}
.stat-tile .stat-value {
    font-family: var(--font-serif);
    font-size: 1.15rem;
    line-height: 1.15;
    color: var(--ink);
    font-weight: 600;
}
.stat-tile .stat-value.muted { color: var(--muted); font-weight: 400; font-style: italic; font-family: var(--font-sans); font-size: 1rem; }
.stat-tile .stat-value.warn  { color: var(--warn); }
.stat-tile .stat-value.fail  { color: var(--fail); }
.stat-tile .stat-value.pass  { color: var(--pass); }
/* Sub-line under .stat-value — used on the truck-view "Next Change
 * Due At" tile to show the miles-remaining delta and a calendar-date
 * fallback below the absolute target mileage. Keeps the headline
 * tight while still answering "and how far off is that?" inline. */
.stat-tile .stat-sub {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--muted);
    margin-top: 0.4rem;
}
.stat-tile .stat-pill.fail {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    border: 1px solid var(--fail);
    border-radius: 999px;
    color: var(--fail);
    background: rgba(184, 51, 31, 0.10);
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   Phase 7 — Schedule board: ribbon + tabs + kanban + heatmap
   -------------------------------------------------------------------------- */

/* Lock schedule-nav chevrons and date input so buttons don't drift */
.schedule-nav input[type="date"] {
    width: 9.5rem;
    text-align: center;
}
.schedule-nav .nav-btn {
    flex: 0 0 auto;
}

/* Ribbon variant for the schedule page — adds a date picker / nav controls */
.ribbon-meta .schedule-nav {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: auto;
    flex-wrap: wrap;
}
.ribbon-meta .schedule-nav .nav-btn {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    border: 1px solid var(--rule);
    padding: 0.4rem 0.65rem;
    border-radius: 2px;
    text-decoration: none;
    background: var(--paper-2);
    line-height: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.ribbon-meta .schedule-nav .nav-btn:hover {
    border-color: var(--brand-green);
    color: var(--brand-green);
    text-decoration: none;
}
.ribbon-meta .schedule-nav input[type="date"] {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    color: var(--ink);
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.4rem 0.6rem;
    line-height: 1;
}

/* Slim mono tab strip — replaces the Bootstrap btn-group view switcher */
.schedule-tabs {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 1rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--rule);
    flex-wrap: wrap;
}
.schedule-tabs a {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    text-decoration: none;
    padding: 0.55rem 0.85rem;
    border: 1px solid transparent;
    border-radius: 2px;
}
.schedule-tabs a:hover { color: var(--ink); }
.schedule-tabs a.active {
    color: var(--ink);
    border-color: var(--rule);
    background: var(--paper-2);
}
.schedule-tabs .filter-spacer { flex: 1; min-width: 0.5rem; }
.schedule-tabs select.filter-input {
    font-size: 0.78rem;
    padding: 0.35rem 1.8rem 0.35rem 0.6rem;
    background-position: calc(100% - 12px) center, calc(100% - 7px) center;
}

/* Read-only banner */
.viewing-only {
    margin: 0 0 1rem;
    padding: 0.55rem 0.85rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
}

/* --------------------------------------------------------------------------
   Schedule board (kanban) — engineer / project / type views all use it
   -------------------------------------------------------------------------- */

.schedule-board {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1rem;
}
.schedule-board.project-board,
.schedule-board.type-board {
    grid-template-columns: 1fr;
}
@media (max-width: 720px) {
    .schedule-board { grid-template-columns: 1fr; }
}

.schedule-column {
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.85rem 0.85rem 0.5rem;
    display: flex;
    flex-direction: column;
    min-height: 200px;
}
.schedule-column.unscheduled {
    background: var(--paper);
    border-style: dashed;
    border-color: var(--rule);
}
.schedule-column.full-width {
    background: transparent;
    border: 0;
    padding: 0;
}
.schedule-column.full-width .schedule-column-header {
    border-bottom-color: var(--rule);
    padding: 0.4rem 0 0.6rem;
}
.schedule-column.full-width .schedule-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 0.6rem;
    padding-top: 0.6rem;
}

.schedule-column-header {
    margin: 0 0 0.7rem;
    padding-bottom: 0.55rem;
    border-bottom: 1px solid var(--rule-soft);
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.schedule-column-header .col-name {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--ink);
    font-weight: 600;
}
.schedule-column-header .col-name .col-sub {
    color: var(--muted);
    font-weight: 400;
    margin-left: 0.35rem;
}
.schedule-column-header .col-meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
}
.schedule-column-header .col-warn {
    color: var(--fail);
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
.schedule-column-header .col-warn::before {
    content: "";
    width: 6px;
    height: 6px;
    background: var(--fail);
    border-radius: 50%;
}

/* Load bar — normalized 0..1 against the busiest column for the day */
.load-bar {
    width: 100%;
    height: 4px;
    background: var(--rule-soft);
    border-radius: 2px;
    overflow: hidden;
    position: relative;
}
.load-bar > span {
    display: block;
    height: 100%;
    background: var(--brand-green);
    transition: width 0.25s ease;
}
.load-bar.has-warn > span { background: var(--fail); }
.load-bar.empty > span { width: 0; }

.schedule-cards {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    flex: 1;
}
.schedule-empty {
    margin-top: 0.4rem;
    padding: 1rem 0.5rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-style: italic;
    text-align: center;
}

/* --------------------------------------------------------------------------
   Schedule card — paper-flat, mono code as anchor, qualification chip
   -------------------------------------------------------------------------- */

.schedule-card {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.7rem 0.75rem 0.65rem;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.schedule-card:hover {
    border-color: var(--ink);
    box-shadow: 0 2px 8px rgba(26,34,29,0.08);
}
.schedule-card.has-warn {
    border-left: 3px solid var(--fail);
    padding-left: 0.65rem;
}
.schedule-card.has-warn:hover {
    border-color: var(--fail);
    box-shadow: 0 2px 8px rgba(184,51,31,0.15);
}

.schedule-card .card-top {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.45rem;
}
.schedule-card .card-code {
    font-family: var(--font-mono);
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--ink);
    letter-spacing: 0.02em;
    line-height: 1;
}
.schedule-card .card-code a {
    color: inherit;
    text-decoration: none;
}
.schedule-card .card-code a:hover { color: var(--brand-green); text-decoration: none; }
.schedule-card .card-num {
    font-family: var(--font-mono);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--ink);
    letter-spacing: -0.01em;
    line-height: 1;
}
.schedule-card .card-status {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    font-weight: 500;
}
.schedule-card .card-status .status-dot { width: 6px; height: 6px; box-shadow: none; }

.schedule-card .card-project {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--ink);
    line-height: 1.3;
    margin: 0.1rem 0 0.05rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.schedule-card .card-meta {
    font-size: 0.78rem;
    color: var(--muted);
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.schedule-card .card-meta .sep { margin: 0 0.35rem; opacity: 0.5; }

/* Qualification chip — green ✓ when qualified, amber/red when missing */
.qual-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 0.2rem 0.5rem;
    border-radius: 2px;
    margin-top: 0.5rem;
    border: 1px solid var(--rule);
    background: var(--paper-2);
}
.qual-chip::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    flex-shrink: 0;
}
.qual-chip.ok { color: var(--pass); border-color: rgba(47,110,61,0.3); background: rgba(47,110,61,0.05); }
.qual-chip.ok::before { background: var(--pass); }
.qual-chip.warn { color: var(--fail); border-color: rgba(184,51,31,0.4); background: rgba(184,51,31,0.06); }
.qual-chip.warn::before { background: var(--fail); }
.qual-chip.unassigned { color: var(--muted); }
.qual-chip.unassigned::before { background: var(--muted); }

/* Inline reassign panel — toggled by the "+ Reassign" link */
.reassign-toggle {
    display: inline-block;
    margin-top: 0.55rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    cursor: pointer;
    background: transparent;
    border: 0;
    padding: 0;
}
.reassign-toggle:hover { color: var(--brand-green); }
.reassign-panel {
    display: none;
    margin-top: 0.55rem;
    padding: 0.55rem 0.6rem;
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
}
.reassign-panel.open { display: block; }
.reassign-panel .form-grid {
    grid-template-columns: 1fr;
    gap: 0.45rem;
    margin-bottom: 0.45rem;
}
.reassign-panel .form-input,
.reassign-panel .form-select {
    font-size: 0.82rem;
    padding: 0.4rem 0.55rem;
}
.reassign-panel select.form-select {
    background-position: calc(100% - 12px) center, calc(100% - 7px) center;
}
.reassign-panel .reassign-actions {
    display: flex;
    gap: 0.4rem;
    justify-content: flex-end;
}
.reassign-panel .reassign-actions button { padding: 0.35rem 0.7rem; font-size: 0.68rem; }

/* --------------------------------------------------------------------------
   Week view — heatmap of (engineer, day) cells
   -------------------------------------------------------------------------- */

.heatmap-wrapper {
    overflow-x: auto;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
}
.heatmap-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--paper);
    table-layout: fixed;
}
.heatmap-table thead th {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    font-weight: 500;
    background: var(--paper-2);
    border-bottom: 2px solid var(--rule);
    padding: 0.6rem 0.5rem;
    text-align: center;
    white-space: nowrap;
}
.heatmap-table thead th.engineer-col {
    text-align: left;
    width: 200px;
}
.heatmap-table tbody th.engineer-cell {
    text-align: left;
    padding: 0.7rem 0.85rem;
    background: var(--paper-2);
    border-bottom: 1px solid var(--rule-soft);
    font-family: var(--font-sans);
    font-weight: 600;
    color: var(--ink);
    font-size: 0.92rem;
    width: 200px;
    vertical-align: top;
}
.heatmap-table tbody th.engineer-cell .total {
    display: block;
    margin-top: 0.2rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-weight: 400;
    color: var(--muted);
}
.heatmap-table tbody td {
    padding: 0;
    border-bottom: 1px solid var(--rule-soft);
    border-left: 1px solid var(--rule-soft);
    vertical-align: top;
}
.heatmap-table tbody tr:last-child td,
.heatmap-table tbody tr:last-child th { border-bottom: 0; }
.heatmap-table tbody td.other-divider {
    background: var(--paper-2);
    padding: 0.5rem 0.85rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
}

.heatmap-cell {
    display: block;
    text-decoration: none;
    color: inherit;
    padding: 0.65rem 0.6rem 0.7rem;
    min-height: 70px;
    transition: background 0.12s;
}
.heatmap-cell:hover {
    background: var(--green-tint);
    text-decoration: none;
    color: inherit;
}
.heatmap-cell .cell-count {
    font-family: var(--font-mono);
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--ink);
    line-height: 1;
    letter-spacing: -0.01em;
}
.heatmap-cell .cell-count.zero { color: var(--rule); font-weight: 400; }
.heatmap-cell .cell-bar {
    margin-top: 0.4rem;
    width: 100%;
    height: 3px;
    background: var(--rule-soft);
    border-radius: 2px;
    overflow: hidden;
}
.heatmap-cell .cell-bar > span {
    display: block;
    height: 100%;
    background: var(--brand-green);
}
.heatmap-cell .cell-bar.has-warn > span { background: var(--fail); }
.heatmap-cell .cell-warn-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--fail);
    margin-left: 0.35rem;
    vertical-align: middle;
    box-shadow: 0 0 0 2px rgba(184,51,31,0.18);
}
.heatmap-hint {
    margin-top: 0.85rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
}

/* --------------------------------------------------------------------------
   List view — uses the existing ledger pattern but with our schedule columns
   -------------------------------------------------------------------------- */

.ledger-row.schedule-list {
    grid-template-columns: 110px 1fr 200px 130px 160px;
}
.ledger-row.schedule-list.has-warn {
    border-left: 3px solid var(--fail);
    padding-left: 0.5rem;
}

/* --------------------------------------------------------------------------
   Phase 9 — Auth pages (login, forgot password, reset password, accept invite)
   These pages don't have a navbar; they're a quiet centered card on paper.
   -------------------------------------------------------------------------- */

.auth-shell {
    min-height: calc(100vh - 80px);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 4rem 1rem 2rem;
}
.auth-card {
    width: 100%;
    max-width: 420px;
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 2.25rem 2rem 2rem;
    position: relative;
}
.auth-card.wide { max-width: 520px; }

/* Lab-manual brand mark — small mono prefix, serif name */
.auth-brand {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    justify-content: center;
    margin-bottom: 1.5rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--rule);
}
.auth-brand .auth-mark {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--muted);
}
.auth-brand .auth-name {
    font-family: var(--font-serif);
    font-weight: 700;
    font-size: 1rem;
    color: var(--ink);
    letter-spacing: 0.005em;
}

.auth-title {
    font-family: var(--font-serif);
    font-weight: 600;
    font-size: 1.6rem;
    color: var(--ink);
    margin: 0 0 0.4rem;
    line-height: 1.15;
    letter-spacing: -0.01em;
    text-align: center;
}
.auth-subtitle {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    margin: 0 0 1.75rem;
    text-align: center;
}

/* Flash messages styled to match the lab-manual color tokens */
.auth-flash {
    margin: 0 0 1.25rem;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
    font-size: 0.88rem;
    color: var(--ink);
    line-height: 1.45;
}
.auth-flash.success {
    border-color: rgba(47,110,61,0.4);
    background: rgba(47,110,61,0.06);
    color: var(--pass);
}
.auth-flash.error,
.auth-flash.danger {
    border-color: rgba(184,51,31,0.4);
    background: rgba(184,51,31,0.06);
    color: var(--fail);
}
.auth-flash.warning {
    border-color: rgba(183,110,11,0.4);
    background: rgba(183,110,11,0.06);
    color: var(--warn);
}

/* Auth form fields — slightly more vertical breathing room than fieldbook */
.auth-card .form-grid { gap: 0.85rem 1rem; margin-bottom: 1.1rem; }
.auth-card .form-label-mono { margin-bottom: 0.4rem; }
.auth-card .form-input { padding: 0.65rem 0.8rem; }
.auth-card .auth-help {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    margin-top: 0.35rem;
    line-height: 1.4;
}
.auth-card .form-check-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin: -0.3rem 0 1.1rem;
    cursor: pointer;
    font-size: 0.85rem;
}
.auth-card .form-check-row input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--brand-green);
    margin: 0;
}

.auth-submit {
    width: 100%;
    justify-content: center;
    padding: 0.75rem 1rem;
    font-size: 0.78rem;
    letter-spacing: 0.16em;
}

/* Footer links — small mono caps */
.auth-footer {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rule-soft);
    text-align: center;
    display: flex;
    justify-content: center;
    gap: 1.2rem;
    flex-wrap: wrap;
}
.auth-footer a {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    text-decoration: none;
}
.auth-footer a:hover { color: var(--brand-green); }

/* Flash banner — shared across every page, rendered by base.html.
   Replaces the old Bootstrap .alert pattern with a lab-manual treatment
   that matches the warm-paper aesthetic. */
.flash-banner {
    margin: 0 0 1rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--paper-2);
    font-size: 0.9rem;
    color: var(--ink);
    display: flex;
    align-items: center;
    gap: 0.75rem;
    line-height: 1.45;
}
.flash-banner .flash-text { flex: 1; }
.flash-banner .flash-dismiss {
    background: transparent;
    border: 0;
    color: inherit;
    cursor: pointer;
    font-size: 1.15rem;
    line-height: 1;
    padding: 0 0.25rem;
    opacity: 0.5;
}
.flash-banner .flash-dismiss:hover { opacity: 1; }
.flash-banner.success {
    border-color: rgba(47,110,61,0.4);
    background: rgba(47,110,61,0.06);
    color: var(--pass);
}
.flash-banner.error,
.flash-banner.danger {
    border-color: rgba(184,51,31,0.4);
    background: rgba(184,51,31,0.06);
    color: var(--fail);
}
.flash-banner.warning {
    border-color: rgba(183,110,11,0.4);
    background: rgba(183,110,11,0.06);
    color: var(--warn);
}
.flash-banner.info {
    border-color: rgba(58,90,140,0.4);
    background: rgba(58,90,140,0.06);
    color: var(--info);
}

/* Page footer — quiet mono caps band */
.lab-footer {
    margin-top: 4rem;
    padding: 1.25rem 1rem;
    border-top: 1px solid var(--rule);
    text-align: center;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
}

/* --------------------------------------------------------------------------
   Phase 10 — Sieve table (read-only and edit variants)
   Used on mix_design_view, mix_design_create, mix_design_edit (and later
   the sieve table inside test perform/edit pages).
   -------------------------------------------------------------------------- */

.sieve-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--paper);
    margin: 0.5rem 0;
}
.sieve-table thead th {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    font-weight: 500;
    background: var(--paper-2);
    border-bottom: 2px solid var(--rule);
    padding: 0.65rem 0.75rem;
    text-align: left;
}
.sieve-table thead th.numeric { text-align: right; }
.sieve-table tbody td {
    padding: 0.55rem 0.75rem;
    border-bottom: 1px solid var(--rule-soft);
    vertical-align: middle;
    font-size: 0.9rem;
}
.sieve-table tbody tr:last-child td { border-bottom: 0; }
.sieve-table tbody td.sieve-label {
    font-family: var(--font-mono);
    font-weight: 500;
    color: var(--ink);
    width: 110px;
}
.sieve-table tbody td.numeric {
    font-family: var(--font-mono);
    text-align: right;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
.sieve-table tbody td .empty-dash {
    color: var(--rule);
}
.sieve-table.editable tbody td {
    padding: 0.35rem 0.5rem;
}
.sieve-table.editable tbody td.sieve-label {
    padding-left: 0.75rem;
}
.sieve-table.editable input.form-input {
    padding: 0.35rem 0.55rem;
    font-size: 0.85rem;
    text-align: right;
    font-family: var(--font-mono);
}
.sieve-table.editable input.form-input.spec-input {
    text-align: left;
}

/* Identity preview — used on accept_invite to show the email/role from the token */
.auth-identity {
    display: grid;
    grid-template-columns: 60px 1fr;
    row-gap: 0.4rem;
    column-gap: 0.85rem;
    align-items: center;
    margin: 0 0 1.25rem;
    padding: 0.85rem;
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
}
.auth-identity dt {
    font-family: var(--font-mono);
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    margin: 0;
}
.auth-identity dd {
    margin: 0;
    color: var(--ink);
    font-size: 0.9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Test perform pages — sticky action bar, field mode, draft banner, toast.
   Shared across every per-type perform template (rice, density, gyratory,
   ignition).
   -------------------------------------------------------------------------- */

body.has-test-action-bar {
    padding-bottom: 96px;
}

.test-action-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--paper);
    border-top: 1px solid var(--rule);
    box-shadow: 0 -8px 16px -10px rgba(0, 0, 0, 0.08);
    padding: 0.75rem 1rem;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    z-index: 30;
}
.test-action-bar .bar-status {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
}
.test-action-bar .bar-status .status-dot { width: 8px; height: 8px; }
.test-action-bar .last-saved {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--muted);
    text-transform: lowercase;
    letter-spacing: 0.04em;
}
.test-action-bar .bar-spacer { flex: 1; }
.test-action-bar .field-mode-toggle {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
    background: transparent;
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.5rem 0.85rem;
    cursor: pointer;
}
.test-action-bar .field-mode-toggle:hover {
    border-color: var(--ink);
    color: var(--ink);
}
.test-action-bar .field-mode-toggle[aria-pressed="true"] {
    background: var(--ink);
    color: #fff;
    border-color: var(--ink);
}
.test-action-bar .save-buttons {
    display: flex;
    gap: 0.5rem;
}
.test-action-bar .save-buttons button,
.test-action-bar .save-buttons .btn-ink {
    min-height: 44px;
    padding: 0.55rem 1.1rem;
}

@media (max-width: 720px) {
    .test-action-bar {
        flex-wrap: wrap;
        gap: 0.5rem;
        padding: 0.6rem 0.75rem;
    }
    .test-action-bar .field-mode-toggle { order: 4; }
    .test-action-bar .save-buttons { width: 100%; order: 5; }
    .test-action-bar .save-buttons button,
    .test-action-bar .save-buttons .btn-ink { flex: 1; }
    body.has-test-action-bar { padding-bottom: 140px; }
}

body.test-field-mode {
    background: #ffffff;
    color: #000000;
}
body.test-field-mode .form-input,
body.test-field-mode .form-select,
body.test-field-mode .form-textarea,
body.test-field-mode input.form-control,
body.test-field-mode select.form-select,
body.test-field-mode textarea.form-control {
    background: #ffffff;
    color: #000000;
    border: 2px solid #000000;
    font-size: 1.05rem;
    min-height: 44px;
    padding: 0.55rem 0.75rem;
}
body.test-field-mode .form-label-mono,
body.test-field-mode label.form-label {
    color: #000000;
    font-size: 0.78rem;
    font-weight: 600;
}
body.test-field-mode .test-action-bar {
    background: #ffffff;
    border-top: 3px solid #000000;
}
body.test-field-mode .ribbon-meta-sub,
body.test-field-mode .form-help,
body.test-field-mode .section-info {
    color: #444;
}
body.test-field-mode .section-title {
    color: #000;
    font-size: 0.85rem;
}
body.test-field-mode .btn-ink {
    background: #000000;
    border-color: #000000;
    font-size: 0.85rem;
}

.draft-banner {
    margin: 0 0 1.25rem;
    padding: 0.75rem 1rem;
    background: rgba(58, 90, 140, 0.06);
    border: 1px solid rgba(58, 90, 140, 0.4);
    border-radius: 2px;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
}
.draft-banner .draft-icon {
    color: var(--info);
    font-size: 1.1rem;
}
.draft-banner .draft-message {
    flex: 1;
    color: var(--ink);
    font-size: 0.9rem;
}
.draft-banner .draft-message strong {
    font-family: var(--font-mono);
    font-size: 0.78rem;
}
.draft-banner .draft-actions {
    display: flex;
    gap: 0.5rem;
}

.test-toast {
    position: fixed;
    left: 50%;
    bottom: 100px;
    transform: translateX(-50%) translateY(20px);
    background: var(--ink);
    color: #fff;
    padding: 0.75rem 1.4rem;
    border-radius: 2px;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.18s, transform 0.18s;
    z-index: 50;
}
.test-toast.show {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}
.test-toast[data-level="error"] { background: var(--fail); }
.test-toast[data-level="warn"]  { background: var(--warn); }
.test-toast[data-level="success"] { background: var(--pass); }

.calc-field {
    display: flex;
    flex-direction: column;
    /* Matches the .form-label-mono margin-bottom so calc-field cells
     * and regular label/input cells share a vertical baseline when
     * they sit in the same .form-grid row. */
    gap: 0.35rem;
}
.calc-field .calc-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    /* Mirror .form-label-mono so the labels read with the same weight
     * and ink-color across both cell patterns. */
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--ink);
    /* Reserve two lines so a label that wraps at narrow widths
     * (e.g. "Average Gmb (Corrected)" on a 4-up grid) doesn't push
     * its sibling cells' boxes out of alignment. Single-line labels
     * stay vertically centered in the reserved area — equal whitespace
     * above and below, so the cell still feels balanced, while
     * 2-line labels fill the area edge-to-edge. The box-top is then
     * consistent across the row regardless of label length. */
    min-height: 2lh;
}
.calc-field .calc-label .bi-calculator {
    color: var(--brand-green);
    /* Inherit the label's font-size (0.7rem) so the icon's line-box
     * doesn't expand the flex container past .form-label-mono's
     * natural text-only height. Using a font-size larger than the
     * surrounding text was pushing every calc-field cell ~3-4px
     * lower than its sibling form-input cells in the same row. */
    font-size: 1em;
}
.calc-field .calc-value {
    font-family: var(--font-mono);
    font-size: 1.1rem;
    font-weight: 600;
    /* Cap line-height so the content doesn't grow past the shared
     * 44px min-height that .form-input also uses. Without this,
     * font-size 1.1rem at default 1.5 line-height pushed the box to
     * 46px and the row's baseline cracked. */
    line-height: 1.3;
    color: var(--ink);
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.55rem 0.75rem;
    min-height: 44px;
    display: flex;
    align-items: center;
}
.calc-field .calc-formula {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    color: var(--muted);
    line-height: 1.45;
    word-break: break-word;
}
@media (max-width: 720px) {
    .calc-field .calc-formula { display: none; }
    .calc-field .calc-formula.show-on-mobile { display: block; }
}


/* ====================================================================
 * Density-test (ASPH-COMP) reading cards — Phase 2.
 * Each spot reading lives in its own card with a head, location row,
 * 4 density inputs, and a calc row showing avg + compaction %.
 * ==================================================================== */
.reading-card {
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.85rem 1rem;
    margin-bottom: 0.85rem;
    background: var(--paper);
}
.reading-card.out-of-spec {
    border-color: var(--fail);
    background: var(--paper-2);
    box-shadow: inset 3px 0 0 var(--fail);
}
.reading-card-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.6rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--rule);
}
.reading-card-head strong {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    color: var(--ink);
}
.reading-card-head .gps-btn {
    margin-left: auto;
}
.reading-card-head .gps-display {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--muted);
}
.reading-card-head .gps-display a {
    color: var(--brand-green);
    text-decoration: none;
}
.reading-card-head .gps-display a:hover { text-decoration: underline; }

/* Per-type test detail tables — used on the view pages for the
 * gyratory specimens grid, rice flask measurements, and density
 * readings ledgers. Distinct from .ledger (which is the flex-column
 * grid used for row-style list layouts in the project view). The
 * older templates applied .ledger to actual <table> elements, which
 * collapsed cell spacing because flex doesn't honor table-cell.
 * Tester report: "weights and the voids it needs to be spaced out,
 * it's all smushed together." */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--ink);
}
.data-table thead th {
    text-align: left;
    padding: 0.5rem 0.85rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}
.data-table tbody td {
    padding: 0.55rem 0.85rem;
    border-bottom: 1px solid var(--rule-soft);
    vertical-align: middle;
    white-space: nowrap;
}
.data-table tbody tr:last-child td {
    border-bottom: 0;
}
.data-table tbody tr:hover {
    background: var(--paper-2);
}
.data-table tbody td strong {
    color: var(--ink);
}
/* First column (the row-id / "#" / "Flask") reads as a label, not data. */
.data-table thead th:first-child,
.data-table tbody td:first-child {
    font-family: var(--font-mono);
    color: var(--muted);
    width: 1%;
    padding-right: 1rem;
}
/* When a data-table cell wraps a <strong> link in the first column
 * (e.g. "PD2" pointing to the pour view), let the link's own colour
 * win over the muted first-column rule. The mono font stays. */
.data-table tbody td:first-child a strong {
    color: var(--brand-green);
}
.data-table tbody td:first-child a:hover strong {
    color: var(--brand-green-light, var(--brand-green));
}

/* Compact action link used in a data-table's trailing actions column —
 * mono / uppercase / muted-by-default, brightens on hover. Matches the
 * section-action affordance the rest of the app uses for inline
 * "do something" links, sized down for table density. */
.row-action {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
    padding: 0.1rem 0.35rem;
    white-space: nowrap;
}
.row-action + .row-action {
    margin-left: 0.45rem;
}
.row-action:hover {
    color: var(--brand-green);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.row-action .bi {
    font-size: 0.95em;
    color: var(--brand-green);
    opacity: 0.75;
}
.row-action:hover .bi {
    opacity: 1;
}

/* Cylinder Sets table on the pour view — fits without horizontal
 * scrolling on a typical laptop by folding Cast/Pickup, Truck/Size/Cap,
 * and Min/Max temp into stacked cells. Overrides the data-table default
 * `white-space: nowrap` on tbody cells so the Tests column's chips wrap
 * cleanly and the stacked cells can flow on narrower viewports. */
.cylinder-sets-table {
    table-layout: auto;
    width: 100%;
}
.cylinder-sets-table tbody td,
.cylinder-sets-table thead th {
    white-space: normal;
}
.cylinder-sets-table .cs-stack {
    line-height: 1.35;
    min-width: 9rem;
}
.cylinder-sets-table .cs-stack > div + div {
    margin-top: 0.15rem;
}
.cylinder-sets-table .cs-stack-label {
    display: inline-block;
    min-width: 2.6rem;
    font-size: 0.65rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--muted);
    font-weight: 500;
    margin-right: 0.35rem;
}
.cylinder-sets-table .cs-stack-sub {
    font-size: 0.78rem;
    color: var(--muted);
}
.cylinder-sets-table .cs-tests-cell {
    min-width: 12rem;
}
.cylinder-sets-table .cs-tests-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}
.cylinder-sets-table .cs-actions-col {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

/* Read-only static value rendered like a paragraph; used in setup
 * sections when a value is loaded from the create-time setup. */
.form-static {
    margin: 0;
    padding: 0.55rem 0.75rem;
    background: var(--paper-2);
    border: 1px solid var(--rule);
    border-radius: 2px;
    min-height: 44px;
    display: flex;
    align-items: center;
}

/* Stack reading-card grids on small screens so each row is 2-up
 * (or 1-up for the calc row) rather than squashed 5-up. */
@media (max-width: 720px) {
    .reading-card .form-grid {
        grid-template-columns: repeat(2, 1fr) !important;
    }
    .reading-card-head .gps-display {
        display: none;
    }
}

/* Prominent pass/fail callout used on per-type perform pages where the
 * spec result is the headline outcome the engineer wants to see at a
 * glance — currently the IGN AC Pass/Fail block. The small status pill
 * in the section header stays for top-of-page scanning; this callout is
 * the impossible-to-miss version right next to where the value is
 * computed. */
.result-callout {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.85rem;
    margin: 1.25rem 0 0.5rem 0;
    padding: 1rem 1.5rem;
    border: 2px solid var(--rule);
    border-radius: 4px;
    background: var(--paper-2);
    font-family: var(--font-mono);
    font-size: 1.5rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--muted);
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.result-callout i {
    font-size: 2rem;
    line-height: 1;
}
.result-callout.pass {
    border-color: var(--pass);
    color: var(--pass);
    background: rgba(76, 122, 78, 0.08);
}
.result-callout.fail {
    border-color: var(--fail);
    color: var(--fail);
    background: rgba(184, 51, 31, 0.10);
}
.result-callout-detail {
    font-family: var(--font-mono);
    font-size: 0.95rem;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0.04em;
    opacity: 0.85;
}
@media (max-width: 720px) {
    .result-callout {
        flex-direction: column;
        gap: 0.4rem;
        font-size: 1.2rem;
        padding: 0.85rem 1rem;
    }
    .result-callout i { font-size: 1.6rem; }
    .result-callout-detail { font-size: 0.85rem; }
}

/* Collapsible "optional / legacy method" panel inside a perform-section.
 * Used by rice/perform.html for the Flask Wt. + Sample+Flask pair —
 * the engineer's normal flow is to tare the flask and read sample
 * weight directly, so the legacy double-weigh inputs live behind a
 * <details> that's collapsed by default. The summary acts like a
 * subtle disclosure trigger; nothing else on the page uses this
 * pattern yet but the class is reusable. */
.optional-section {
    margin-top: 1rem;
    padding: 0.65rem 0.85rem;
    border: 1px dashed var(--rule);
    border-radius: 4px;
    background: var(--paper-2);
}
.optional-section > summary {
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    list-style: none;
    user-select: none;
    padding: 0.15rem 0;
}
.optional-section > summary::-webkit-details-marker {
    display: none;
}
.optional-section > summary::before {
    content: "▸ ";
    font-size: 0.7rem;
    margin-right: 0.25rem;
    color: var(--muted);
    transition: transform 0.15s;
    display: inline-block;
}
.optional-section[open] > summary::before {
    content: "▾ ";
}
.optional-section > summary:hover {
    color: var(--ink);
}
.optional-note {
    margin: 0.55rem 0 0 0;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--muted);
    line-height: 1.45;
}

/* Calc-field flagged as out of spec — red border + tinted background
 * + red value text. Matches the gradation row's red affordance. Used
 * on the GYRA perform Result section for voids and VMA when those
 * are out of their spec bands. */
.calc-field.out-of-spec {
    border-color: var(--fail);
    background: rgba(184, 51, 31, 0.06);
    box-shadow: inset 3px 0 0 var(--fail);
}
.calc-field.out-of-spec .calc-label {
    color: var(--fail);
}

/* AGG-SIEVE — brand-aligned status pills for the per-sieve in-spec /
 * out-of-spec column. Bootstrap's default badges read too saturated
 * next to the muted brand palette; these mirror result-callout's
 * pass/fail colors. */
.sieve-status-badge {
    display: inline-block;
    padding: 0.18rem 0.55rem;
    border-radius: 999px;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    border: 1px solid transparent;
}
.sieve-status-badge.pass {
    color: var(--pass);
    background: rgba(76, 122, 78, 0.10);
    border-color: rgba(76, 122, 78, 0.45);
}
.sieve-status-badge.fail {
    color: var(--fail);
    background: rgba(184, 51, 31, 0.10);
    border-color: rgba(184, 51, 31, 0.45);
}

/* --------------------------------------------------------------------------
   tests-create
   -------------------------------------------------------------------------- */

/* Timing chips shown below test-type select */
.ttt-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
.ttt-chip {
    font-size: 0.72rem;
    padding: 0.15rem 0.5rem;
    border-radius: 3px;
    border: 1px solid transparent;
}
.ttt-chip-prep {
    background: #fff4e5;
    color: #6b3e00;
    border-color: #e8c79a;
}
.ttt-chip-active {
    background: #e7f1ff;
    color: #00386b;
    border-color: #a9c8ec;
}
.ttt-chip-multi {
    background: #e2ecf6;
    color: #2c4d6e;
    border-color: #a8c2dc;
}

/* Mode toggle: New blank test / Start from existing */
.create-mode-toggle {
    display: flex;
    gap: 0;
    margin-bottom: 1rem;
    border: 1px solid var(--border-color);
    border-radius: 4px;
    overflow: hidden;
}
.create-mode-btn {
    flex: 1;
    padding: 0.6rem 1rem;
    font-family: var(--font-mono, 'DM Mono', monospace);
    font-size: 0.82rem;
    font-weight: 500;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    background: #f8f9fa;
    color: #555;
    border: none;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.create-mode-btn + .create-mode-btn {
    border-left: 1px solid var(--border-color);
}
.create-mode-btn:hover {
    background: #e9ecef;
}
.create-mode-btn.active {
    background: var(--brand-green, #202d26);
    color: #fff;
}
.create-mode-panel {
    border: 1px solid var(--border-color);
    border-radius: 4px;
    padding: 1rem;
    margin-bottom: 1rem;
    background: #f8f9fa;
}

/* --- Perform page: "Reopen test" form (self-revert) --- */
.test-action-bar .reopen-form { margin: 0; display: inline-flex; }
.test-action-bar .reopen-form .btn-ink {
    /* Match the sticky bar's other action buttons. */
    padding: 0.55rem 1rem;
}

/* --- Save & Complete confirmation modal --- */
.perform-complete-modal {
    position: fixed;
    inset: 0;
    z-index: 1100;
    background: rgba(15, 23, 42, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
}
.perform-complete-modal[hidden] { display: none; }
.perform-complete-modal-card {
    background: #fff;
    border-radius: 14px;
    max-width: 480px;
    width: 100%;
    padding: 24px;
    box-shadow: 0 18px 40px rgba(15, 23, 42, 0.35);
    font-family: var(--font-sans);
    color: #1a1a1a;
    /* Tap input feels snappy on iPad — no double-tap-to-zoom interpretation
       and no gray flash on the action buttons inside the card. */
    -webkit-tap-highlight-color: transparent;
}
.perform-complete-modal-title {
    margin: 0 0 12px;
    font-size: 22px;
    font-weight: 700;
    color: #0f172a;
}
.perform-complete-modal-detail {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 10px;
    padding: 14px 16px;
    margin-bottom: 14px;
}
.perform-complete-modal-test {
    margin: 0 0 4px;
    font-family: var(--font-mono);
    font-size: 14px;
    font-weight: 600;
    color: #0f172a;
}
.perform-complete-modal-project {
    margin: 0;
    color: #475569;
    font-size: 13px;
    font-family: var(--font-mono);
}
.perform-complete-modal-note {
    color: #475569;
    font-size: 14px;
    line-height: 1.4;
    margin: 0 0 18px;
}
.perform-complete-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}
.perform-complete-modal-actions .btn-ink,
.perform-complete-modal-actions .btn-ghost {
    /* Large finger-friendly targets — same sizing the kiosk numpad
       uses, since these are exactly the same iPad bench scenario. */
    min-height: 48px;
    padding: 0 22px;
    font-size: 16px;
    touch-action: manipulation;
}
