/* Donor Registration Wizard Styles */

/* Tooltips */
.tooltip {
    position: fixed;
    background: rgba(51, 51, 51, 0.85);
    color: #fff;
    padding: 7px 11px;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 500;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s;
    z-index: 10000;
}

.tooltip.visible {
    opacity: 1;
}

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

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
    background: linear-gradient(135deg, #8b1a1a 0%, #5c0e0e 100%);
    /* Pin the gradient to the viewport so the poppy shade is identical on every screen
       regardless of content height (otherwise a tall list stretches it and looks lighter). */
    background-attachment: fixed;
    min-height: 100vh;
    padding: 20px;
}

.wizard-container {
    max-width: 1200px;
    margin: 0 auto;
    background: #fff;
    border-radius: 12px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
    overflow: hidden;
}

/* Header
   Kept deliberately short: the app is used on wide-but-short laptop screens, where every
   pixel the chrome takes is a pixel the form does not get.

   Three columns on ONE row — [logo] [title + subtitle] [actions]. The logo and the action
   buttons span the full header height (`align-items: stretch`), and only the middle column
   stacks two lines. That is what lets the logo be large without the header growing: its
   height is the row height, not an independent box that adds to it. `--header-row-h` is
   the single knob for the whole header's height. */
.wizard-header {
    --header-row-h: 84px;
    background: #f8f9fa;
    padding: 8px 30px;
    border-bottom: 1px solid #e9ecef;
}

.header-top {
    display: flex;
    align-items: stretch;
    gap: 16px;
    min-height: var(--header-row-h);
}

.header-logo-link {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    line-height: 0;
}

/* Fills the row height; width follows the aspect ratio. */
.header-logo {
    height: var(--header-row-h);
    width: auto;
    object-fit: contain;
    transition: transform 0.2s ease;
}

.header-logo-link:hover .header-logo {
    transform: scale(1.08);
}

/* The only column that stacks: title over subtitle, centred against the logo. */
.header-titles {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
}

/* Full-height column, but the buttons themselves sit centred in it. */
.header-actions {
    display: flex;
    gap: 10px;
    align-items: center;
    flex-shrink: 0;
}

.btn-small {
    padding: 8px 16px;
    font-size: 0.85rem;
}

/* Preprocessing aliquot cards — reuse the .user-row.row-actions-bar card look (full-height
   tinted delete bar on the right) from the def-output tree. The card keeps its own
   15px/20px padding; the body adds none (double padding was the "huge" gap), and the
   inner form-row/-group margins are zeroed so the fields sit snug. overflow is visible so
   the material/container dropdowns aren't clipped by the card (row-actions-bar hides it),
   and the delete bar re-rounds its own right corners since the card no longer clips. */
#pp-aliquots {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 10px;
}
.user-row.row-actions-bar.pp-aliquot-card {
    margin-bottom: 0;
}
.pp-aliquot-card > .user-row-actions {
    border-radius: 0 8px 8px 0;
}
.pp-aliquot-body {
    flex: 1;
    min-width: 0;
}
.pp-aliquot-body .form-row {
    margin: 0;
}
.pp-aliquot-body .form-group {
    margin-bottom: 0;
}
/* Input-QC-failed lock: grey the region and stop its children receiving pointer events, while
   the region itself keeps them so the data-tooltip explaining the lock still shows on hover. */
.pp-qc-locked {
    opacity: 0.5;
    cursor: not-allowed;
}
.pp-qc-locked > * {
    pointer-events: none;
}
/* Duplicate aliquot barcode: red outline on the card + an inline message under its fields. */
.pp-aliquot-invalid {
    outline: 1px solid #dc3545;
    outline-offset: 1px;
    border-radius: 8px;
}
.pp-aliquot-error {
    color: #dc3545;
    font-size: 0.8rem;
    margin-top: 4px;
}
/* QC-verdict field — a segmented Pass/Fail control (green pass / red fail when active). */
.dynamic-qc-verdict .qc-verdict-segmented {
    display: inline-flex;
    border: 1px solid #ccc;
    border-radius: 8px;
    overflow: hidden;
    margin-top: 4px;
}
.qc-verdict-opt {
    border: none;
    background: #f8f9fa;
    color: #495057;
    padding: 8px 22px;
    font-size: 0.95rem;
    cursor: pointer;
    transition:
        background 0.15s ease,
        color 0.15s ease;
}
.qc-verdict-opt + .qc-verdict-opt {
    border-left: 1px solid #ccc;
}
.qc-verdict-opt.qc-pass.active {
    background: #1f7a37;
    color: #fff;
}
.qc-verdict-opt.qc-fail.active {
    background: #b3261e;
    color: #fff;
}
.dynamic-qc-verdict .qc-verdict-reason-group {
    margin-top: 10px;
}
/* Def-bound QC acceptance-criteria form rendered inline inside a qc-verdict widget. */
.qc-criteria-slot:not(:empty) {
    margin: 6px 0 10px;
}
/* One output-QC row per produced aliquot (criteria + its own Pass/Fail). */
.qc-output-item {
    border: 1px solid #e3e6ea;
    border-radius: 8px;
    padding: 10px 12px;
    margin-top: 8px;
}
.qc-output-head {
    font-weight: 600;
    font-size: 0.9rem;
    margin-bottom: 4px;
}
/* Read-only QC blocks in the run-detail viewer. */
.run-qc-head {
    font-weight: 600;
    font-size: 0.9rem;
    margin-bottom: 4px;
}
/* The verdict ribbon leads its own line above the criteria. `.role-badge` is an inline
   pill whose vertical padding bleeds outside its line box, so as `display:inline` its
   margin-bottom is ignored and the next block's background paints over it. */
.run-qc-verdict > .role-badge {
    display: inline-block;
    margin-bottom: 10px;
}
/* A recorded verdict renders its criteria through the live form engine, so it needs the
   inert treatment the run-detail steps get: it documents, it never edits. */
.run-qc-verdict .qc-entry-criteria {
    pointer-events: none;
}
.run-qc-verdict + .run-qc-verdict {
    margin-top: 12px;
}
/* Read-only QC groups in the specimen editor (Preprocessing / Processing). */
.qc-group {
    width: 100%;
}
.qc-empty {
    color: #6c757d;
    font-style: italic;
    margin: 4px 0;
}
/* Verdict, then the provenance that supports it, then the way to the run — one row inside
   a form-note--info block, which supplies the padding, colour and border.
   It wraps rather than truncating: on a narrow screen the SOP title is still worth reading,
   and an ellipsis in the middle of "who recorded it" helps nobody. */
.qc-provenance {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}
.qc-provenance .qc-provenance-meta {
    min-width: 0;
}
/* The badge sits on the note's own tinted background, so pale-green-on-pale-blue left its
   edges indistinct. An outline in its own text colour separates it from the block without
   inventing a second palette — the badge keeps the fill and colour it has everywhere else. */
.qc-provenance .role-badge {
    border: 1px solid currentColor;
}
/* The go-to button carries no margin here: it is a member of the flex row, not an
   afterthought appended to a label. */
.qc-provenance .field-fk-link {
    margin-left: 0;
}
/* Separate the loaded run form from the specimen-scan field above it. */
#pp-run:not(:empty) {
    display: block;
    margin-top: 24px;
}

.btn.btn-icon-only {
    width: 40px;
    height: 40px;
    padding: 0;
    line-height: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.btn.btn-icon-only svg {
    display: block;
    flex-shrink: 0;
}

.wizard-header h1 {
    color: #333;
    margin: 0;
    font-size: 1.45rem;
}

/* Breadcrumb Navigation */
.breadcrumb-nav {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 0 30px 8px;
    font-size: 0.8rem;
}

.breadcrumb-nav a {
    color: #8b1a1a;
    text-decoration: none;
    font-weight: 500;
    transition: color 0.2s;
}

.breadcrumb-nav a:hover {
    color: #6e1515;
    text-decoration: underline;
}

.breadcrumb-nav .breadcrumb-sep {
    color: #adb5bd;
    font-size: 0.9rem;
}

.breadcrumb-nav .breadcrumb-current {
    color: #495057;
    font-weight: 600;
}

/* Progress Bar */
.progress-bar {
    position: relative;
    padding: 12px 20px 0;
}

.progress-steps {
    display: flex;
    justify-content: space-between;
    position: relative;
    z-index: 1;
}

.step {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1;
    cursor: pointer;
}

.step:hover span {
    transform: scale(1.1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.step:hover label {
    color: #8b1a1a;
}

.step span {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #e9ecef;
    color: #6c757d;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    font-size: 1rem;
    transition: all 0.3s ease;
    border: 3px solid #e9ecef;
}

.step label {
    margin-top: 8px;
    font-size: 0.75rem;
    color: #6c757d;
    text-align: center;
    transition: color 0.3s ease;
}

.step.active span {
    background: #8b1a1a;
    color: #fff;
    border-color: #8b1a1a;
}

.step.active label {
    color: #8b1a1a;
    font-weight: 600;
}

.step.completed span {
    background: #8b1a1a;
    color: transparent;
    border-color: #8b1a1a;
    position: relative;
}

.step.completed span::after {
    content: '✓';
    position: absolute;
    color: #fff;
    font-size: 1.1rem;
    font-weight: bold;
}

.step.completed label {
    color: #8b1a1a;
}

.progress-line {
    position: absolute;
    top: 32px;
    left: 60px;
    right: 60px;
    height: 4px;
    background: #8b1a1a;
    z-index: 0;
}

.progress-fill {
    height: 100%;
    background: #8b1a1a;
    width: 0%;
    transition: width 0.3s ease;
}

/* Content */
.wizard-content {
    padding: 30px;
    min-height: 500px;
    background: #f0f2f5;
}

.wizard-step {
    display: none;
}

.wizard-step.active {
    display: block;
    animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateX(20px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

.wizard-step h2 {
    color: #333;
    margin-bottom: 20px;
    padding: 15px 20px;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.section-note {
    color: #6c757d;
    margin-bottom: 20px;
    font-style: italic;
}

/* Form Sections */
.form-section {
    margin-bottom: 30px;
    padding: 20px;
    background: #fff;
    border-radius: 10px;
    border: 1px solid #e9ecef;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.form-section h3 {
    color: #8b1a1a;
    font-size: 1rem;
    margin-bottom: 20px;
    padding-bottom: 10px;
    border-bottom: 2px solid #8b1a1a;
    font-weight: 600;
}

.form-row {
    display: flex;
    gap: 20px;
    margin-bottom: 15px;
    flex-wrap: wrap;
}

.form-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 15px;
}

@media (max-width: 768px) {
    .form-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 480px) {
    .form-grid {
        grid-template-columns: 1fr;
    }
}

.form-row.form-row-2col > .form-group {
    min-width: 45%;
}

.form-row.form-row-3col > .form-group {
    min-width: 30%;
}

.form-group {
    flex: 1;
    min-width: 200px;
    padding: 12px;
    background: #f8f9fa;
    border-radius: 8px;
    border-left: 3px solid #dee2e6;
}

.form-group.full-width {
    flex: 100%;
    min-width: 100%;
}

/* Field size wrapper — size 2 takes 2 of 3 slots in a form-row */
.field-size-2 {
    flex: 2;
    min-width: 0;
}

.field-size-2 > .form-group {
    min-width: 100%;
}

.form-grid .field-size-2 {
    grid-column: span 2;
}

.form-grid .field-size-3 {
    grid-column: 1 / -1;
}

/* Searchable-select size-2 in form-row */
.form-group.field-size-2 {
    flex: 2;
    min-width: 0;
}

.form-group label,
.form-group .field-label {
    display: block;
    margin-bottom: 8px;
    color: #495057;
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.form-group input,
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 10px 12px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    font-size: 0.95rem;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
    background: #fff;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.form-group input:disabled,
.form-group select:disabled {
    background: #e9ecef;
    cursor: not-allowed;
}

.form-group input[readonly] {
    background: #f8f9fa;
}

.form-group textarea {
    resize: vertical;
    min-height: 60px;
}

/* Checkbox Group */
.consent-checkboxes {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
}

.checkbox-group {
    min-width: auto;
    flex: 0 0 auto;
    padding: 0;
    background: transparent;
    border: none;
}

.checkbox-group label {
    display: flex;
    align-items: center;
    cursor: pointer;
    font-weight: 500;
    padding: 10px 16px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    transition: all 0.2s ease;
    background: #fff;
    text-transform: none;
    letter-spacing: normal;
    font-size: 0.95rem;
}

.checkbox-group label:hover {
    border-color: #8b1a1a;
    background: #fef5f5;
}

.checkbox-group input[type='checkbox'] {
    width: auto;
    margin-right: 8px;
    cursor: pointer;
    accent-color: #8b1a1a;
}

.checkbox-group label:has(input[type='checkbox']:checked) {
    border-color: #8b1a1a;
    background: #fdf0f0;
    color: #8b1a1a;
}

/* Radio Button Groups */
.radio-group {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    padding: 10px 0;
}

.radio-group label {
    display: flex;
    align-items: center;
    cursor: pointer;
    font-weight: 500;
    padding: 10px 16px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    transition: all 0.2s ease;
    background: #fff;
}

.radio-group label:hover {
    border-color: #8b1a1a;
    background: #fef5f5;
}

.radio-group input[type='radio'] {
    width: auto;
    margin-right: 8px;
    cursor: pointer;
    accent-color: #8b1a1a;
}

.radio-group input[type='radio']:checked + span,
.radio-group label:has(input[type='radio']:checked) {
    border-color: #8b1a1a;
    background: #fdf0f0;
    color: #8b1a1a;
}

/* Conditional Fields */
.conditional-fields {
    margin-left: 20px;
    padding-left: 15px;
    border-left: 4px solid #8b1a1a;
    background: rgba(139, 26, 26, 0.05);
    border-radius: 0 8px 8px 0;
    padding: 15px;
    margin-top: 5px;
    margin-bottom: 15px;
    animation: slideDown 0.2s ease-out;
}

.conditional-fields .form-group {
    background: #fff;
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Footer */
.wizard-footer {
    padding: 20px 30px;
    background: #f8f9fa;
    border-top: 1px solid #e9ecef;
    display: flex;
    justify-content: space-between;
    gap: 10px;
}

/* Buttons */
.btn {
    padding: 12px 30px;
    border: 2px solid transparent;
    border-radius: 6px;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Primary: filled brand color (Next, Save) */
.btn-primary {
    background: #8b1a1a;
    border-color: #8b1a1a;
    color: #fff;
}

.btn-primary:hover:not(:disabled) {
    background: #6e1515;
    border-color: #6e1515;
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(139, 26, 26, 0.4);
}

/* Secondary: warm neutral tint (Previous, Cancel) */
.btn-secondary {
    background: #f5f0e6;
    border-color: #d4c9b5;
    color: #5c5347;
}

.btn-secondary:hover:not(:disabled) {
    background: #ebe4d6;
    border-color: #b8a98f;
    color: #3d3832;
}

/* Inline editor label decorations (FK "open linked" + barcode "print") — a small
   square icon button styled like a secondary button (background / border / icon). */
.field-fk-link,
.field-barcode-print,
.field-copy,
.field-now {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    margin-left: 6px;
    vertical-align: middle;
    border: 1px solid #d4c9b5;
    border-radius: 5px;
    background: #f5f0e6;
    color: #5c5347;
    cursor: pointer;
    transition: all 0.15s ease;
}

.field-fk-link:hover,
.field-barcode-print:hover,
.field-copy:hover,
.field-now:hover {
    background: #ebe4d6;
    border-color: #b8a98f;
    color: #3d3832;
}

.field-fk-link svg,
.field-barcode-print svg,
.field-copy svg,
.field-now svg {
    width: 13px;
    height: 13px;
}

/* Field + action button on one row: the control flexes, the button sits to its right
   at full field height (a comfortably-clickable square, not a tiny label-corner icon).
   Applied to the fk-link / barcode-print / now buttons (the copy button keeps its
   inline-with-label placement). */
.field-with-action {
    display: flex;
    align-items: stretch;
    gap: 6px;
}

.field-with-action > .field-action-control {
    flex: 1 1 auto;
    min-width: 0;
}

.field-with-action > .field-fk-link,
.field-with-action > .field-barcode-print,
.field-with-action > .field-now {
    flex: 0 0 auto;
    width: auto;
    min-width: 42px;
    height: auto;
    margin-left: 0;
    align-self: stretch;
    border-radius: 6px;
}

/* Multi-select variant: the pill widget grows as pills wrap, so the button can't stretch
   to the control — it stays level with the first row at exactly the size a single-select
   field's button has (41px = .ss-input's 2px borders + 8px paddings + its line box). */
.field-with-action > .field-fk-link.field-action-top {
    align-self: flex-start;
    height: 41px;
}

.field-with-action > .field-fk-link svg,
.field-with-action > .field-barcode-print svg,
.field-with-action > .field-now svg {
    width: 16px;
    height: 16px;
}

/* Success: filled green (Submit) */
.btn-success {
    background: #28a745;
    border-color: #28a745;
    color: #fff;
}

.btn-success:hover:not(:disabled) {
    background: #218838;
    border-color: #218838;
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(40, 167, 69, 0.4);
}

/* Summary */
.summary-container {
    background: #fff;
    border-radius: 10px;
    padding: 25px;
    border: 1px solid #e9ecef;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.summary-section {
    margin-bottom: 20px;
    padding: 15px;
    background: #f8f9fa;
    border-radius: 8px;
    border-left: 3px solid #8b1a1a;
}

.summary-section:last-child {
    margin-bottom: 0;
}

.summary-section h3 {
    color: #8b1a1a;
    font-size: 1rem;
    margin-bottom: 10px;
}

.summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 10px;
}

.summary-item {
    display: flex;
    flex-direction: column;
}

.summary-item .label {
    font-size: 0.8rem;
    color: #6c757d;
}

.summary-item .value {
    font-weight: 500;
    color: #333;
}

.summary-item .value.empty {
    color: #adb5bd;
    font-style: italic;
}

.summary-item.full-width {
    grid-column: 1 / -1;
}

.summary-item.bold .value {
    font-weight: 600;
    font-size: 0.95rem;
}

.summary-item.small .label,
.summary-item.small .value {
    font-size: 0.75rem;
}

.summary-item.small .value {
    font-weight: 400;
}

.summary-subsection {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px dashed #dee2e6;
}

.summary-subsection-title {
    font-size: 0.8rem;
    font-weight: 500;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    display: block;
    margin-bottom: 8px;
}

/* Loading Overlay */
.loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1500;
}

.loading-card {
    background: #fff;
    border-radius: 16px;
    padding: 40px 48px;
    display: flex;
    flex-direction: column;
    align-items: center;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}

.loading-spinner {
    width: 48px;
    height: 48px;
    border: 4px solid #e9ecef;
    border-top: 4px solid #8b1a1a;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

.loading-card p {
    color: #495057;
    margin: 20px 0 0;
    font-size: 1rem;
    font-weight: 500;
}

/* Modal */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1001;
}

.modal-content {
    background: #fff;
    border-radius: 12px;
    padding: 30px;
    max-width: 600px;
    max-height: 80vh;
    overflow-y: auto;
    width: 90%;
}

.modal-content h2 {
    margin-bottom: 20px;
    color: #333;
}

.modal-content .btn {
    margin-top: 20px;
}

/* Result Styles */
.result-success {
    color: #28a745;
}

.result-error {
    color: #dc3545;
}

.result-steps {
    margin-top: 15px;
    font-size: 0.9rem;
}

.result-step {
    padding: 8px 0;
    border-bottom: 1px solid #e9ecef;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.result-step:last-child {
    border-bottom: none;
}

.result-step.success {
    color: #28a745;
}

.result-step.error {
    color: #dc3545;
}

.result-step .step-name {
    font-weight: 500;
}

.result-step .step-status {
    font-size: 0.85rem;
}

/* Repeatable Entries List */
.entries-list {
    margin-bottom: 15px;
}

.entries-list:empty::before {
    content: attr(data-empty-text);
    display: block;
    padding: 20px;
    text-align: center;
    color: #6c757d;
    font-style: italic;
    background: #fff;
    border-radius: 8px;
    border: 2px dashed #adb5bd;
}

.entry-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 15px;
    background: #fff;
    border: 2px solid #adb5bd;
    border-radius: 8px;
    margin-bottom: 8px;
    cursor: pointer;
    transition: all 0.2s ease;
}

.entry-item:hover {
    background: #e9ecef;
    border-color: #8b1a1a;
}

.entry-item.active {
    background: #fdf0f0;
    color: #333;
    border-color: #8b1a1a;
    box-shadow: inset 4px 0 0 #8b1a1a;
}

.entry-item.active .entry-subtitle {
    color: #6c757d;
}

.entry-item-content {
    flex: 1;
}

.entry-title {
    font-weight: 600;
    font-size: 0.95rem;
}

.entry-subtitle {
    font-size: 0.8rem;
    color: #6c757d;
    margin-top: 2px;
}

.entry-actions {
    display: flex;
    align-items: stretch;
    align-self: stretch;
    margin: -12px -15px -12px 0;
    border-radius: 0 6px 6px 0;
    overflow: hidden; /* clip each button's hover fill to the rounded right corner */
}

/* Full-height tinted action bars, consistent with the .user-row.row-actions-bar
   .btn-icon bars used on every other builder card: a light persistent tint of the
   icon colour at rest (duplicate violet, delete red), intensifying on hover/press —
   rather than a transparent-until-hover solid fill, which read as small floating icons. */
.btn-entry-duplicate,
.btn-entry-delete {
    border: none;
    cursor: pointer;
    width: 48px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    transition: background 0.15s ease;
}

.btn-entry-duplicate svg,
.btn-entry-delete svg {
    width: 16px;
    height: 16px;
}

.btn-entry-duplicate {
    color: #7c3aed;
    background: rgba(124, 58, 237, 0.08);
}
.btn-entry-duplicate:hover,
.btn-entry-duplicate:active {
    background: rgba(124, 58, 237, 0.22);
}

.btn-entry-delete {
    color: #dc3545;
    background: rgba(220, 53, 69, 0.08);
}
.btn-entry-delete:hover,
.btn-entry-delete:active {
    background: rgba(220, 53, 69, 0.22);
}

/* Add Button */
.btn-add {
    display: block;
    width: 100%;
    padding: 15px;
    background: #f8f9fa;
    border: 2px dashed #8b1a1a;
    border-radius: 8px;
    color: #8b1a1a;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    margin-bottom: 20px;
}

.btn-add:hover {
    background: #8b1a1a;
    color: #fff;
    border-style: solid;
}

/* Entry Form */
.entry-form {
    background: #fff;
    border: 2px solid #8b1a1a;
    border-radius: 10px;
    padding: 20px;
    margin-top: 15px;
    box-shadow: 0 4px 12px rgba(139, 26, 26, 0.15);
}

.entry-form .form-section {
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
    margin-bottom: 0;
}

.entry-form .form-section h3 {
    display: none;
}

.entry-form-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid #e9ecef;
}

.entry-form-header h3 {
    margin: 0;
    color: #8b1a1a;
    font-size: 1.1rem;
}

.btn-close-form {
    background: none;
    border: none;
    font-size: 1.5rem;
    color: #6c757d;
    cursor: pointer;
    padding: 0;
    line-height: 1;
}

.btn-close-form:hover {
    color: #333;
}

.entry-form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 20px;
    padding-top: 15px;
    border-top: 1px solid #e9ecef;
}

/* Nested entry-form (biospecimen inside phase) */
.entry-form .entry-form {
    border-color: #d4c9b5;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    margin-bottom: 10px;
}

/* Planned-output tree editor — reuses the form-builder field-card look
   (.user-row.row-actions-bar gives the full-height tinted .btn-icon action bars).
   The section itself is a standard .builder-fieldset. */
.bio-children-tree {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 10px 0 14px;
}
.po-node {
    display: flex;
    flex-direction: column;
}
/* The header keeps its full .user-row card styling; flatten its bottom when expanded
   so the form body below reads as one unit. */
.po-node > .po-node-head {
    margin-bottom: 0;
    cursor: pointer; /* the whole head toggles expand/collapse */
}
.po-node.po-expanded > .po-node-head {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
/* Expanded form body — connects under the card like the composer's settings panel. */
.po-node-body {
    background: #f8f9fa;
    border: 2px solid #e9ecef;
    border-top: none;
    border-radius: 0 0 10px 10px;
    padding: 14px 16px 2px;
}
.po-node-body .form-row {
    margin-bottom: 12px;
}
.po-num {
    width: 100%;
}
/* Green "add sub-output" bar (delete=red / expand=blue tints already exist globally). */
.user-row.row-actions-bar .btn-icon-activate {
    color: #1f7a37;
    background: rgba(40, 167, 69, 0.1);
}
.user-row.row-actions-bar .btn-icon-activate:hover,
.user-row.row-actions-bar .btn-icon-activate:active {
    background: rgba(40, 167, 69, 0.24);
}
/* Nested sub-outputs, indented with tree elbow connectors (trunk + per-child elbow). */
.po-children {
    position: relative;
    padding-left: 28px;
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.po-children > .po-node {
    position: relative;
}
/* Vertical trunk running down the left of the group. */
.po-children::before {
    content: '';
    position: absolute;
    left: 11px;
    top: -8px;
    height: 100%;
    border-left: 2px solid #cfd4da;
}
/* Horizontal elbow from the trunk to each child card's header. */
.po-children > .po-node::before {
    content: '';
    position: absolute;
    left: -17px;
    top: 25px;
    width: 17px;
    border-top: 2px solid #cfd4da;
}
/* Mask the trunk below the LAST child's elbow so it terminates cleanly. */
.po-children > .po-node:last-child::after {
    content: '';
    position: absolute;
    left: -19px;
    top: 27px;
    bottom: -8px;
    width: 5px;
    background: #fff;
}

/* Form-definition version history — indented cards under a form-composer row, reusing
   the planned-output tree's trunk + elbow connectors. */
.form-versions {
    position: relative;
    padding-left: 28px;
    margin: 4px 0 10px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.form-versions::before {
    content: '';
    position: absolute;
    left: 11px;
    top: -6px;
    height: 100%;
    border-left: 2px solid #cfd4da;
}
/* Version cards carry the standard .user-row object-list chrome; only the tree
   connector + clickability are layered on here. */
.form-version-row {
    position: relative;
    cursor: pointer;
}
.form-versions .user-row {
    margin-bottom: 0;
}
.form-version-row::before {
    content: '';
    position: absolute;
    left: -17px;
    top: 50%;
    width: 17px;
    border-top: 2px solid #cfd4da;
}
.form-versions .form-version-row:last-child::after {
    content: '';
    position: absolute;
    left: -19px;
    top: 50%;
    bottom: -8px;
    width: 5px;
    background: #fff;
}

/* A preprocessing aliquot row prefilled from the plan. */
#pp-aliquot-table tr.pp-planned-row td {
    background: #f3f8f2;
}

/* Language Switcher */
.lang-switcher {
    display: flex;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid #ced4da;
}

.lang-btn {
    padding: 8px 12px;
    border: none;
    background: #e9ecef;
    color: #6c757d;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.lang-btn:first-child {
    border-right: 1px solid #ced4da;
}

.lang-btn.active {
    background: #8b1a1a;
    color: #fff;
}

.lang-btn:hover:not(.active) {
    background: #dee2e6;
}

/* Autocomplete */
.autocomplete-wrapper {
    position: relative;
}

.autocomplete-input {
    width: 100%;
    padding: 10px 12px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    font-size: 0.95rem;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
    background: #fff;
}

.autocomplete-input:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.autocomplete-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border: 2px solid #adb5bd;
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 200px;
    overflow-y: auto;
    z-index: 100;
    display: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.autocomplete-dropdown.show {
    display: block;
}

.autocomplete-item {
    padding: 10px 12px;
    cursor: pointer;
    font-size: 0.9rem;
    border-bottom: 1px solid #f0f0f0;
}

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

.autocomplete-item:hover,
.autocomplete-item.active {
    background: #8b1a1a;
    color: #fff;
}

.autocomplete-loading,
.autocomplete-no-results {
    padding: 10px 12px;
    color: #6c757d;
    font-style: italic;
    font-size: 0.9rem;
}

.snomed-code {
    color: #6c757d;
    font-size: 0.8rem;
    font-weight: normal;
}

.autocomplete-item:hover .snomed-code,
.autocomplete-item.active .snomed-code {
    color: rgba(255, 255, 255, 0.8);
}

/* Custom Dialog Modal */
.dialog-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    /* Above .barcode-modal (2000). Both sat at 2000 and the picker, being appended to the
       body later, painted over the confirmation it had just opened — the operator picked a
       cell and the "is this the right position?" dialog appeared behind the picker. */
    z-index: 2100;
    opacity: 0;
    visibility: hidden;
    transition:
        opacity 0.2s ease,
        visibility 0.2s ease;
}

.dialog-overlay.show {
    opacity: 1;
    visibility: visible;
}

.dialog-box {
    background: #fff;
    border-radius: 12px;
    padding: 0;
    max-width: 420px;
    width: 90%;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    transform: scale(0.9) translateY(-20px);
    transition: transform 0.2s ease;
}

.dialog-overlay.show .dialog-box {
    transform: scale(1) translateY(0);
}

/* A dialog never outgrows the viewport: its body scrolls, the title and the buttons stay
   where they are. A long one (a logbook entry with many fields) used to grow past both
   edges of the screen, taking its own OK button with it. */
.dialog-box {
    display: flex;
    flex-direction: column;
    max-height: 90vh;
}

.dialog-box > .dialog-body {
    min-height: 0;
    overflow-y: auto;
}

.dialog-header {
    padding: 20px 24px 0;
    display: flex;
    align-items: center;
    gap: 12px;
}

.dialog-icon {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
    flex-shrink: 0;
}

.dialog-icon.info {
    background: rgba(139, 26, 26, 0.15);
    color: #8b1a1a;
}

.dialog-icon.warning {
    background: rgba(255, 193, 7, 0.15);
    color: #e6a700;
}

.dialog-icon.error {
    background: rgba(220, 53, 69, 0.15);
    color: #dc3545;
}

.dialog-icon.confirm {
    background: rgba(139, 26, 26, 0.15);
    color: #8b1a1a;
}

.dialog-title {
    font-size: 1.1rem;
    font-weight: 600;
    color: #333;
    margin: 0;
}

.dialog-body {
    padding: 16px 24px 24px;
    color: #555;
    font-size: 0.95rem;
    line-height: 1.5;
}

/* Form groups in an html: true dialog.
   `.form-group` carries no margin of its own — in an editor the spacing comes from the
   `.form-row` flex gap, and a dialog body has no form-row, so the fields sat flush against
   each other. `min-width: 0` because the 200px floor overflows a narrow dialog box. */
.dialog-body .form-group {
    min-width: 0;
}

.dialog-body .form-group + .form-group {
    margin-top: 10px;
}

/* Manual state override: from → to above the warning. */
.state-override-change {
    margin-bottom: 10px;
}

/* A dialog listing records (the storage cell's occupants) stacks cards, not fields. */
.dialog-body .user-row + .user-row {
    margin-top: 8px;
}

/* The plan-deviation gate: what departed from the SOP, per specimen, above the one reason
   box that covers all of it. The findings are what the operator is being asked to justify,
   so they get the same tinted panel a deviation gets in the run viewer — reading as a
   quotation of the problem rather than as more dialog prose. */
.pp-dev-specimen {
    background: #fdf0f0;
    border: 1px solid #f3d6d6;
    border-radius: 8px;
    padding: 8px 12px;
    margin-top: 10px;
    color: #6e1515;
}
.pp-dev-specimen strong {
    font-family: 'SF Mono', Menlo, Consolas, monospace;
    font-size: 0.9rem;
}
.pp-dev-specimen ul {
    margin: 4px 0 0;
    padding-left: 18px;
}
.pp-dev-specimen li + li {
    margin-top: 2px;
}
/* The reason is the point of the dialog, not a footnote to the list. */
.dialog-body .pp-dev-reason-group {
    margin-top: 16px;
}
.pp-dev-reason {
    width: 100%;
    resize: vertical;
}

.dialog-footer {
    padding: 0 24px 24px;
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    flex-wrap: wrap; /* long button labels wrap inside the box instead of overflowing */
}

/* Buttons share the row and grow to fill it; when a label is too long to fit
   side-by-side they wrap and each takes the full dialog width (never ragged). */
.dialog-footer .dialog-btn {
    flex: 1 1 auto;
}

.dialog-btn {
    padding: 10px 20px;
    border: 2px solid transparent;
    border-radius: 6px;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    white-space: nowrap;
}

.dialog-btn-secondary {
    background: #f5f0e6;
    border-color: #d4c9b5;
    color: #5c5347;
}

.dialog-btn-secondary:hover {
    background: #ebe4d6;
    border-color: #b8a98f;
    color: #3d3832;
}

.dialog-btn-primary {
    background: #8b1a1a;
    border-color: #8b1a1a;
    color: #fff;
}

.dialog-btn-primary:hover {
    background: #6e1515;
    border-color: #6e1515;
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(139, 26, 26, 0.4);
}

.dialog-btn-danger {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff;
}

.dialog-btn-danger:hover {
    background: #c82333;
    border-color: #c82333;
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4);
}

/* Barcode Scan Modal */
.barcode-modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.8);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
}

.barcode-modal-content {
    position: relative;
    background: #fff;
    border-radius: 16px;
    padding: 40px 50px;
    text-align: center;
    max-width: 520px;
    width: 90%;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

/* Close button — top-right, primary (maroon) tint, large. Applies to every barcode
   scan dialog (all use .barcode-modal-content .btn-close-modal). */
.barcode-modal-content .btn-close-modal {
    position: absolute;
    top: 12px;
    right: 16px;
    width: 44px;
    height: 44px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: #8b1a1a;
    font-size: 2.2rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 10px;
    transition:
        background 0.15s ease,
        color 0.15s ease;
}

.barcode-modal-content .btn-close-modal:hover,
.barcode-modal-content .btn-close-modal:focus-visible {
    background: rgba(139, 26, 26, 0.12);
    color: #6f1414;
}

.barcode-icon {
    color: #8b1a1a;
    margin-bottom: 20px;
}

/* Account actions on the clinician's scan dialog (language, change password, sign out):
   the dialog covers the header that normally holds them. Layout only — the controls are
   the header's own (.lang-switcher, .btn.btn-secondary.btn-small). */
.barcode-modal-account {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding-top: 20px;
    border-top: 1px solid #e9ecef;
}

/* The stacking order of everything that covers the page, lowest first:
     1000  .biospecimen-modal        1001  .modal
     1500  .loading-overlay
     2000  .barcode-modal            the scan dialog and the pickers built like it
     2050  #menu-change-password-modal
     2100  .dialog-overlay           the shared confirm / alert (leave prompt, "Reload?")
     2200  #login-modal              signing in again after a 401
     9999  .app-loader-overlay       (bb-base; lets clicks through)
    10000  the idle-logout warning (bb-base, inline), .tooltip, .password-expiry-banner
    11000  .toast-container
   "Change password" is offered from the scan dialog, so its form is above that (at .modal's
   1001 it opened underneath) — and below the shared dialog, which it shared 2100 with: a
   confirm or an alert raised while the form was open came up behind it.
   The sign-in dialog is above every one of them. It shared 2000 with the scan dialog, which
   comes later in the document: a session that ended under "Change password" opened the
   sign-in underneath both, and the page looked dead. Whatever was open is still there once
   the operator is signed in again. Only the idle warning (which a signed-out page never
   raises) and the toasts — the rate limiter's among them — go over it. */
#menu-change-password-modal {
    z-index: 2050;
}

#login-modal {
    z-index: 2200;
}

.barcode-modal-content h2 {
    color: #333;
    font-size: 1.5rem;
    margin-bottom: 10px;
}

.barcode-modal-content p {
    color: #6c757d;
    font-size: 1rem;
    margin-bottom: 25px;
}

.barcode-input {
    width: 100%;
    padding: 15px 20px;
    font-size: 1.2rem;
    border: 2px solid #8b1a1a;
    border-radius: 8px;
    text-align: center;
    letter-spacing: 2px;
    margin-bottom: 20px;
    outline: none;
    transition: box-shadow 0.2s;
}

.barcode-input:focus {
    box-shadow: 0 0 0 4px rgba(139, 26, 26, 0.25);
}

.barcode-input::placeholder {
    letter-spacing: normal;
    color: #adb5bd;
}

.barcode-input-wrapper {
    position: relative;
    width: 100%;
    margin-bottom: 10px;
}

.barcode-input-wrapper .barcode-input {
    margin-bottom: 0;
    padding-right: 70px;
}

.barcode-char-counter {
    position: absolute;
    right: 15px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.85rem;
    color: #868e96;
    font-weight: 500;
    pointer-events: none;
}

.barcode-char-counter.valid {
    color: #28a745;
}

.barcode-warning {
    color: #dc3545;
    font-size: 0.9rem;
    margin: 0 0 15px 0;
    padding: 8px 12px;
    background: rgba(220, 53, 69, 0.1);
    border-radius: 4px;
    text-align: center;
}

/* User Menu */
.user-menu {
    position: relative;
}

.user-menu-btn.btn-icon-only {
    gap: 2px;
}

.user-menu-caret {
    display: block;
    transition: transform 0.2s;
}

.user-menu-dropdown.show ~ .user-menu-btn .user-menu-caret,
.user-menu-btn[aria-expanded='true'] .user-menu-caret {
    transform: rotate(180deg);
}

.user-menu-dropdown {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
    min-width: 200px;
    z-index: 100;
    overflow: hidden;
}

.user-menu-dropdown.show {
    display: block;
}

.user-menu-header {
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.user-menu-name {
    font-weight: 600;
    color: #333;
    font-size: 0.95rem;
}

.user-menu-role {
    font-size: 0.75rem;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.user-menu-divider {
    height: 1px;
    background: #e9ecef;
}

.user-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 12px 16px;
    border: none;
    background: none;
    cursor: pointer;
    font-size: 0.9rem;
    color: #495057;
    transition: background 0.15s;
}

.user-menu-item:hover {
    background: #f8f9fa;
    color: #dc3545;
}

/* Login Modal */
.login-logo {
    margin-bottom: 20px;
    text-align: center;
}

.login-logo-text {
    margin-bottom: 12px;
    font-size: 1.4rem;
    font-weight: 700;
    color: #2c3e50;
    letter-spacing: 2px;
}

.login-logo img {
    width: 260px;
    height: 260px;
    object-fit: contain;
}

/* Password visibility toggle */
.password-wrapper {
    position: relative;
    margin-bottom: 12px;
}
.password-wrapper .login-input {
    padding-right: 3rem;
    margin-bottom: 0;
}
.password-toggle {
    position: absolute;
    right: 0.75rem;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    color: #6c757d;
    padding: 0.2rem;
    display: flex;
    align-items: center;
}
.password-toggle:hover {
    color: #8b1a1a;
}

.login-input {
    width: 100%;
    padding: 15px 20px;
    font-size: 1rem;
    border: 2px solid #adb5bd;
    border-radius: 8px;
    margin-bottom: 12px;
    outline: none;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}

.login-input:focus {
    border-color: #8b1a1a;
    box-shadow: 0 0 0 4px rgba(139, 26, 26, 0.25);
}

.login-error {
    color: #dc3545;
    font-size: 0.9rem;
    margin: 0 0 12px 0;
    padding: 8px 12px;
    background: rgba(220, 53, 69, 0.1);
    border-radius: 4px;
    text-align: center;
}

/* Why the previous session ended (idle logout): information, not an error. */
.login-notice {
    color: #6c4a00;
    font-size: 0.9rem;
    margin: 0 0 12px 0;
    padding: 8px 12px;
    background: rgba(255, 193, 7, 0.15);
    border-radius: 4px;
    text-align: center;
}

.login-btn {
    width: 100%;
    padding: 14px;
    font-size: 1.05rem;
    margin-top: 4px;
}

.login-link {
    color: #8b1a1a;
    text-decoration: none;
    font-size: 0.9rem;
}

.login-link:hover {
    text-decoration: underline;
}

/* Password expiry warning banner */
.password-expiry-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 10000;
    background: #fff3cd;
    color: #856404;
    border-bottom: 1px solid #ffc107;
    padding: 10px 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.95rem;
    gap: 16px;
}

.password-expiry-dismiss {
    background: none;
    border: none;
    font-size: 1.3rem;
    color: #856404;
    cursor: pointer;
    padding: 0 4px;
    line-height: 1;
}

.password-expiry-dismiss:hover {
    color: #533f03;
}

/* Overview Screen */
.overview-screen {
    display: flex;
    flex-direction: column;
    padding: 20px;
}

.overview-screen.active {
    display: flex;
}

.overview-header {
    text-align: center;
    margin-bottom: 30px;
}

/* Header subtitle — the second line of the title column, so it no longer needs the old
   left margin that manually cleared the logo. */
.donor-info-summary {
    display: none;
    color: #6c757d;
    font-size: 0.85rem;
    font-weight: bold;
}

.donor-info-summary.visible {
    display: block;
}

.donor-info-summary span + span::before {
    content: ' · ';
    margin: 0 0.4em;
}

/* Timeline container for all cards */
.overview-cards {
    display: flex;
    flex-direction: column;
    gap: 0;
    max-width: 800px;
    margin: 0 auto;
    width: 100%;
    position: relative;
    padding-left: 40px;
}

/* Timeline vertical line */
.overview-cards::before {
    content: '';
    position: absolute;
    left: 15px;
    top: 30px;
    bottom: 30px;
    width: 3px;
    background: linear-gradient(to bottom, #8b1a1a, #5c0e0e);
    border-radius: 2px;
}

/* Timeline dots on each card */
.overview-cards > .overview-card {
    margin-bottom: 15px;
    position: relative;
}

.overview-cards > .overview-card::before {
    content: '';
    position: absolute;
    /* -35 (not -33): compensates for the card's 2px border so the dot centers
       on the timeline axis, matching the borderless phase-group dots. */
    left: -35px;
    top: 50%;
    transform: translateY(-50%);
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #e9ecef;
    border: 3px solid #fff;
    box-shadow: 0 0 0 2px #8b1a1a;
    z-index: 1;
}

.overview-cards > .overview-card.completed::before {
    background: #8b1a1a;
}

/* Pulsing dot for action items */
.overview-cards > .overview-card.action::before {
    background: #8b1a1a;
    animation: pulse-dot 2s ease-in-out infinite;
}

@keyframes pulse-dot {
    0%,
    100% {
        box-shadow:
            0 0 0 2px #8b1a1a,
            0 0 0 4px rgba(139, 26, 26, 0.4);
    }
    50% {
        box-shadow:
            0 0 0 2px #8b1a1a,
            0 0 0 10px rgba(139, 26, 26, 0);
    }
}

/* Pulsing halo around the card icon for action items (mirrors pulse-dot ring style) */
.overview-card.action .card-icon {
    animation: pulse-card-icon 2s ease-in-out infinite;
}

@keyframes pulse-card-icon {
    0%,
    100% {
        box-shadow:
            0 0 0 2px rgba(139, 26, 26, 0.4),
            0 0 0 4px rgba(139, 26, 26, 0.2);
    }
    50% {
        box-shadow:
            0 0 0 2px rgba(139, 26, 26, 0.6),
            0 0 0 14px rgba(139, 26, 26, 0);
    }
}

/* Phase cards container */
.phase-cards-container {
    display: flex;
    flex-direction: column;
    gap: 0;
}

/* Phase group - contains header and cards for one phase */
.phase-group {
    margin-bottom: 20px;
    position: relative;
}

.phase-group:last-child {
    margin-bottom: 0;
}

/* Phase group header */
.phase-group-header {
    margin-bottom: 10px;
    padding-left: 5px;
    display: flex;
    align-items: center;
    gap: 10px;
}

.phase-group-header h3 {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: #495057;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.phase-group.completed .phase-group-header h3 {
    color: #155724;
}

.phase-group.current .phase-group-header h3 {
    color: #8b1a1a;
}

.phase-group.pending .phase-group-header h3 {
    color: #6c757d;
}

.phase-header-icon {
    display: inline-flex;
    align-items: center;
}

.phase-header-icon.status-done {
    color: #155724;
}

.phase-header-icon.status-action {
    color: #8b1a1a;
}

.phase-header-icon.status-pending {
    color: #6c757d;
}

/* Cards container within a phase group */
.phase-group-cards {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Timeline dot for phase group */
.phase-group::before {
    content: '';
    position: absolute;
    left: -33px;
    top: 8px;
    width: 12px;
    height: 12px;
    background: #fff;
    border-radius: 50%;
    border: 3px solid #fff;
    box-shadow: 0 0 0 2px #8b1a1a;
    z-index: 1;
}

.phase-group.completed::before {
    background: #8b1a1a;
}

.phase-group.current::before {
    background: #8b1a1a;
    animation: pulse-dot 2s ease-in-out infinite;
}

.phase-group.pending::before {
    background: #fff;
    box-shadow: 0 0 0 2px #adb5bd;
}

/* Cards within phase group - no individual timeline dots */
.phase-group-cards .overview-card {
    margin-bottom: 0;
}

.phase-group-cards .overview-card::before {
    display: none;
}

.overview-card {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 20px 25px;
    background: #fff;
    border: 2px solid #e9ecef;
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.2s ease;
    position: relative;
}

.overview-card:hover:not(.disabled) {
    border-color: #8b1a1a;
    box-shadow: 0 4px 15px rgba(139, 26, 26, 0.2);
    transform: translateY(-2px);
}

.overview-card:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.overview-card .card-icon {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #8b1a1a 0%, #5c0e0e 100%);
    border-radius: 12px;
    color: #fff;
}

.overview-card .card-content {
    flex: 1;
    min-width: 0;
}

.overview-card h3 {
    margin: 0;
    font-size: 1.1rem;
    color: #333;
}

.overview-card .card-description {
    margin: 4px 0 0;
    font-size: 0.85rem;
    color: #6c757d;
}

.overview-card .card-status {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.overview-card .card-status svg {
    flex-shrink: 0;
}

.overview-card .card-status.status-draft {
    background: #fff3cd;
    color: #856404;
}

.overview-card .card-status.status-registered {
    background: #d4edda;
    color: #155724;
}

/* Done status - green with check */
.overview-card .card-status.status-done {
    background: #d4edda;
    color: #155724;
}

/* Action needed status - attention-grabbing */
.overview-card .card-status.status-action {
    background: #8b1a1a;
    color: #fff;
    animation: pulse-attention 2s ease-in-out infinite;
}

@keyframes pulse-attention {
    0%,
    100% {
        box-shadow: 0 0 0 0 rgba(139, 26, 26, 0.4);
    }
    50% {
        box-shadow: 0 0 0 8px rgba(139, 26, 26, 0);
    }
}

/* Attention-needing list rows (e.g. incoming transports on the lab screen) —
   reuses the same glow the clinician flow uses for a newly opened phase. */
.field-row.row-action {
    border-left: 3px solid #8b1a1a;
    animation: pulse-attention 2s ease-in-out infinite;
}

/* The row just saved from a list's editor, pointed out when the list is back (Phase 27 E1): the
   app's attention glow, as on the overview cards and the incoming transports, run twice and done. */
.user-row.row-just-saved {
    border-color: #8b1a1a;
    animation: pulse-attention 1s ease-in-out 2;
}

/* A read-only manager editor: its fieldsets are shown but non-interactive
   (native controls are also disabled in JS; this covers custom widgets). */
.fieldset-readonly {
    pointer-events: none;
}

/* The transported-samples manifest is navigation, not input: its cards stay clickable
   in a transport the reader may not edit. */
.fieldset-readonly #transport-samples-list {
    pointer-events: auto;
}

/* Locked status - gray */
.overview-card .card-status.status-locked {
    background: #e9ecef;
    color: #6c757d;
}

/* Legacy status classes */
.overview-card .card-status.status-completed {
    background: #d4edda;
    color: #155724;
}

.overview-card .card-status.status-current {
    background: #cce5ff;
    color: #004085;
}

.overview-card .card-status.status-pending {
    background: #e9ecef;
    color: #6c757d;
}

/* Disabled card (locked phases) */
.overview-card.disabled {
    opacity: 0.6;
    cursor: not-allowed;
    background: #f8f9fa;
}

.overview-card.disabled .card-icon {
    background: #adb5bd;
}

.overview-card.disabled:hover {
    transform: none;
    box-shadow: none;
    border-color: #e9ecef;
}

/* Biospecimen collection card styling */
.overview-card.biospecimen-card .card-icon {
    background: linear-gradient(135deg, #8b1a1a 0%, #5c0e0e 100%);
}

.overview-card.biospecimen-card .card-icon.biospecimen-icon {
    background: linear-gradient(135deg, #8b1a1a 0%, #5c0e0e 100%);
}

.overview-card.biospecimen-card:hover:not(.disabled) {
    border-color: #8b1a1a;
    box-shadow: 0 4px 15px rgba(139, 26, 26, 0.2);
}

.overview-card.biospecimen-card:focus {
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.overview-card.biospecimen-card.current .card-status.status-action {
    background: #8b1a1a;
    color: #fff;
}

.overview-card.biospecimen-card.current::before {
    background: #8b1a1a;
    box-shadow: 0 0 0 2px #8b1a1a;
    animation: pulse-dot-biospecimen 2s ease-in-out infinite;
}

@keyframes pulse-dot-biospecimen {
    0%,
    100% {
        box-shadow:
            0 0 0 2px #8b1a1a,
            0 0 0 4px rgba(139, 26, 26, 0.4);
    }
    50% {
        box-shadow:
            0 0 0 2px #8b1a1a,
            0 0 0 10px rgba(139, 26, 26, 0);
    }
}

.overview-card.biospecimen-card.completed::before {
    background: #8b1a1a;
    box-shadow: 0 0 0 2px #8b1a1a;
}

.overview-card.biospecimen-card.disabled .card-icon {
    background: #adb5bd;
}

/* Biospecimen Collection Screen */
.biospecimen-collection-screen {
    display: none;
    flex-direction: column;
    gap: 20px;
    padding: 20px 0;
}

.biospecimen-collection-header {
    display: flex;
    align-items: center;
    gap: 20px;
}

.biospecimen-collection-header h2 {
    margin: 0;
    font-size: 1.4rem;
    color: #333;
}

.biospecimen-collection-header .btn-back {
    display: flex;
    align-items: center;
    gap: 8px;
}

.biospecimen-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.biospecimen-item {
    display: flex;
    align-items: center;
    gap: 15px;
    padding: 15px 20px;
    background: #fff;
    border: 2px solid #e9ecef;
    border-radius: 10px;
    transition: all 0.2s ease;
}

.biospecimen-item:hover {
    border-color: #8b1a1a;
    box-shadow: 0 2px 8px rgba(139, 26, 26, 0.15);
}

.biospecimen-item.collected {
    background: #fdf0f0;
    border-color: #8b1a1a;
}

.biospecimen-item .biospecimen-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.biospecimen-item .biospecimen-name {
    font-weight: 600;
    color: #333;
}

.biospecimen-item .biospecimen-type {
    font-size: 0.85rem;
    color: #6c757d;
}

.biospecimen-item .biospecimen-status {
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
}

.biospecimen-item.pending .biospecimen-status {
    background: #fff3cd;
    color: #856404;
}

.biospecimen-item.collected .biospecimen-status {
    background: #d4edda;
    color: #155724;
}

.biospecimen-item .btn-collect {
    padding: 8px 16px;
    background: #8b1a1a;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-weight: 500;
    transition: background 0.2s;
}

.biospecimen-item .btn-collect:hover:not(:disabled) {
    background: #6e1515;
}

.biospecimen-item .btn-collect:disabled {
    background: #adb5bd;
    cursor: not-allowed;
}

/* Biospecimen Collection Modal */
.biospecimen-modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 20px;
}

.biospecimen-modal-content {
    background: #fff;
    border-radius: 12px;
    width: 100%;
    max-width: 780px;
    max-height: 90vh;
    overflow-y: auto;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}

.biospecimen-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 25px;
    border-bottom: 1px solid #e9ecef;
}

.biospecimen-modal-header h3 {
    margin: 0;
    font-size: 1.2rem;
    color: #333;
}

.biospecimen-modal-header .btn-close-modal {
    background: none;
    border: none;
    font-size: 1.5rem;
    color: #6c757d;
    cursor: pointer;
    padding: 0;
    line-height: 1;
}

.biospecimen-modal-header .btn-close-modal:hover {
    color: #333;
}

.biospecimen-modal-body {
    padding: 25px;
}

.biospecimen-readonly-info {
    margin-bottom: 25px;
    padding: 20px;
    background: #f8f9fa;
    border-radius: 8px;
}

.biospecimen-readonly-info h4,
.biospecimen-collection-form h4 {
    margin: 0 0 15px 0;
    font-size: 1rem;
    color: #495057;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.info-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 15px;
}

.info-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.info-item label {
    font-size: 0.75rem;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.info-item span {
    font-size: 0.95rem;
    color: #333;
    font-weight: 500;
}

.biospecimen-collection-form .form-row {
    margin-bottom: 15px;
}

.biospecimen-collection-form .form-group {
    margin-bottom: 0;
}

.biospecimen-modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 20px 25px;
    border-top: 1px solid #e9ecef;
    background: #f8f9fa;
    border-radius: 0 0 12px 12px;
}

/* Admin Dashboard Screen */
.admin-dashboard-screen {
    display: none;
    flex-direction: column;
    align-items: center;
    padding: 20px;
}

.admin-dashboard-header {
    text-align: center;
    margin-bottom: 30px;
}

.admin-dashboard-header p {
    color: #6c757d;
    font-size: 1rem;
}

/* Admin dashboard: 3 columns, degrading to 2 then 1 on narrower screens. */
.admin-dashboard-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 15px;
    max-width: 1400px;
    width: 100%;
}

@media (max-width: 1000px) {
    .admin-dashboard-cards {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 600px) {
    .admin-dashboard-cards {
        grid-template-columns: 1fr;
    }
}

.admin-dashboard-cards .overview-card::before {
    display: none;
}

/* Courier Dashboard */
.courier-dashboard-screen {
    display: none;
    flex-direction: column;
    align-items: center;
    padding: 20px;
}

.courier-dashboard-cards {
    display: grid;
    grid-template-columns: 1fr;
    gap: 15px;
    max-width: 700px;
    width: 100%;
}

/* Lab dashboard: its two entry cards sit side by side (collapse to one on mobile). */
.lab-dashboard-cards {
    grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 640px) {
    .lab-dashboard-cards {
        grid-template-columns: 1fr;
    }
}

.courier-dashboard-cards .overview-card::before {
    display: none;
}

.courier-card {
    cursor: pointer;
}

.courier-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 15px rgba(139, 26, 26, 0.2);
}

/* Toolbar above the card grid */
.courier-toolbar {
    display: flex;
    justify-content: flex-end;
    margin: 0 0 12px 0;
}

/* In-progress sites: animated icon to signal an active transport. */
.courier-card-icon-inprogress {
    animation: courier-icon-pulse 2s ease-in-out infinite;
}
.courier-card-icon-inprogress svg {
    animation: courier-truck-move 1.6s ease-in-out infinite;
}
@keyframes courier-truck-move {
    0% {
        transform: translateX(-3px);
    }
    50% {
        transform: translateX(3px);
    }
    100% {
        transform: translateX(-3px);
    }
}
@keyframes courier-icon-pulse {
    0%,
    100% {
        box-shadow:
            0 0 0 2px rgba(139, 26, 26, 0.4),
            0 0 0 4px rgba(139, 26, 26, 0.2);
    }
    50% {
        box-shadow:
            0 0 0 2px rgba(139, 26, 26, 0.6),
            0 0 0 14px rgba(139, 26, 26, 0);
    }
}

.courier-card .card-contact {
    font-size: 0.8rem;
    color: #6c757d;
    margin-top: 4px;
}

.courier-card .card-description a {
    color: #8b1a1a;
    text-decoration: none;
}

.courier-card .card-description a:hover {
    text-decoration: underline;
}

.courier-card .card-phone {
    font-size: 0.8rem;
    margin-top: 4px;
}

.courier-card .card-phone a {
    color: #8b1a1a;
    text-decoration: none;
}

.courier-card .card-phone a:hover {
    text-decoration: underline;
}

.card-badge.courier-count {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 60px;
    padding: 8px 12px;
    background: transparent;
}

.card-badge.courier-count .count-number {
    font-size: 1.8rem;
    font-weight: 700;
    color: #8b1a1a;
    line-height: 1;
}

.card-badge.courier-count .count-label {
    font-size: 0.7rem;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.courier-empty {
    text-align: center;
    color: #6c757d;
    padding: 60px 20px;
    font-size: 1.1rem;
}

/* Courier Site Detail Screen */
.courier-site-detail-screen {
    display: none;
    flex-direction: column;
    align-items: center;
    padding: 20px;
}

.courier-site-detail-cards {
    display: flex;
    flex-direction: column;
    gap: 15px;
    max-width: 700px;
    width: 100%;
}

.courier-site-detail-cards .overview-card::before {
    display: none;
}

.courier-detail-card {
    cursor: default;
    flex-direction: row !important;
    align-items: center !important;
    text-align: left !important;
    gap: 12px;
    padding: 14px 18px;
}

.courier-detail-card:hover {
    transform: none;
    box-shadow: none;
    border-color: #e9ecef;
}

.courier-detail-card h3 {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.9rem;
}

.courier-status-dot {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    border-radius: 50%;
}

.courier-status-dot.collected {
    background: #ffc107;
    box-shadow: 0 0 0 3px rgba(255, 193, 7, 0.25);
}

.courier-status-dot.in-transit {
    background: #28a745;
    box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.25);
}

.overview-card .card-status.status-chevron {
    background: transparent;
    color: #adb5bd;
    padding: 6px;
}

.overview-card:hover:not(.disabled) .card-status.status-chevron {
    color: #8b1a1a;
}

/* User Modal - two-column grid */
.user-modal-content {
    max-width: 600px;
}

.user-modal-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
}

.user-modal-grid .form-group {
    min-width: 0;
    margin-bottom: 0;
}

.user-modal-grid .form-group.full-width {
    grid-column: 1 / -1;
}

.field-hint {
    display: block;
    font-size: 0.7rem;
    color: #6c757d;
    margin-top: 4px;
}

/* A field the specimen's container leaves meaningless (`container-type.js`): shown, disabled, and
   saying why — never hidden, so the form keeps its shape. */
.form-group.field-na > label,
.form-group.field-na input,
.form-group.field-na select {
    opacity: 0.55;
}

/* User Management */
.user-management {
    max-width: 1100px;
    width: 100%;
    margin: 0 auto;
}

/* Admin list toolbar — back / fulltext search / add row above any admin list */
.admin-list-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 20px;
}

/* Infinite-scroll trigger — a thin marker below the list the observer watches. */
.infinite-sentinel {
    height: 1px;
    width: 100%;
}

.admin-list-toolbar .btn {
    flex: 0 0 120px;
    height: 40px;
    padding: 0 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

/* Square icon-only buttons in a toolbar must not stretch to the 120px text-button basis. */
.admin-list-toolbar .btn.btn-icon-only {
    flex: 0 0 40px;
    width: 40px;
    padding: 0;
}

.admin-list-toolbar input[type='text'] {
    flex: 1 1 auto;
    min-width: 0;
    height: 40px;
    padding: 8px 14px 8px 36px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 0.95rem;
    background-color: #fff;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>");
    background-repeat: no-repeat;
    background-position: 12px center;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}

.admin-list-toolbar input[type='text']:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

/* Editor header — back/cancel + title row above an admin editor */
.editor-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
    gap: 15px;
}

.editor-header h2 {
    margin: 0;
    font-size: 1.4rem;
    color: #333;
    flex: 1;
    text-align: center;
}

.editor-header .btn {
    flex: 0 0 120px;
    height: 40px;
    padding: 0 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

/* Spacer div on the right of editor-header — match button width so h2 stays centered */
.editor-header > div:last-child {
    flex: 0 0 120px;
}

.user-row {
    display: flex;
    align-items: center;
    gap: 15px;
    padding: 15px 20px;
    background: #fff;
    border: 2px solid #e9ecef;
    border-radius: 10px;
    margin-bottom: 5px;
    transition: all 0.2s ease;
}

.user-row:hover {
    border-color: #8b1a1a;
    box-shadow: 0 2px 8px rgba(139, 26, 26, 0.15);
}

.user-row.inactive {
    opacity: 0.55;
    background: #f8f9fa;
}

/* Clickable object-list cards: the whole card navigates to edit, so action
   buttons become full-height bars flush to the right edge (bb-portal style),
   kept side by side. Children center vertically; the actions strip stretches.

   `row-actions-bar` opts a row into the same full-height-bar look *without* the
   navigate-on-click semantics — for rows whose body isn't clickable (trash,
   composer field/step rows), where `cursor: pointer` would be a lie. */
.user-row.clickable-card,
.user-row.row-actions-bar {
    align-items: stretch;
    overflow: hidden;
}

.user-row.clickable-card {
    cursor: pointer;
}

/* Lab receive view: active box + temperature chart side by side. */
.receive-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    align-items: start;
    margin-top: 20px; /* separate from the "boxes on transport" group above */
}

/* Grid gap owns the spacing; zero the fieldsets' own margins so both columns
   top-align (otherwise the 2nd one picks up .builder-fieldset margin-top). */
.receive-grid > .builder-fieldset {
    margin-top: 0;
}

@media (max-width: 900px) {
    .receive-grid {
        grid-template-columns: 1fr;
    }
    .receive-grid > .builder-fieldset + .builder-fieldset {
        margin-top: 16px;
    }
}

/* Highlight the currently-active box in the receive list. */
.user-row.box-switch.active-box-row {
    outline: 2px solid var(--primary, #2b6cb0);
    outline-offset: -2px;
    background: rgba(43, 108, 176, 0.06);
}

.user-row.clickable-card > *,
.user-row.row-actions-bar > * {
    align-self: center;
}

.user-row.clickable-card > .user-row-actions,
.user-row.row-actions-bar > .user-row-actions {
    align-self: stretch;
    gap: 0;
    margin: -15px -20px -15px 0;
}

.user-row.clickable-card .user-row-actions .btn-icon,
.user-row.row-actions-bar .user-row-actions .btn-icon {
    width: 48px;
    height: auto;
    border-radius: 0;
    align-self: stretch;
    transition: background 0.15s ease;
}

/* Full-height bars: a flat colour fill reads better than the icon scale. Each
   button's background is a light tint of its icon colour, intensifying on
   hover/press (issues #2/#3). */
.user-row.clickable-card .user-row-actions .btn-icon:hover,
.user-row.row-actions-bar .user-row-actions .btn-icon:hover {
    transform: none;
}

.user-row.clickable-card .btn-icon-delete,
.user-row.row-actions-bar .btn-icon-delete {
    background: rgba(220, 53, 69, 0.08);
}
.user-row.clickable-card .btn-icon-delete:hover,
.user-row.clickable-card .btn-icon-delete:active,
.user-row.row-actions-bar .btn-icon-delete:hover,
.user-row.row-actions-bar .btn-icon-delete:active {
    background: rgba(220, 53, 69, 0.22);
}

/* Restore (trash) + expand/collapse (composer rows) + versions toggle + open-structure
   (storage units): neutral blue, same bar. The versions toggle also stays tinted while
   active (history expanded). */
.user-row.clickable-card .btn-icon-restore,
.user-row.row-actions-bar .btn-icon-restore,
.user-row.row-actions-bar .btn-expand-field,
.user-row.clickable-card .btn-form-versions,
.user-row.row-actions-bar .btn-form-versions,
.user-row.clickable-card .btn-browse,
.user-row.row-actions-bar .btn-browse,
.user-row.clickable-card .btn-entries,
.user-row.row-actions-bar .btn-entries {
    color: #1a4e8b;
    background: rgba(26, 78, 139, 0.1);
}
.user-row.clickable-card .btn-icon-restore:hover,
.user-row.clickable-card .btn-icon-restore:active,
.user-row.row-actions-bar .btn-icon-restore:hover,
.user-row.row-actions-bar .btn-icon-restore:active,
.user-row.row-actions-bar .btn-expand-field:hover,
.user-row.row-actions-bar .btn-expand-field:active,
.user-row.clickable-card .btn-form-versions:hover,
.user-row.clickable-card .btn-form-versions:active,
.user-row.clickable-card .btn-form-versions.active,
.user-row.row-actions-bar .btn-form-versions:hover,
.user-row.row-actions-bar .btn-form-versions:active,
.user-row.row-actions-bar .btn-form-versions.active,
.user-row.clickable-card .btn-browse:hover,
.user-row.clickable-card .btn-browse:active,
.user-row.row-actions-bar .btn-browse:hover,
.user-row.row-actions-bar .btn-browse:active,
.user-row.clickable-card .btn-entries:hover,
.user-row.clickable-card .btn-entries:active,
.user-row.row-actions-bar .btn-entries:hover,
.user-row.row-actions-bar .btn-entries:active {
    background: rgba(26, 78, 139, 0.22);
}

/* "Go to the linked record" (builder cards) — violet. It sits next to the blue
   expand bar on the same card, and a second blue-family tint there was
   indistinguishable from it; navigation-away needs its own hue, not another blue. */
.user-row.clickable-card .btn-icon-open-record,
.user-row.row-actions-bar .btn-icon-open-record {
    color: #6d28d9;
    background: rgba(109, 40, 217, 0.1);
}
.user-row.clickable-card .btn-icon-open-record:hover,
.user-row.clickable-card .btn-icon-open-record:active,
.user-row.row-actions-bar .btn-icon-open-record:hover,
.user-row.row-actions-bar .btn-icon-open-record:active {
    background: rgba(109, 40, 217, 0.24);
}

.user-row.clickable-card .btn-icon-deactivate {
    background: rgba(230, 167, 0, 0.12);
}
.user-row.clickable-card .btn-icon-deactivate:hover,
.user-row.clickable-card .btn-icon-deactivate:active {
    background: rgba(230, 167, 0, 0.28);
}

.user-row.clickable-card .btn-icon-activate {
    background: rgba(40, 167, 69, 0.1);
}

/* An enrollment's specimens (enrollment rows): green, the specimen-collection hue, beside
   the violet clinical-data bar. */
.user-row.clickable-card .btn-pd-specimens,
.user-row.clickable-card .btn-donor-specimens {
    color: #1f7a37;
    background: rgba(40, 167, 69, 0.1);
}
.user-row.clickable-card .btn-pd-specimens:hover,
.user-row.clickable-card .btn-pd-specimens:active,
.user-row.clickable-card .btn-donor-specimens:hover,
.user-row.clickable-card .btn-donor-specimens:active {
    background: rgba(40, 167, 69, 0.24);
}
.user-row.clickable-card .btn-icon-activate:hover,
.user-row.clickable-card .btn-icon-activate:active {
    background: rgba(40, 167, 69, 0.24);
}

/* Directory summary (project rows) — bright teal. It shares the row with the navy
   prep-batches bar, and a dark teal was indistinguishable from it at 48px; this one
   is lighter and clearly green-leaning. */
.user-row.clickable-card .btn-icon-directory,
.user-row.row-actions-bar .btn-icon-directory {
    color: #0d9488;
    background: rgba(13, 148, 136, 0.12);
}
.user-row.clickable-card .btn-icon-directory:hover,
.user-row.clickable-card .btn-icon-directory:active,
.user-row.row-actions-bar .btn-icon-directory:hover,
.user-row.row-actions-bar .btn-icon-directory:active {
    background: rgba(13, 148, 136, 0.26);
}

.user-row.clickable-card .btn-icon-batches {
    color: #1a4e8b;
    background: rgba(26, 78, 139, 0.1);
}
.user-row.clickable-card .btn-icon-batches:hover,
.user-row.clickable-card .btn-icon-batches:active {
    background: rgba(26, 78, 139, 0.22);
}

/* Audit trail + clinical data (Phases 25/26) — the two per-row actions that open a record
   rather than change one. Amber for the history, violet for the clinical capture: both had no
   rule at all and rendered as plain grey glyphs with no hover, the only buttons in the bar that
   did not answer the pointer. */
.user-row.clickable-card .btn-provenance,
.user-row.row-actions-bar .btn-provenance {
    color: #b45309;
    background: rgba(180, 83, 9, 0.1);
}
.user-row.clickable-card .btn-provenance:hover,
.user-row.clickable-card .btn-provenance:active,
.user-row.row-actions-bar .btn-provenance:hover,
.user-row.row-actions-bar .btn-provenance:active {
    background: rgba(180, 83, 9, 0.22);
}
.user-row.clickable-card .btn-clinical,
.user-row.clickable-card .btn-clinical-pd,
.user-row.row-actions-bar .btn-clinical,
.user-row.row-actions-bar .btn-clinical-pd {
    color: #7c3aed;
    background: rgba(124, 58, 237, 0.1);
}
.user-row.clickable-card .btn-clinical:hover,
.user-row.clickable-card .btn-clinical:active,
.user-row.clickable-card .btn-clinical-pd:hover,
.user-row.clickable-card .btn-clinical-pd:active,
.user-row.row-actions-bar .btn-clinical:hover,
.user-row.row-actions-bar .btn-clinical:active,
.user-row.row-actions-bar .btn-clinical-pd:hover,
.user-row.row-actions-bar .btn-clinical-pd:active {
    background: rgba(124, 58, 237, 0.22);
}

/* Device-logbook + SOP-run history + SPREC actions: a persistent light-blue fill like
   the others. All three open a read-only record of what happened. */
.user-row.clickable-card .btn-logbook,
.user-row.clickable-card .btn-sop-runs,
.user-row.clickable-card .btn-sprec {
    color: #1565c0;
    background: rgba(21, 101, 192, 0.1);
}
.user-row.clickable-card .btn-logbook:hover,
.user-row.clickable-card .btn-logbook:active,
.user-row.clickable-card .btn-sop-runs:hover,
.user-row.clickable-card .btn-sop-runs:active,
.user-row.clickable-card .btn-sprec:hover,
.user-row.clickable-card .btn-sprec:active {
    background: rgba(21, 101, 192, 0.24);
}

/* Specimen-collection cards (clinician flow): print = blue, collect = green,
   undo = amber. Undo reverts a record of a physical draw, so it is cautionary
   rather than destructive — amber, not the red of a delete bar. */
.user-row.row-actions-bar .btn-icon-print {
    color: #1565c0;
    background: rgba(21, 101, 192, 0.1);
}
.user-row.row-actions-bar .btn-icon-print:hover,
.user-row.row-actions-bar .btn-icon-print:active {
    background: rgba(21, 101, 192, 0.24);
}

.user-row.row-actions-bar .btn-icon-collect {
    color: #1f7a37;
    background: rgba(40, 167, 69, 0.1);
}
.user-row.row-actions-bar .btn-icon-collect:hover,
.user-row.row-actions-bar .btn-icon-collect:active {
    background: rgba(40, 167, 69, 0.24);
}

.user-row.row-actions-bar .btn-icon-undo {
    color: #96741d;
    background: rgba(230, 167, 0, 0.12);
}
.user-row.row-actions-bar .btn-icon-undo:hover,
.user-row.row-actions-bar .btn-icon-undo:active {
    background: rgba(230, 167, 0, 0.28);
}

/* Correct (supersede a logbook entry): a full-height red bar in the edit family. */
.user-row.clickable-card .btn-correct,
.user-row.row-actions-bar .btn-correct {
    color: #8b1a1a;
    background: rgba(139, 26, 26, 0.08);
}
.user-row.clickable-card .btn-correct:hover,
.user-row.clickable-card .btn-correct:active,
.user-row.row-actions-bar .btn-correct:hover,
.user-row.row-actions-bar .btn-correct:active {
    background: rgba(139, 26, 26, 0.2);
}

/* Rename (a storage container): the secondary amber. It shares a bar with the red delete,
   and a second warm red there was indistinguishable from it at a glance. */
.user-row.clickable-card .btn-node-rename,
.user-row.row-actions-bar .btn-node-rename {
    color: #8a6d00;
    background: rgba(230, 167, 0, 0.14);
}
.user-row.clickable-card .btn-node-rename:hover,
.user-row.clickable-card .btn-node-rename:active,
.user-row.row-actions-bar .btn-node-rename:hover,
.user-row.row-actions-bar .btn-node-rename:active {
    background: rgba(230, 167, 0, 0.3);
}

/* Duplicate action: a violet fill, same full-height bar as the others. */
.user-row.clickable-card .btn-duplicate,
.user-row.row-actions-bar .btn-duplicate {
    color: #7c3aed;
    background: rgba(124, 58, 237, 0.1);
}
.user-row.clickable-card .btn-duplicate:hover,
.user-row.clickable-card .btn-duplicate:active,
.user-row.row-actions-bar .btn-duplicate:hover,
.user-row.row-actions-bar .btn-duplicate:active {
    background: rgba(124, 58, 237, 0.24);
}

.user-row-info {
    flex: 1;
    min-width: 0;
}

.user-row-name {
    font-weight: 600;
    color: #333;
    font-size: 1rem;
}

.user-row-username {
    font-size: 0.8rem;
    color: #6c757d;
    margin-top: 2px;
}

.role-badge {
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.role-badge.role-admin {
    background: rgba(139, 26, 26, 0.15);
    color: #8b1a1a;
}

/* Clinician gets its own hue rather than sharing admin's brand red — the two are the
   roles most often confused, and the badge is the only thing telling them apart in the
   list. Violet, and specifically the blue-leaning one: the GDPR *status* badge next to
   it is a magenta-leaning purple (#6a1b9a), so they must not converge. */
.role-badge.role-clinician {
    background: rgba(109, 40, 217, 0.15);
    color: #6d28d9;
}

.role-badge.role-courier {
    background: rgba(26, 78, 139, 0.15);
    color: #1a4e8b;
}

.role-badge.role-lab {
    background: rgba(15, 118, 110, 0.15);
    color: #0f766e;
}

.status-badge {
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.status-badge.active {
    background: #d4edda;
    color: #155724;
}

.status-badge.inactive {
    background: #f8d7da;
    color: #721c24;
}

.status-badge.locked {
    background: #fff3cd;
    color: #856404;
}

.status-badge.gdpr-locked {
    background: #e8d4f0;
    color: #6a1b9a;
}

/* Form Composer: a field shown only while its condition holds. Beside the key, small, in the
   neutral blue of information — it says how the field behaves, not that anything is wrong. */
.status-badge.field-cond-badge {
    margin-left: 6px;
    padding: 1px 8px;
    background: rgba(26, 78, 139, 0.12);
    color: #1a4e8b;
}

/* The option picker of a condition on a choice field: one tick per option. */
.cond-options {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
}

.cond-option {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-weight: normal;
}

.you-badge {
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
    align-self: center;
    background: #fff3cd;
    color: #856404;
}

.user-row-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

/* Code-system "code prefix" toggle cell: inline label + switch, centered. */
.cs-prefix-cell {
    align-items: center;
    gap: 8px;
}

.cs-prefix-label {
    font-size: 0.72rem;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.btn-sm {
    padding: 6px 12px;
    font-size: 0.8rem;
    font-weight: 600;
    border: 2px solid transparent;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    white-space: nowrap;
}

.btn-sm.btn-primary {
    background: #8b1a1a;
    border-color: #8b1a1a;
    color: #fff;
}

.btn-sm.btn-primary:hover {
    background: #6e1515;
    border-color: #6e1515;
}

.btn-sm.btn-warning {
    background: #ffc107;
    border-color: #ffc107;
    color: #333;
}

.btn-sm.btn-warning:hover {
    background: #e0a800;
    border-color: #e0a800;
}

.btn-sm.btn-success {
    background: #28a745;
    border-color: #28a745;
    color: #fff;
}

.btn-sm.btn-success:hover {
    background: #218838;
    border-color: #218838;
}

.btn-sm.btn-danger,
.btn-small.btn-danger,
.btn.btn-danger {
    background: #dc3545;
    border-color: #dc3545;
    color: #fff;
}

.btn-sm.btn-danger:hover,
.btn-small.btn-danger:hover,
.btn.btn-danger:hover {
    background: #c82333;
    border-color: #c82333;
}

.btn-icon {
    width: 32px;
    height: 32px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s ease;
    background: transparent;
    color: #6c757d;
}

.btn-icon:hover {
    transform: scale(1.1);
}

.btn-icon.btn-icon-edit {
    color: #8b1a1a;
}

.btn-icon.btn-icon-edit:hover {
    background: rgba(139, 26, 26, 0.1);
    color: #6e1515;
}

/* Device logbook action — blue, with a hover/active wash. */
.btn-icon.btn-logbook {
    color: #1565c0;
}

.btn-icon.btn-logbook:hover {
    background: rgba(21, 101, 192, 0.12);
    color: #0d47a1;
}

.btn-icon.btn-logbook:active {
    background: rgba(21, 101, 192, 0.22);
}

.btn-icon.btn-provenance {
    color: #b45309;
}

.btn-icon.btn-provenance:hover {
    background: rgba(180, 83, 9, 0.12);
    color: #92400e;
}

.btn-icon.btn-provenance:active {
    background: rgba(180, 83, 9, 0.22);
}

.btn-icon.btn-clinical,
.btn-icon.btn-clinical-pd {
    color: #7c3aed;
}

.btn-icon.btn-clinical:hover,
.btn-icon.btn-clinical-pd:hover {
    background: rgba(124, 58, 237, 0.12);
    color: #5b21b6;
}

.btn-icon.btn-clinical:active,
.btn-icon.btn-clinical-pd:active {
    background: rgba(124, 58, 237, 0.22);
}

/* SPREC compute action — blue, same family as the device-logbook button. */
.btn-icon.btn-sprec {
    color: #1565c0;
}

.btn-icon.btn-sprec:hover {
    background: rgba(21, 101, 192, 0.12);
    color: #0d47a1;
}

.btn-icon.btn-sprec:active {
    background: rgba(21, 101, 192, 0.22);
}

.btn-icon.btn-icon-deactivate {
    color: #e6a700;
}

.btn-icon.btn-icon-deactivate:hover {
    background: rgba(255, 193, 7, 0.15);
    color: #cc9500;
}

/* Specimen-card actions outside a full-height bar (same colour family as the bars). */
.btn-icon.btn-icon-print {
    color: #1565c0;
}

.btn-icon.btn-icon-print:hover {
    background: rgba(21, 101, 192, 0.12);
    color: #0d47a1;
}

.btn-icon.btn-icon-collect {
    color: #1f7a37;
}

.btn-icon.btn-icon-collect:hover {
    background: rgba(40, 167, 69, 0.12);
    color: #196b2e;
}

.btn-icon.btn-icon-undo {
    color: #96741d;
}

.btn-icon.btn-icon-undo:hover {
    background: rgba(230, 167, 0, 0.18);
    color: #7a5e17;
}

.btn-icon.btn-icon-activate {
    color: #28a745;
}

.btn-icon.btn-icon-activate:hover {
    background: rgba(40, 167, 69, 0.1);
    color: #218838;
}

/* Family (parent / siblings / children) button — green like the version-cut action. */
.btn-icon.btn-family {
    color: #28a745;
}

.user-row.clickable-card .btn-family {
    background: rgba(40, 167, 69, 0.1);
}

.user-row.clickable-card .btn-family:hover,
.user-row.clickable-card .btn-family:active {
    background: rgba(40, 167, 69, 0.24);
    color: #218838;
}

.btn-icon.btn-icon-batches {
    color: #1a4e8b;
}

.btn-icon.btn-icon-batches:hover {
    background: rgba(26, 78, 139, 0.1);
    color: #154273;
}

.btn-icon.btn-icon-delete {
    color: #dc3545;
}

.btn-icon.btn-icon-delete:hover {
    background: rgba(220, 53, 69, 0.1);
    color: #c82333;
}

/* Disabled icon button: dimmed + not-allowed, but still hoverable so the
   data-tooltip can explain why it's unavailable. No scale/colour intensify. */
.btn-icon.is-disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.btn-icon.is-disabled:hover {
    transform: none;
}

.user-row.clickable-card .user-row-actions .btn-icon.is-disabled:hover {
    background: rgba(0, 0, 0, 0.03);
}

/* Disabled look that still receives hover (so the data-tooltip explains why).
   Uses a class, not the native [disabled] attribute, which would suppress the
   pointer events the tooltip relies on. */
.btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn.is-disabled:hover {
    transform: none;
}

.user-row-detail {
    font-size: 0.8rem;
    color: #6c757d;
    margin-top: 2px;
}

/* Barcodes / identifiers on a card's detail line — a light monospace chip, matching
   the scan lists (.pending-specimen-sid code). */
.user-row-detail code {
    background: #f1f3f5;
    padding: 1px 6px;
    border-radius: 4px;
    font-family: ui-monospace, monospace;
    font-size: 0.78rem;
    color: #495057;
    letter-spacing: 0.03em;
}

/* Audit Log */
.audit-log {
    max-width: 1100px;
    width: 100%;
    margin: 0 auto;
}

.audit-log-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
    gap: 15px;
}

.audit-log-header h2 {
    margin: 0;
    font-size: 1.4rem;
    color: #333;
    flex: 1;
    text-align: center;
}

.audit-controls {
    display: flex;
    gap: 10px;
    margin-bottom: 15px;
    align-items: center;
    flex-wrap: wrap;
}

.audit-controls input[type='text'] {
    padding: 8px 12px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 0.85rem;
    background: #fff;
    transition: border-color 0.2s;
    flex: 1;
    min-width: 150px;
}

.audit-controls input[type='text']:focus {
    outline: none;
    border-color: #8b1a1a;
}

.audit-controls select {
    padding: 8px 12px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 0.85rem;
    background: #fff;
    cursor: pointer;
    transition: border-color 0.2s;
}

.filter-active {
    border-color: #8b1a1a !important;
}

.audit-controls select:focus {
    outline: none;
    border-color: #8b1a1a;
}

.audit-table {
    border: 2px solid #e9ecef;
    border-radius: 10px;
    overflow: hidden;
}

.audit-header-row {
    display: flex;
    gap: 10px;
    padding: 10px 15px;
    background: #f8f9fa;
    border-bottom: 2px solid #e9ecef;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #6c757d;
}

.audit-row {
    display: flex;
    gap: 10px;
    padding: 10px 15px;
    border-bottom: 1px solid #dee2e6;
    font-size: 0.85rem;
    transition: background 0.15s;
    align-items: center;
}

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

.audit-row:hover {
    background: #f8f9fa;
}

.audit-cell-time {
    width: 140px;
    flex-shrink: 0;
    font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
    font-size: 0.8rem;
    color: #495057;
}

.audit-cell-actor {
    width: 150px;
    flex-shrink: 0;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.audit-cell-category {
    width: 110px;
    flex-shrink: 0;
}

.audit-cell-action {
    width: 220px;
    flex-shrink: 0;
    display: flex;
    gap: 6px;
    align-items: center;
    min-width: 0;
}

.audit-action-name {
    font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
    font-size: 0.8rem;
    color: #333;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.audit-cell-object {
    flex: 1;
    min-width: 0;
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
    color: #333;
}

.audit-cell-expand {
    width: 20px;
    flex-shrink: 0;
    text-align: center;
    color: #6c757d;
}

/* Category tick-boxes. `access` is one row per HTTP request and starts unticked. */
.audit-categories {
    display: flex;
    gap: 14px;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.audit-cat-toggle {
    display: flex;
    gap: 5px;
    align-items: center;
    cursor: pointer;
    user-select: none;
}

.audit-cat-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.3px;
    white-space: nowrap;
}

.audit-cat-badge.cat-domain {
    background: rgba(13, 110, 253, 0.12);
    color: #0a58ca;
}

.audit-cat-badge.cat-security {
    background: rgba(220, 53, 69, 0.14);
    color: #b02a37;
}

.audit-cat-badge.cat-system {
    background: rgba(108, 117, 125, 0.16);
    color: #52585d;
}

.audit-cat-badge.cat-access {
    background: rgba(25, 135, 84, 0.12);
    color: #146c43;
}

/* Only a refusal or an error earns a badge; `ok` is the overwhelming majority. */
.audit-outcome-badge {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 10px;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    white-space: nowrap;
}

.audit-outcome-badge.outcome-denied {
    background: rgba(255, 193, 7, 0.22);
    color: #7a5c00;
}

.audit-outcome-badge.outcome-rejected {
    background: rgba(108, 117, 125, 0.16);
    color: #495057;
}

.audit-outcome-badge.outcome-error {
    background: rgba(220, 53, 69, 0.16);
    color: #b02a37;
}

.audit-justification {
    color: #6c757d;
    font-style: italic;
    font-size: 0.8rem;
}

/* Drill-down to everything one request did. */
.audit-req-link {
    border: none;
    background: none;
    cursor: pointer;
    color: #6c757d;
    font-size: 0.9rem;
    padding: 0 2px;
    line-height: 1;
}

.audit-req-link:hover {
    color: #8b1a1a;
}

/* Expandable rows carry a decrypted before/after diff, fetched on open. */
.audit-row-expandable {
    cursor: pointer;
}

.audit-row-expandable:focus-visible {
    outline: 2px solid #8b1a1a;
    outline-offset: -2px;
}

.audit-expander {
    display: inline-block;
    transition: transform 0.15s;
}

.audit-row-expandable.expanded {
    background: #f8f9fa;
}

.audit-row-expandable.expanded .audit-expander {
    transform: rotate(90deg);
}

.audit-diff-row {
    padding: 10px 15px 14px 165px;
    background: #f8f9fa;
    border-bottom: 1px solid #dee2e6;
    font-size: 0.82rem;
}

.audit-diff {
    border-collapse: collapse;
}

.audit-diff th {
    text-align: left;
    font-weight: 600;
    color: #495057;
    padding: 2px 14px 2px 0;
    white-space: nowrap;
    vertical-align: top;
}

.audit-diff td {
    padding: 2px 8px 2px 0;
    vertical-align: top;
}

.audit-diff-from {
    color: #b02a37;
}

.audit-diff-to {
    color: #146c43;
}

.audit-diff-arrow {
    color: #6c757d;
}

.audit-diff-empty,
.audit-diff-loading {
    color: #6c757d;
    font-style: italic;
}

/* A diff that would not decrypt — the stored token, shown as visibly unreadable. */
.audit-diff-raw {
    color: #b02a37;
    font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
    font-size: 0.75rem;
    word-break: break-all;
}

/* Donor history (DSAR): the one read that demands a stated reason. */
.audit-dsar {
    margin-bottom: 12px;
    font-size: 0.85rem;
}

.audit-dsar summary {
    cursor: pointer;
    color: #8b1a1a;
    font-weight: 500;
}

.audit-dsar-note {
    color: #6c757d;
    margin: 6px 0;
}

.audit-dsar-fields {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
}

.audit-dsar-fields input[type='number'] {
    width: 120px;
}

.audit-dsar-fields input[type='text'] {
    flex: 1;
    min-width: 200px;
}

.audit-dsar-fields input {
    padding: 8px 12px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 0.85rem;
    background: #fff;
}

.audit-dsar-fields input:focus {
    outline: none;
    border-color: #8b1a1a;
}

/* Active drill-downs, shown as removable chips so a filtered list never looks unfiltered. */
.audit-chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.audit-chip {
    padding: 4px 10px;
    border: 2px solid #8b1a1a;
    border-radius: 14px;
    background: rgba(139, 26, 26, 0.08);
    color: #8b1a1a;
    cursor: pointer;
    font-size: 0.78rem;
    font-weight: 500;
}

.audit-chip code {
    font-size: 0.75rem;
}

.audit-chip:hover {
    background: rgba(139, 26, 26, 0.16);
}

.audit-empty {
    text-align: center;
    color: #6c757d;
    padding: 40px;
}

/* GDPR Donor Search */
.gdpr-search-wrapper {
    position: relative;
    margin-bottom: 20px;
    max-width: 1100px;
    width: 100%;
    text-align: center;
}

.gdpr-search-input {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 0.95rem;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
    background: #fff;
    text-align: left;
}

.gdpr-search-input:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.gdpr-search-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border: 2px solid #adb5bd;
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 240px;
    overflow-y: auto;
    z-index: 100;
    display: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

/* The person picked on the GDPR screen (QA4 C4-12): their actions, then their enrollments. */
.gdpr-subject {
    max-width: 900px;
    margin: 16px auto 0;
}

.gdpr-subject-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 12px;
}

.gdpr-subject-heading {
    font-size: 1rem;
    margin: 12px 0 8px;
}

.gdpr-donor-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    background: #8b1a1a;
    color: #fff;
    border-radius: 20px;
    font-size: 0.9rem;
    font-weight: 500;
}

.gdpr-chip-label {
    white-space: nowrap;
}

.gdpr-chip-dismiss {
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.8);
    font-size: 1.2rem;
    cursor: pointer;
    padding: 0 2px;
    line-height: 1;
    transition: color 0.15s;
}

.gdpr-chip-dismiss:hover {
    color: #fff;
}

/* Responsive */
@media (max-width: 768px) {
    body {
        padding: 10px;
    }

    .wizard-header {
        padding: 20px 0;
    }

    /* Narrow screens stack the columns and drop the logo entirely, so the row-height
       floor from the desktop layout must not apply here. */
    .header-top {
        flex-direction: column;
        align-items: stretch;
        gap: 12px;
        min-height: 0;
    }

    .header-logo-link {
        display: none;
    }

    .header-titles {
        align-items: center;
        text-align: center;
    }

    .header-actions {
        justify-content: flex-end;
        padding: 0 24px;
    }

    .donor-info-summary.visible {
        display: flex;
        flex-direction: column;
        align-items: center;
        text-align: center;
        font-weight: bold;
        padding-top: 12px;
    }

    .donor-info-summary span + span::before {
        content: none;
    }

    .btn.btn-icon-only {
        flex: 0;
        min-width: 60px;
        height: 60px;
    }

    .wizard-header h1 {
        font-size: 1.4rem;
        text-align: center;
    }

    .step label {
        display: none;
    }

    .step span {
        width: 32px;
        height: 32px;
        font-size: 0.85rem;
    }

    .wizard-content {
        padding: 20px 0;
    }

    .form-row {
        flex-direction: column;
        gap: 15px;
    }

    .form-group {
        min-width: 100%;
    }

    .wizard-footer {
        flex-wrap: wrap;
    }

    .btn {
        flex: 1;
        min-width: 100px;
    }

    /* Overview screen responsive */
    .overview-card {
        flex-direction: column;
        text-align: center;
        padding: 20px;
    }

    .overview-card .card-icon {
        margin-bottom: 10px;
    }

    .overview-card .card-content {
        width: 100%;
    }

    .overview-card .card-status {
        margin-left: 0;
        margin-top: 10px;
    }

    /* Timeline adjustments for mobile */
    .overview-cards {
        padding-left: 30px;
    }

    .overview-cards::before {
        left: 10px;
    }

    .overview-cards > .overview-card::before {
        left: -24px;
        width: 10px;
        height: 10px;
    }

    .phase-group::before {
        left: -24px;
        width: 10px;
        height: 10px;
    }

    /* Audit log responsive */
    .audit-cell-time {
        width: 110px;
        font-size: 0.75rem;
    }

    .audit-row,
    .audit-header-row {
        flex-wrap: wrap;
    }

    /* Fixed cell widths stop fitting once the row wraps; let every cell take the width it
       needs and keep the time stamp as the one anchor. */
    .audit-cell-actor,
    .audit-cell-category,
    .audit-cell-action {
        width: auto;
    }

    .audit-controls {
        flex-direction: column;
    }

    .audit-controls input[type='text'],
    .audit-controls select {
        width: 100%;
    }

    /* The diff is indented under the timestamp column, which no longer exists once rows wrap. */
    .audit-diff-row {
        padding-left: 15px;
    }

    /* User management responsive */
    .user-row {
        flex-wrap: wrap;
    }

    .user-row-actions {
        width: 100%;
        justify-content: flex-end;
        margin-top: 8px;
    }

    /* Clickable cards: on narrow screens the action bars move to the bottom of
       the card as one full-width strip, each button equal width and flush to
       the card's bottom/side edges. */
    .user-row.clickable-card > .user-row-actions,
    .user-row.row-actions-bar > .user-row-actions {
        width: calc(100% + 40px);
        margin: 10px -20px -15px -20px;
        border-top: 1px solid #e9ecef;
    }

    .user-row.clickable-card .user-row-actions .btn-icon,
    .user-row.row-actions-bar .user-row-actions .btn-icon {
        flex: 1;
        width: auto;
        height: 44px;
        align-self: auto;
    }

    .admin-list-toolbar {
        flex-wrap: wrap;
    }

    /* Mobile: back + add stay on top row; search wraps below them full-width. */
    .admin-list-toolbar input[type='text'] {
        flex: 1 1 100%;
        order: 2;
    }

    .admin-list-toolbar .btn {
        order: 1;
    }

    .editor-header {
        flex-wrap: wrap;
    }
}

/* Builder Screens */

.field-library {
    max-width: 1100px;
}

.field-row .field-row-badges {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
}

.field-flag-badge {
    padding: 2px 8px;
    border-radius: 20px;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.field-flag-required {
    background: #fff3cd;
    color: #856404;
}

.field-flag-pii {
    background: #f8d7da;
    color: #721c24;
}

.field-flag-searchable {
    background: #d4edda;
    color: #155724;
}

.field-flag-size {
    background: #d6e9f8;
    color: #0c5460;
}

.role-badge.type-text {
    background: rgba(108, 117, 125, 0.15);
    color: #495057;
}

.role-badge.type-number {
    background: rgba(0, 123, 255, 0.15);
    color: #0056b3;
}

.role-badge.type-date {
    background: rgba(111, 66, 193, 0.15);
    color: #6f42c1;
}

.role-badge.type-boolean {
    background: rgba(40, 167, 69, 0.15);
    color: #155724;
}

.role-badge.type-select {
    background: rgba(255, 193, 7, 0.15);
    color: #856404;
}

.role-badge.type-ontology {
    background: rgba(23, 162, 184, 0.15);
    color: #0c5460;
}

.role-badge.type-readonly {
    background: rgba(108, 117, 125, 0.1);
    color: #6c757d;
}

.field-empty-state {
    text-align: center;
    color: #6c757d;
    padding: 40px;
}

/* A form's lists could not be loaded: what happened, and "Try again" (builder/load-retry.js). */
.load-failed-notice {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 16px;
    padding: 10px 14px;
    border: 1px solid #ffc107;
    border-radius: 8px;
    background: #fff3cd;
    color: #856404;
}

.field-editor-hidden {
    display: none;
}

.field-editor-panel {
    animation: fadeIn 0.3s ease;
}

.builder-fieldset {
    border: 2px solid rgba(139, 26, 26, 0.25);
    border-radius: 10px;
    padding: 20px;
    margin-top: 20px;
}

.builder-fieldset:first-of-type {
    margin-top: 0;
}

.builder-fieldset legend {
    font-size: 0.85rem;
    font-weight: 600;
    color: #8b1a1a;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    padding: 0 8px;
}

.builder-field-config-inputs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 15px;
    margin-bottom: 15px;
}

/* Bilingual options editor: value | EN | SK | remove. */
.opt-rows-header,
.opt-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 32px;
    gap: 8px;
    align-items: center;
}
.opt-rows-header {
    font-size: 0.8rem;
    color: #6c757d;
    margin: 4px 0;
}
.opt-row {
    margin-bottom: 6px;
}
.opt-row input {
    width: 100%;
}
.opt-add {
    margin-top: 4px;
}

.builder-field-config-toggles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 15px;
    margin-top: 15px;
}

.builder-toggle-group {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

.builder-toggle-group label {
    font-size: 0.85rem;
    font-weight: 600;
    color: #495057;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    white-space: nowrap;
    margin-bottom: 0;
}

.field-size-select {
    padding: 2px 6px;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    font-size: 0.75rem;
    width: 44px;
}

/* The wizard step's version pin shares the compact look but holds a word ("latest" /
   "najnovšia"), not a single digit. */
.step-form-version.field-size-select {
    width: auto;
    min-width: 6.5rem;
}

.btn-expand-field {
    color: #6c757d;
    transition: color 0.15s;
}

.btn-expand-field.active {
    color: var(--clr-primary, #495057);
}

.field-settings-panel {
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-top: none;
    border-radius: 0 0 8px 8px;
    padding: 12px 16px;
    margin: -4px 0 8px;
}

.field-settings-columns {
    display: flex;
    gap: 12px;
}

.field-settings-columns > fieldset {
    flex: 1;
    min-width: 0;
}

@media (max-width: 900px) {
    .field-settings-columns {
        flex-direction: column;
    }
}

.builder-fieldset-nested {
    border: 1px solid #dee2e6;
    margin: 0;
    padding: 10px 12px;
}

.builder-fieldset-nested legend {
    font-size: 0.75rem;
    padding: 0 6px;
}

.rule-card {
    position: relative;
    border: 1px solid #dee2e6;
    border-radius: 6px;
    padding: 10px 30px 10px 10px;
    margin-bottom: 6px;
    background: #fff;
}

.rule-card-remove {
    position: absolute;
    top: 6px;
    right: 6px;
    background: none;
    border: none;
    font-size: 1.1rem;
    color: #adb5bd;
    cursor: pointer;
    line-height: 1;
    padding: 2px 4px;
}

.rule-card-remove:hover {
    color: var(--clr-error, #dc3545);
}

.rule-card-fields {
    display: flex;
    gap: 8px;
    align-items: end;
}

.rule-card-field {
    flex: 1;
    min-width: 0;
}

.rule-card-field label {
    display: block;
    font-size: 0.7rem;
    color: #6c757d;
    margin-bottom: 2px;
}

.rule-card-field select,
.rule-card-field input {
    width: 100%;
    font-size: 0.8rem;
    padding: 4px 6px;
}

@media (max-width: 600px) {
    .rule-card-fields {
        flex-direction: column;
    }
}

/* Inline validation messages in wizard forms */
.validation-message {
    font-size: 0.75rem;
    margin-top: 4px;
    padding: 2px 0;
}

.validation-error {
    color: var(--clr-error, #dc3545);
}

.validation-warning {
    color: var(--clr-warning, #ffc107);
}

.form-group.has-error input,
.form-group.has-error select,
.form-group.has-error textarea {
    border-color: var(--clr-error, #dc3545);
}

/* Section item in form composer */
.section-item {
    background: #f0f0f0;
    border-left: 3px solid #6c757d;
}

.cond-group-toggle {
    display: flex;
    gap: 4px;
    margin-bottom: 6px;
}

.toggle-pill {
    padding: 3px 10px;
    font-size: 0.7rem;
    font-weight: 600;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    cursor: pointer;
    color: #6c757d;
    background: #fff;
}

.toggle-pill.active {
    background: rgba(139, 26, 26, 0.1);
    border-color: rgba(139, 26, 26, 0.4);
    color: #8b1a1a;
}

/* A group nested in a condition (QA4 B4-3): a box of its own, its rows indented inside it. */
.cond-group {
    position: relative;
    border: 1px dashed #ced4da;
    border-left: 3px solid rgba(139, 26, 26, 0.4);
    border-radius: 6px;
    padding: 8px 30px 8px 10px;
    margin-bottom: 6px;
    background: #fafafa;
}

.cond-group-title {
    font-size: 0.7rem;
    font-weight: 600;
    color: #6c757d;
    margin-bottom: 6px;
}

.btn-add-cond-group {
    margin-left: 6px;
}

/* The composer rows sit outside any .form-group, and tilia's input chrome is scoped
   to `.form-group input` — so these fell through to the browser default (1px square
   border, no focus ring). Restate the standard border/background/focus here, at the
   row's smaller scale. */
.section-item .section-label-input {
    font-size: 0.8rem;
    padding: 3px 6px;
    width: 140px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    background: #fff;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}

.section-item .section-label-input:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.builder-preview {
    margin-top: 20px;
    border-radius: 10px;
    border: 2px solid #dee2e6;
}

.builder-preview-header {
    background: #e9ecef;
    padding: 8px 16px;
    font-size: 0.75rem;
    font-weight: 700;
    color: #6c757d;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border-bottom: 2px solid #dee2e6;
    border-radius: 8px 8px 0 0;
}

.builder-preview-icon {
    margin-right: 4px;
}

.builder-preview-body {
    background: #fff;
    padding: 20px;
    min-height: 60px;
}

.builder-preview-body .form-group {
    border-left-color: #8b1a1a;
}

.builder-preview-empty {
    color: #adb5bd;
    text-align: center;
    font-style: italic;
}

.field-editor-actions {
    display: flex;
    gap: 10px;
    margin-top: 20px;
}

.field-section-hidden {
    display: none;
}

/* Toggle Switch */
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
    cursor: pointer;
}

.toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
    position: absolute;
}

.toggle-slider {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: #adb5bd;
    border-radius: 24px;
    transition: background 0.2s ease;
}

.toggle-slider::before {
    content: '';
    position: absolute;
    width: 18px;
    height: 18px;
    left: 3px;
    bottom: 3px;
    background: #fff;
    border-radius: 50%;
    transition: transform 0.2s ease;
}

.toggle-switch input:checked + .toggle-slider {
    background: #8b1a1a;
}

.toggle-switch input:checked + .toggle-slider::before {
    transform: translateX(20px);
}

.toggle-switch input:focus + .toggle-slider {
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.builder-sortable-list {
    min-height: 40px;
    border: 2px solid rgba(139, 26, 26, 0.15);
    border-radius: 10px;
    padding: 8px;
}

.builder-sortable-item {
    cursor: grab;
    border-left: 3px solid #dee2e6;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}

.builder-sortable-item:hover {
    border-left-color: #8b1a1a;
    box-shadow: 0 2px 8px rgba(139, 26, 26, 0.1);
}

.builder-sortable-item:active {
    cursor: grabbing;
}

/* A frozen form version shown in the editor: rows expand but do not drag, and the
   toggles show state without inviting a click. */
.form-editor-readonly .builder-sortable-item,
.form-editor-readonly .builder-sortable-item:active {
    cursor: pointer;
}

.form-editor-readonly .section-item,
.form-editor-readonly .note-item,
.form-editor-readonly .toggle-slider {
    cursor: default;
}

.form-editor-readonly .toggle-slider {
    opacity: 0.6;
}

.form-version-info {
    margin: -8px 0 16px;
    font-size: 0.8rem;
}

/* Freeze-new-version dialog: from → to, then the change table. */
.freeze-head {
    margin-bottom: 10px;
}

.freeze-changes td:first-child {
    white-space: nowrap;
    font-weight: 600;
}

.freeze-changes .freeze-added td:first-child {
    color: #1e7e34;
}

.freeze-changes .freeze-removed td:first-child {
    color: #c82333;
}

.freeze-changes .freeze-changed td:first-child {
    color: #1a4e8b;
}

.form-version-warning {
    margin-top: -10px;
    color: #8b1a1a;
}

/* Form-composer field rows and wizard steps drag by the left handle only, so the card
   body itself isn't grabbable (rows with tinted action bars). */
.user-row.row-actions-bar.builder-sortable-item,
.user-row.row-actions-bar.builder-sortable-item:active {
    cursor: default;
}

.builder-sortable-item.sortable-ghost {
    opacity: 0.3;
}

.sortable-fallback {
    background: #fff !important;
    border: 2px solid #8b1a1a !important;
    border-radius: 10px !important;
    box-shadow: 0 8px 24px rgba(139, 26, 26, 0.25) !important;
    opacity: 0.95 !important;
}

.builder-drag-indicator {
    color: #adb5bd;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    transition: color 0.2s;
}

.builder-sortable-item:hover .builder-drag-indicator {
    color: #8b1a1a;
}

/* On rows with tinted action bars, the drag handle is itself a full-height grey bar
   (mirroring the right-edge action buttons) — the whole bar is the grab target. */
.user-row.row-actions-bar > .builder-drag-indicator {
    align-self: stretch;
    justify-content: center;
    width: 40px;
    margin: -15px 0 -15px -20px;
    background: #f1f3f5;
    color: #adb5bd;
    cursor: grab;
    transition:
        background 0.15s ease,
        color 0.15s ease;
}
.user-row.row-actions-bar:hover > .builder-drag-indicator {
    color: #adb5bd;
}
.user-row.row-actions-bar > .builder-drag-indicator:hover {
    background: #e6eaee;
    color: #495057;
}
.user-row.row-actions-bar > .builder-drag-indicator:active {
    background: #d7dde2;
    cursor: grabbing;
}

/* Builder cards that carry an expandable settings panel are clickable across their
   whole surface (the chevron button does the same thing). Opening the linked
   form/field is a dedicated action button instead — see .btn-icon-open-record. */
.sortable-field-item:not(.section-item) {
    cursor: pointer;
}

/* Courier site assignment (project wizard, people & places): one card per courier,
   carrying the site picker for the sites they serve on this study. */
.member-sites-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 8px;
}
.member-site-row {
    align-items: center;
    gap: 16px;
}
.member-site-row .user-row-info {
    flex: 0 0 30%;
    min-width: 160px;
}
.member-site-row .member-site-picker {
    flex: 1;
}

/* Tag pills (multiselect, ontology) */
.tag-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.tag-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    background: rgba(139, 26, 26, 0.1);
    color: #8b1a1a;
    padding: 0.2rem 0.5rem;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: 500;
    white-space: nowrap;
}

/* A pill that opens its linked record — the pill body is the link (the × keeps its
   own cursor, so removal still reads as removal). */
.tag-pill-linked {
    cursor: pointer;
    transition:
        background 0.15s ease,
        box-shadow 0.15s ease;
}

.tag-pill-linked:hover {
    background: rgba(139, 26, 26, 0.22);
    box-shadow: 0 0 0 2px rgba(139, 26, 26, 0.2);
}

.tag-remove {
    background: none;
    border: none;
    color: #8b1a1a;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0 0.15rem;
    opacity: 0.6;
}

.tag-remove:hover {
    opacity: 1;
}

.tag-picker .tag-picker-select {
    border: none;
    background: transparent;
    font-size: 0.8rem;
    color: #6c757d;
    cursor: pointer;
    padding: 0.15rem;
    outline: none;
    min-width: 80px;
}

/* The add-select is inline inside the picker box — no border/ring of its own;
   the focus ring is shown on the .tag-picker wrapper instead. */
.tag-picker .tag-picker-select:focus {
    border: none;
    box-shadow: none;
}

/* When a search picker is inside a form-group, remove the form-group chrome */
/* search-picker form-groups inherit normal .form-group styling */

/* Search picker (async ontology search + tag pills) */
.search-picker {
    border: 2px solid #adb5bd;
    border-radius: 6px;
    padding: 0.35rem 0.5rem;
    background: #fff;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    min-height: 38px;
    position: relative;
    transition: border-color 0.2s;
}

.search-picker:focus-within {
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.15);
}

.search-picker .tag-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    width: 100%;
}

.search-picker-input {
    border: none !important;
    background: transparent !important;
    outline: none !important;
    font-size: 0.85rem;
    padding: 0.2rem 0 !important;
    min-width: 200px;
    flex: 1;
    box-shadow: none !important;
}

/* Option lists are shown in the top layer (commons/popup.js). The UA stylesheet styles
   `[popover]` as a centred box with its own border and padding, so the few properties
   our own rules do not set are neutralised here — position, inset and margin are set
   inline by the helper, which is where the anchor's rect is known. */
.search-picker-dropdown[popover],
.ss-dropdown[popover] {
    padding: 0;
    width: auto;
    height: auto;
    max-width: none;
    color: inherit;
}
.search-picker-dropdown[popover]:not(:popover-open),
.ss-dropdown[popover]:not(:popover-open) {
    display: none;
}

.search-picker-dropdown {
    display: none;
    background: #fff;
    border: 2px solid #8b1a1a;
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 240px;
    overflow-y: auto;
    /* As wide as its field, however long an entry is (QA4 A21): an entry wraps, the list
       does not grow past the card it belongs to. */
    overflow-x: hidden;
    z-index: 200;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

.search-picker-item {
    padding: 0.45rem 0.7rem;
    font-size: 0.85rem;
    cursor: pointer;
    overflow-wrap: anywhere;
}

.search-picker-item:hover:not(.disabled) {
    background: rgba(139, 26, 26, 0.08);
}

.search-picker-item.disabled {
    color: #6c757d;
    cursor: default;
    font-style: italic;
}

/* Searchable Select */
.ss-wrapper {
    position: relative;
    flex: 1;
}

.ss-input {
    width: 100%;
    padding: 8px 12px;
    border: 2px solid #dee2e6;
    border-radius: 8px;
    font-size: 0.9rem;
    background: #fff;
    transition: border-color 0.2s;
}

.ss-input:focus {
    border-color: #8b1a1a;
    outline: none;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.15);
}

.ss-dropdown {
    display: none;
    max-height: 240px;
    overflow-y: auto;
    background: #fff;
    border: 2px solid #8b1a1a;
    border-top: none;
    border-radius: 0 0 8px 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
    z-index: 100;
}

.ss-dropdown.ss-open {
    display: block;
}

.ss-option {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 12px;
    cursor: pointer;
    transition: background 0.1s;
}

.ss-option:hover,
.ss-option.ss-highlighted {
    background: rgba(139, 26, 26, 0.08);
}

.ss-option-label {
    font-weight: 500;
    color: #333;
}

.ss-option-key {
    font-size: 0.75rem;
    color: #6c757d;
    font-family: monospace;
}

.ss-empty {
    padding: 12px;
    text-align: center;
    color: #6c757d;
    font-style: italic;
}

/* Searchable multi-select (local data, not API-driven) */
.ss-multi-picker {
    border: 2px solid #adb5bd;
    border-radius: 6px;
    padding: 0.35rem 0.5rem;
    background: #fff;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    min-height: 38px;
    position: relative;
    transition: border-color 0.2s;
}

.ss-multi-picker:focus-within {
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.15);
}

.ss-multi-picker .tag-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    width: 100%;
}

.ss-multi-picker .search-picker-input {
    border: none !important;
    background: transparent !important;
    outline: none !important;
    font-size: 0.85rem;
    padding: 0.2rem 0 !important;
    min-width: 200px;
    flex: 1;
    box-shadow: none !important;
}

.ss-multi-picker .search-picker-dropdown.ss-open {
    display: block;
}

.ss-multi-picker .search-picker-dropdown {
    display: none;
    background: #fff;
    border: 2px solid #8b1a1a;
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 240px;
    overflow-y: auto;
    z-index: 200;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

.form-group:has(.ss-multi-picker) {
    background: transparent;
    border-left-color: transparent;
}

.builder-sortable-header {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-bottom: 12px;
}

.builder-sortable-header select {
    flex: 1;
    min-width: 200px;
    padding: 8px 12px;
    border: 2px solid #dee2e6;
    border-radius: 8px;
    font-size: 0.9rem;
    background: #fff;
    transition: border-color 0.2s;
}

.builder-sortable-header select:focus {
    border-color: #8b1a1a;
    outline: none;
}

/* Project Wizard — step indicator */
/* Project wizard progress bar — uses shared .progress-bar / .step classes */
.field-editor-panel > .progress-bar {
    margin-bottom: 20px;
}

/* Project Wizard only: the clinician/site tag pickers sit in a full-width
   form-group, so drop the panel chrome and let the picker read as a standalone
   control. Dynamic-form multiselect fields keep the normal field chrome so they
   match every other field. */
.form-group:has(> .clinician-multi-select-container),
.form-group:has(> .site-multi-select-container) {
    background: transparent;
    border-left-color: transparent;
}

/* Project Wizard — tag picker wrapper & add select */
.tag-picker {
    border: 2px solid #adb5bd;
    border-radius: 6px;
    padding: 8px;
    background: #fff;
    min-height: 38px;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}

.tag-picker:focus-within {
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.15);
}

.tag-picker-add {
    margin-top: 6px;
    width: 100%;
    padding: 6px 10px;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    font-size: 0.85rem;
    color: #6c757d;
}

/* Project Wizard — phase cards */
.project-phase-card {
    border: 2px solid #e9ecef;
    border-radius: 12px;
    margin-bottom: 12px;
    overflow: hidden;
    transition: border-color 0.2s ease;
}

.project-phase-card:hover {
    border-color: #8b1a1a;
}

.project-phase-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    background: #fff;
    border-bottom: 1px solid #e9ecef;
}

.phase-number-badge {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: #8b1a1a;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    font-size: 0.85rem;
    flex-shrink: 0;
}

.phase-description-input {
    flex: 1;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 6px 10px;
    font-size: 0.95rem;
    background: transparent;
    transition: border-color 0.2s;
}

.phase-description-input:hover {
    border-bottom-color: #dee2e6;
}

.phase-description-input:focus {
    border-bottom-color: #8b1a1a;
    outline: none;
}

.project-phase-header-actions {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
}

.project-phase-body {
    padding: 16px;
    background: #f8f9fa;
}

/* Project Wizard — biospecimen definition cards */
.biospecimen-def-card {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 15px 20px;
    background: #fff;
    border: 2px solid #e9ecef;
    border-radius: 10px;
    margin-bottom: 10px;
    transition: all 0.2s ease;
}

.biospecimen-def-card:hover {
    border-color: #8b1a1a;
    box-shadow: 0 2px 8px rgba(139, 26, 26, 0.15);
}

.bio-card-grid {
    flex: 1;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
}

.bio-card-grid .form-group {
    min-width: 0;
    padding: 8px 10px;
}

.bio-card-grid .form-group label {
    font-size: 0.75rem;
    margin-bottom: 4px;
}

.bio-card-grid .form-group select,
.bio-card-grid .form-group input {
    padding: 6px 8px;
    font-size: 0.85rem;
}

.bio-card-actions {
    display: flex;
    align-items: center;
    padding-top: 20px;
    flex-shrink: 0;
}

.project-add-phase-row {
    margin-top: 12px;
}

@media (max-width: 768px) {
    .bio-card-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 480px) {
    .bio-card-grid {
        grid-template-columns: 1fr;
    }
    .biospecimen-def-card {
        flex-direction: column;
    }
    .bio-card-actions {
        padding-top: 0;
        align-self: flex-end;
    }
}

@media (max-width: 600px) {
    .field-row .field-row-badges {
        width: 100%;
    }

    .field-editor-actions {
        flex-wrap: wrap;
    }

    .builder-sortable-header {
        flex-wrap: wrap;
    }
}

/* Search with filter dropdown (e.g. phase-manager) */
.search-with-filter {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.search-with-filter input {
    flex: 1;
    min-width: 200px;
}

.search-with-filter select {
    min-width: 160px;
}

/* Section title inside builder forms */
.bio-section-title {
    margin: 15px 0 10px;
    color: #333;
    font-size: 1rem;
}

/* Password label hint (inline qualifier) */
.label-hint {
    font-weight: 400;
    font-size: 0.75rem;
    text-transform: none;
}

/* Empty state centered text */
.empty-state-text {
    text-align: center;
    color: #6c757d;
    padding: 40px;
}

/* Admin card icon gradients */
.admin-card-gdpr {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.admin-card-audits {
    background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.admin-card-users {
    background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.admin-card-fields {
    background: linear-gradient(135deg, #2196f3 0%, #64b5f6 100%);
}
.admin-card-forms {
    background: linear-gradient(135deg, #9c27b0 0%, #ce93d8 100%);
}
.admin-card-wizards {
    background: linear-gradient(135deg, #ff9800 0%, #ffb74d 100%);
}
.admin-card-bindings {
    background: linear-gradient(135deg, #009688 0%, #4db6ac 100%);
}
.admin-card-projects {
    background: linear-gradient(135deg, #e91e63 0%, #f48fb1 100%);
}
.admin-card-phases {
    background: linear-gradient(135deg, #795548 0%, #a1887f 100%);
}
.admin-card-sites {
    background: linear-gradient(135deg, #607d8b 0%, #90a4ae 100%);
}
.admin-card-organizations {
    background: linear-gradient(135deg, #5c6bc0 0%, #7986cb 100%);
}

/* File input styling */
.form-group input[type='file'] {
    padding: 8px;
    border: 2px dashed #adb5bd;
    border-radius: 6px;
    background: #f8f9fa;
    cursor: pointer;
    font-size: 0.85rem;
}

.form-group input[type='file']:hover {
    border-color: #8b1a1a;
    background: #fdf0f0;
}

.form-group input[type='file']::file-selector-button {
    background: #8b1a1a;
    color: #fff;
    border: none;
    padding: 6px 14px;
    border-radius: 4px;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    margin-right: 10px;
}

.form-group input[type='file']::file-selector-button:hover {
    background: #6e1515;
}

.file-upload-status {
    display: block;
    margin-top: 4px;
    color: #6c757d;
    font-size: 0.8rem;
}

.file-upload-status:empty {
    display: none;
}

.file-upload-status.file-upload-progress {
    color: #6c757d;
    font-style: italic;
}

.file-upload-status.file-upload-error {
    color: #dc3545;
}

.file-download-link {
    color: #0d6efd;
    text-decoration: none;
    word-break: break-all;
}

.file-download-link:hover {
    text-decoration: underline;
}

.file-remove-btn {
    margin-left: 6px;
    border: none;
    background: transparent;
    color: #dc3545;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0 4px;
}

.file-remove-btn:hover {
    color: #a71d2a;
}

/* Password strength rules checklist */
.pw-rules {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin: 6px 0 8px;
    font-size: 12px;
}

.pw-rule {
    color: #999;
    transition: color 0.15s;
}

.pw-rule.pass {
    color: #2e7d32;
}

.pw-rule .pw-rule-icon {
    display: inline-block;
    width: 14px;
    text-align: center;
    font-size: 11px;
}

/* Dynamic wizard */
.dynamic-wizard-body {
    padding-top: 1rem;
}
.dynamic-wizard-loading,
.dynamic-wizard-error {
    text-align: center;
    padding: 3rem 1rem;
    color: #666;
}

/* Code System Manager */
.role-badge.type-active {
    background: #d4edda;
    color: #155724;
}

.role-badge.type-inactive {
    background: #f8d7da;
    color: #721c24;
}

/* A partial QC fail — some outputs failed while the run itself stands. Deliberately
   between the pass/fail pair, because it is neither. */
.role-badge.type-partial {
    background: #fff3cd;
    color: #7a5200;
}

/* "This is the record you came from" — a navigational marker, not a state, so it takes
   the app's link blue rather than either QC colour. */
.role-badge.type-origin {
    background: rgba(26, 78, 139, 0.12);
    color: #1a4e8b;
}

/* Single-select ontology picker */
.search-single-picker {
    position: relative;
}

.search-single-picker .search-single-input {
    width: 100%;
    padding: 10px 32px 10px 12px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    font-size: 0.95rem;
    background: #fff;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}

.search-single-picker .search-single-input:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}

.search-single-picker .search-single-clear {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    font-size: 1.2rem;
    color: #adb5bd;
    cursor: pointer;
    padding: 2px 6px;
    line-height: 1;
}

.search-single-picker .search-single-clear:hover {
    color: #8b1a1a;
}

.search-single-picker .search-picker-dropdown {
    display: none;
    background: #fff;
    border: 2px solid #8b1a1a;
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 240px;
    overflow-y: auto;
    z-index: 100;
}

.cs-history-toggle {
    background: none;
    border: none;
    color: #8b1a1a;
    cursor: pointer;
    font-size: 0.8rem;
    padding: 0;
    margin-left: 0.5rem;
    text-decoration: underline;
}

.cs-history-row {
    padding-left: 2rem;
    opacity: 0.7;
}

/* ---------- Transport scan view (Phase 4-7) ---------- */

.scan-header {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}

.scan-header .scan-back {
    justify-self: start;
}

.scan-header .scan-state {
    justify-self: center;
    display: flex;
    justify-content: center;
}

.scan-header .scan-cancel {
    justify-self: end;
}

.scan-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 10px;
    margin-bottom: 16px;
    font-size: 0.9rem;
}

.scan-meta-chip {
    background: #f5f5f5;
    border-radius: 14px;
    padding: 4px 12px;
    line-height: 1.4;
}

.scan-meta-chip strong {
    margin-right: 6px;
    color: #555;
    font-weight: 600;
}

.capacity-grid {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.capacity-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 14px;
    border-radius: 8px;
    background: #f8f8f8;
}

.capacity-temp {
    font-weight: 600;
    font-size: 0.95rem;
}

.scan-errors {
    margin: 0;
    padding-left: 1.2rem;
    color: #8b1a1a;
}

.scan-ready {
    margin: 0;
    padding: 8px 0;
    color: #155724;
    font-weight: 600;
}

.pending-specimens {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.pending-specimen-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 6px;
    background: #f9f9f9;
    border: 1px solid #e9ecef;
}

.pending-specimen-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}

.pending-specimen-type {
    font-size: 0.9rem;
    font-weight: 500;
}

.pending-specimen-sid code {
    background: #fff;
    padding: 2px 6px;
    border-radius: 4px;
    font-family: ui-monospace, monospace;
    font-size: 0.8rem;
    color: #555;
}

/* Box name + its SID/temp/capacity badges on a single header row. */
.pickup-box-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}

/* Specimens packed into a box, shown as cards under the box name. */
.pickup-box-specimens {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 8px;
}

/* Route chip: "From → To" with a graphical arrow. */
.scan-route-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: #f5f5f5;
    border-radius: 14px;
    padding: 6px 14px;
    line-height: 1.4;
    font-size: 0.9rem;
    flex-wrap: wrap;
}

.scan-route-chip .scan-route-site {
    font-weight: 500;
}

.scan-route-chip .scan-arrow {
    color: #8b1a1a;
    flex-shrink: 0;
}

/* Per-origin-site pickup progress (multi-site transport). */
.site-progress-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.site-progress-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-radius: 8px;
    background: #f5f5f5;
    font-size: 0.9rem;
}

.site-progress-row .site-progress-badge {
    flex-shrink: 0;
    width: 20px;
    text-align: center;
    font-weight: 700;
}

.site-progress-row.site-progress-done .site-progress-badge {
    color: #1e7e34;
}

.site-progress-row.site-progress-todo .site-progress-badge {
    color: #8b6d1a;
}

.site-progress-row .site-progress-name {
    font-weight: 500;
}

.site-progress-row .site-progress-counts {
    margin-left: auto;
    color: #666;
    font-size: 0.85rem;
}

@media (max-width: 600px) {
    .scan-route-chip {
        font-size: 0.85rem;
        padding: 6px 10px;
    }
    .pending-specimen-row {
        padding: 8px 10px;
    }
}

@media (max-width: 600px) {
    .scan-header {
        display: flex;
        flex-wrap: wrap;
    }
    .scan-header .scan-state {
        order: -1;
        flex: 1 1 100%;
    }
    .scan-header .scan-back,
    .scan-header .scan-cancel {
        flex: 1 1 0;
    }
    .scan-meta-chip {
        font-size: 0.85rem;
    }
}

/* -------------------------------------------------------------------------
   Global biospecimen search (MIABIS) — roadmap Phase 17
   ------------------------------------------------------------------------- */
.bsearch-screen .bsearch-title {
    margin: 0;
    font-size: 1.1rem;
}
.bsearch-filters {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 0.75rem 1rem;
    margin: 1rem 0;
}
/* Match the standard .form-group card look used across the app. */
.bsearch-field {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    background: #f8f9fa;
    border-radius: 8px;
    border-left: 3px solid #dee2e6;
}
.bsearch-field > span {
    color: #495057;
    font-weight: 600;
    font-size: 0.85rem;
}
.bsearch-field input,
.bsearch-field select {
    width: 100%;
    padding: 10px 12px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    font-size: 0.95rem;
    font-weight: 400;
    background: #fff;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}
.bsearch-field input:focus,
.bsearch-field select:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}
.bsearch-actions {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.bsearch-counts {
    font-weight: 700;
    padding: 0.5rem 0;
    border-top: 1px solid var(--border-color, #e0e0e0);
    border-bottom: 1px solid var(--border-color, #e0e0e0);
    margin-bottom: 0.5rem;
}
.bsearch-list .bsearch-row {
    display: grid;
    grid-template-columns: 1.2fr 1.5fr 1.2fr 1fr 0.8fr 1fr 1fr;
    gap: 0.5rem;
    align-items: center;
    font-size: 0.85rem;
}
.bsearch-list .bsearch-row:hover {
    background: #f8f9fa;
}
.bsearch-list .bsearch-barcode {
    font-family: monospace;
    font-weight: 700;
}

/* MIABIS search filter fieldset — matches .builder-fieldset used elsewhere. */
.bsearch-fieldset {
    border: 2px solid rgba(139, 26, 26, 0.25);
    border-radius: 10px;
    padding: 20px;
    margin: 0.5rem 0 1rem;
}
.bsearch-fieldset > legend {
    font-size: 0.85rem;
    font-weight: 600;
    color: #8b1a1a;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    padding: 0 8px;
}

/* Global delayed loader overlay (loader.js) — shown only for slow operations. */
/* Global loader overlay styles now ship with bb-base's shared js/loader.js
   (injected on first use). Theme via --loader-accent / --loader-ring. */

/* --- Fieldset section intro + Directory publish confirmation --- */
.builder-section-hint {
    margin: 0 0 15px;
    color: #6c757d;
    font-size: 0.85rem;
}
/* The summary is a label/value list of everything that leaves for the Directory —
   descriptions and diagnosis terms wrap to three lines in the default 420px modal.
   Same treatment as the coverage dialog: widen to fit rather than squeeze. */
.dialog-box:has(.directory-confirm-list) {
    max-width: 760px;
}
.directory-confirm-intro {
    margin: 0 0 12px;
}
.directory-confirm-list {
    list-style: none;
    margin: 0;
    padding: 0;
    border: 1px solid #e9ecef;
    border-radius: 6px;
    overflow: hidden;
}
.directory-confirm-list li {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 8px 14px;
    border-bottom: 1px solid #e9ecef;
}
.directory-confirm-list li:last-child {
    border-bottom: 0;
}
.directory-confirm-list li:nth-child(odd) {
    background: #f8f9fa;
}
.directory-confirm-list span {
    color: #6c757d;
}
.directory-confirm-list b {
    color: #212529;
    text-align: right;
}

/* --- Toast notifications (bottom-right info bubbles; see js/wizard/toast.js) --- */
.toast-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 11000;
    display: flex;
    flex-direction: column;
    gap: 15px;
    width: 510px;
    max-width: calc(100vw - 40px);
    pointer-events: none;
}
.toast {
    display: flex;
    align-items: flex-start;
    gap: 15px;
    background: #fff;
    border: 1px solid var(--border-color, #e0e0e0);
    border-left: 6px solid #607d8b;
    border-radius: 9px;
    box-shadow: 0 6px 24px rgba(38, 50, 56, 0.18);
    padding: 18px 21px;
    color: #263238;
    font-size: 1.2rem;
    line-height: 1.4;
    opacity: 0;
    transform: translateX(24px);
    transition:
        opacity 0.25s ease,
        transform 0.25s ease;
    pointer-events: auto;
}
.toast-show {
    opacity: 1;
    transform: translateX(0);
}
.toast-body {
    flex: 1;
    word-break: break-word;
}
.toast-close {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1.27rem;
    line-height: 1;
    color: #b0bec5;
    padding: 2px 5px;
    margin: -3px -6px 0 0;
    border-radius: 6px;
}
.toast-close:hover {
    color: #37474f;
    background: rgba(0, 0, 0, 0.05);
}
/* Per-kind tinted surfaces: the tint separates the bubble from the white
   wizard card underneath and carries the severity alongside the left accent.
   Three steps per kind: lightest fill, mid-dark border, full-strength left accent.
   `border-color` is a four-side shorthand, so `border-left-color` MUST stay last
   or it wipes out the accent bar. */
.toast-success {
    background: #dcefe0;
    border-color: #7fae89;
    border-left-color: #2e7d32;
}
.toast-error {
    background: #f9dcdc;
    border-color: #d59595;
    border-left-color: #c62828;
}
.toast-warning {
    background: #faedc6;
    border-color: #cfae5c;
    border-left-color: #f9a825;
}
.toast-info {
    background: #dfe7eb;
    border-color: #93a8b2;
    border-left-color: #607d8b;
}
@media (prefers-reduced-motion: reduce) {
    .toast {
        transition: opacity 0.15s ease;
        transform: none;
    }
}

/* Computed SPREC code block (per-specimen dialog) */
.sprec-code {
    font-family: var(--mono-font, monospace);
    font-size: 1.05rem;
    letter-spacing: 0.02em;
    background: var(--surface-2, #f4f4f4);
    border: 1px solid var(--border-color, #ddd);
    border-radius: 4px;
    padding: 10px 12px;
    margin: 6px 0 0;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-all;
}

/* MIABIS/SPREC coverage dialog table (redesign M6) */
/* The coverage dialog widens to fit its (non-wrapping) table rather than
   squeezing values onto multiple lines — readability over a narrow modal. */
.dialog-box:has(.coverage-report) {
    max-width: 92vw;
    width: max-content;
}
.coverage-report {
    max-height: 50vh;
    overflow: auto;
    /* Inset shadow at the top/bottom edges signals there's more to scroll. */
    box-shadow:
        inset 0 -14px 10px -12px rgba(0, 0, 0, 0.35),
        inset 0 14px 10px -12px rgba(0, 0, 0, 0.2);
    scrollbar-width: thin;
    scrollbar-gutter: stable;
}
.coverage-report::-webkit-scrollbar {
    width: 10px;
}
.coverage-report::-webkit-scrollbar-thumb {
    background: var(--border-color, #bbb);
    border-radius: 5px;
}
.coverage-report .coverage-table {
    width: 100%;
    border-collapse: collapse;
    margin: 8px 0 14px;
    font-size: 0.85rem;
}
.coverage-report .coverage-table th,
.coverage-report .coverage-table td {
    border: 1px solid var(--border-color, #ddd);
    padding: 4px 8px;
    text-align: left;
    white-space: nowrap; /* don't wrap — the dialog widens to fit instead */
}
.coverage-report .coverage-table th:last-child,
.coverage-report .coverage-table td:last-child {
    width: 2.5rem;
    text-align: center;
}
.coverage-report .coverage-table th {
    background: var(--surface-2, #f4f4f4);
    font-weight: 600;
    position: sticky;
    top: 0;
}
/* Red = a REQUIRED (mandatory) attribute is missing; orange = optional/recommended. */
.coverage-report tr.coverage-missing-req td {
    background: rgba(198, 40, 40, 0.12);
}
.coverage-report tr.coverage-missing-opt td {
    background: rgba(230, 145, 20, 0.14);
}
.coverage-report .cov-ok {
    color: #2e7d32;
    font-weight: 700;
}
.coverage-report .cov-no {
    color: #c62828;
    font-weight: 700;
}
/* Dotted paths (attribute + tilia source) render uniformly in a mono font. */
.coverage-report .cov-path {
    font-family: var(--mono-font, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
    font-size: 0.9em;
}
.coverage-report .cov-calc {
    color: var(--text-muted, #8a8a8a);
    font-style: italic;
}
.coverage-report .cov-req {
    text-align: center;
}
.coverage-report .cov-star {
    color: #c62828;
}

/* ---------- Device logbook (Phase 21) ---------- */

/* Pushes the "add" button to the right of the toolbar, matching every manager. */
.dl-toolbar-spacer {
    flex: 1;
}

/* The filter panel must not sit flush against the first entry row. */
.dl-filters,
.list-filters {
    margin-bottom: 1.5rem;
}

/* An admin list's filters (Phase 27 E2) — the device log's panel, opened from the toolbar. */
.list-filter-fields .form-group {
    flex: 0 1 200px;
}

/* Until the list's first page brings the fields: as tall as a field group, not a paragraph taller. */
.list-filter-pending .field-hint {
    margin: 0;
}

.list-filter-range {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.list-filter-range input {
    min-width: 0;
}

/* The Filter button says when filters are in force, as the search box does (`filter-active`). */
.admin-list-toolbar .btn-list-filter.filter-active {
    background: rgba(139, 26, 26, 0.08);
    color: #8b1a1a;
}

.manager-filter-chips:empty {
    display: none;
}

.list-filter-count {
    align-self: center;
    color: #6c757d;
    font-size: 0.82rem;
}

.dl-filter-actions {
    display: flex;
    align-items: flex-end;
    gap: 0.4rem;
}

/* A correction and the entry it supersedes read as one record: the pair is boxed,
   and the correction is indented under its original with a connecting rule. */
.dl-group {
    border-left: 3px solid var(--primary, #8b1a1a);
    border-radius: 4px;
    margin-bottom: 0.75rem;
    background: rgba(139, 26, 26, 0.03);
}

.dl-correction {
    margin-left: 2rem;
    position: relative;
    opacity: 0.95;
}

.dl-correction::before {
    content: '';
    position: absolute;
    left: -1rem;
    top: 50%;
    width: 0.75rem;
    border-top: 1px dashed var(--primary, #8b1a1a);
}

.dl-correction-tag {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--primary, #8b1a1a);
    margin-right: 0.35rem;
}

/* Device name shown as the h2 under the "Device logbook" header. */
.dl-device-name {
    margin: 0 0 0.75rem;
    font-size: 1.15rem;
}

/* Detail dialog: field table + correction diff. */
/* A logbook entry is a two-column table of labels and values, some of them long: at the
   confirm dialog's default 420px nearly every row wrapped. */
.dialog-box:has(.dl-detail-table) {
    max-width: 760px;
}
.dl-detail-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}
.dl-detail-table th,
.dl-detail-table td {
    border: 1px solid var(--border-color, #ddd);
    padding: 4px 8px;
    text-align: left;
    vertical-align: top;
    /* Multi-line values (e.g. the humanized "configured values" list) keep their line
       breaks; single-line values are unaffected. */
    white-space: pre-line;
}

/* Editor: the specimen-barcode add-box + removable cards (mirrors the transport
   editor's sample manifest). */
.dl-specimens .dl-specimen-cards {
    margin-top: 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.dl-specimen-card {
    margin: 0;
}
.dl-detail-table th {
    width: 32%;
    background: var(--surface-2, #f4f4f4);
    font-weight: 600;
}
.dl-diff {
    margin: 4px 0 0;
    padding-left: 1.1rem;
}
.dl-diff-old {
    color: var(--text-muted, #8a8a8a);
    text-decoration: line-through;
}

/* SOP Run detail / history, the provenance timeline and the clinical-data view — read-only
   records inside the shared .biospecimen-modal shell.

   820px was set for the SOP viewer, whose content is a stack of labelled fields. The timeline
   is a three-column grid whose third column holds a field diff, and at that width the diff had
   about a third of the dialog to itself while the page beside it was empty. These are reference
   views a reader scans rather than forms they fill in, so they get the room. */
.run-detail-modal .biospecimen-modal-content {
    max-width: 1100px;
}

/* One rhythm for every block in the viewer. The blocks are a mix of kinds — the origin
   banner, two fieldsets, one host div per SOP step, the fallback aliquot section — and
   each carried its own spacing (or, for a step host, none at all), so the gaps stepped
   between 0 and 20px down the modal. Spacing between siblings belongs to the parent. */
.run-detail-modal .biospecimen-modal-body > * + * {
    margin-top: 20px;
}

/* Structured header facts (SOP / Run by / When / QC). */
.run-facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 8px 18px;
    align-items: center;
    padding-bottom: 14px;
    margin-bottom: 16px;
    border-bottom: 1px solid #e9ecef;
}
.run-facts dt {
    color: #6c757d;
    font-size: 0.85rem;
}
.run-facts dd {
    margin: 0;
    color: #333;
    font-weight: 500;
}
/* The rule under the facts separates them from the QC reasons; with nothing to separate
   it is just a line hanging inside the form group. */
.run-facts:last-child {
    padding-bottom: 0;
    margin-bottom: 0;
    border-bottom: none;
}
.run-deviation {
    background: #fdf0f0;
    border: 1px solid #f3d6d6;
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 0.9rem;
    color: #6e1515;
    margin-bottom: 16px;
    /* The operator's reason keeps its line breaks — the textarea allows them, the DB
       stores them, and collapsing them turned a structured account into one long line. */
    white-space: pre-line;
}
.run-deviation:last-child {
    margin-bottom: 0;
}

/* A checklist field the run recorded against the SOP's prescription. Amber, not red — a
   deviation is a warning the run's reason explains, not a failure. The whole group is
   boxed: the viewer's controls are disabled, and a tint on the control alone lost to the
   disabled grey and read as nothing. */
.run-group-deviated {
    border: 1px solid #d39e00;
    background: #fffbf0;
    border-radius: 8px;
    padding: 8px 10px;
}

.run-group-deviated input:disabled,
.run-group-deviated select:disabled,
.run-group-deviated input,
.run-group-deviated select,
.run-group-deviated textarea {
    border-color: #d39e00;
    background: #fff;
}

.run-field-deviated-note {
    margin: 4px 0 0;
    font-size: 0.85rem;
    color: #7a5200;
    font-weight: 600;
}

/* The storage-placement group's read side: a stack of read-only user-row cards. */
.run-placements {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
/* The same notice restated at the aliquot list. Badge and text on one line — it sits
   above the cards, so it must read as a label on them rather than as another card. */
.run-aliquots-deviation {
    display: flex;
    align-items: center;
    gap: 10px;
}
.run-aliquots-deviation .run-deviation-text {
    min-width: 0;
    overflow-wrap: anywhere;
}
/* Why the run is flagged, and where. The "where" is a barcode or a field label, so it
   takes the monospace treatment barcodes get everywhere else. */
.run-qc-reasons ul {
    margin: 6px 0 0;
    padding-left: 1.1rem;
}
.run-qc-reasons li + li {
    margin-top: 3px;
}
.run-qc-where {
    font-family: 'SF Mono', 'Cascadia Mono', 'Consolas', monospace;
    font-weight: 600;
}

/* Each step renders the standard read-only form (inert); the form's own section
   fieldsets provide the headers. */
.run-step-host {
    pointer-events: none;
}
.run-step-host .dynamic-aliquot-slot {
    pointer-events: auto;
}
/* A placement row's "show in device" bar is the one live control in a placement slot, tinted
   as the aliquot card's "find this aliquot" bar is. */
.run-placement-row .user-row-actions {
    pointer-events: auto;
}
.user-row.row-actions-bar .run-placement-open {
    background: rgba(26, 78, 139, 0.08);
    color: #1a4e8b;
}
.user-row.row-actions-bar .run-placement-open:hover,
.user-row.row-actions-bar .run-placement-open:active {
    background: rgba(26, 78, 139, 0.22);
    color: #154273;
}

.run-section {
    margin-top: 18px;
}
.run-section-title {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #8b1a1a;
    font-weight: 700;
    margin-bottom: 10px;
}

/* Aliquots produced — inline at the aliquot-group slot (or the fallback section).
   Each is a standard clickable-card row (full-height action bar). Navigation is the
   search icon only; the card surface expands the aliquot's output QC. */
.run-aliquots {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.run-aliquot {
    cursor: default;
}
/* A card that carries QC to reveal is the one worth clicking. */
.run-aliquot-node:has(.run-aliquot-body) .run-aliquot {
    cursor: pointer;
}
/* Red means "this aliquot failed its QC" — a fact about the material. The card you
   arrived from is a fact about navigation, so it takes the accent blue instead; an
   aliquot can be both at once and still read correctly. */
.run-aliquot-fail {
    border-color: #d9534f;
    background: #fdf0f0;
}
/* Off its plan (volume/material) but usable — amber, matching the deviation styling. */
.run-aliquot-deviated {
    border-color: #d39e00;
    background: #fffbf0;
}
.run-aliquot-deviated.run-aliquot-hi {
    border-color: #1a4e8b;
    background: #fffbf0;
    box-shadow: inset 3px 0 0 #d39e00;
}
.run-aliquot-hi {
    border-color: #1a4e8b;
    background: #eef3fa;
}
.run-aliquot-fail.run-aliquot-hi {
    border-color: #1a4e8b;
    background: #fdf0f0;
    box-shadow: inset 3px 0 0 #d9534f;
}
/* Expand/collapse chevron: both glyphs are in the markup, the expanded state picks one. */
.run-aliquot-toggle .chev {
    display: inline-flex;
}
.run-aliquot-toggle .chev-up,
.run-aliquot-expanded .run-aliquot-toggle .chev-down {
    display: none;
}
.run-aliquot-expanded .run-aliquot-toggle .chev-up {
    display: inline-flex;
}
/* The cards sit inside a `.run-step-host`, which is pointer-events:none so the read-only
   checklist is inert. The slot rule re-enables the subtree, but state that explicitly on
   the card too — the fallback section renders the same markup outside any slot. */
.run-aliquot-node {
    display: flex;
    flex-direction: column;
    pointer-events: auto;
}
/* Expanded, the card and its QC are one object: the header keeps its full .user-row
   styling but flattens its bottom, and the body continues the SAME 2px/10px border below
   it — the planned-output tree's `.po-node` / `.po-node-body` pattern. */
.run-aliquot-node > .run-aliquot {
    margin-bottom: 0;
}
.run-aliquot-expanded > .run-aliquot {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
/* Visibility is driven by the expanded class rather than the `hidden` attribute: `hidden`
   is only a UA display:none, which any author `display` here would silently defeat. */
.run-aliquot-body {
    display: none;
    background: #f8f9fa;
    border: 2px solid #e9ecef;
    border-top: none;
    border-radius: 0 0 10px 10px;
    padding: 14px 16px 2px;
}
.run-aliquot-expanded > .run-aliquot-body {
    display: block;
}
/* A failed aliquot's panel continues the card's red edge, so the tint reads as one unit
   rather than a red header with a grey box hanging off it. */
.run-aliquot-expanded > .run-aliquot-fail + .run-aliquot-body {
    border-color: #d9534f;
    background: #fdf5f5;
}
.user-row.clickable-card .run-aliquot-open {
    background: rgba(26, 78, 139, 0.08);
    color: #1a4e8b;
}
.user-row.clickable-card .run-aliquot-open:hover,
.user-row.clickable-card .run-aliquot-open:active {
    background: rgba(26, 78, 139, 0.22);
    color: #154273;
}

/* Cold-chain trace, shown read-only wherever a received specimen is inspected (the
   editor's Receipt QC group and the SOP-run viewer). */
.temp-trace {
    width: 100%;
}
.temp-trace .role-badge {
    display: inline-block;
    margin-bottom: 8px;
}
.temp-trace-source {
    margin-top: 6px;
}
.temp-trace .btn-link {
    background: none;
    border: none;
    padding: 0;
    color: #2f6fdb;
    cursor: pointer;
    font: inherit;
}
.temp-trace .btn-link:hover {
    text-decoration: underline;
}

/* SOP run-history chooser rows. */
.run-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.run-list-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    text-align: left;
    width: 100%;
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    padding: 10px 14px;
    font: inherit;
    cursor: pointer;
    transition:
        background 0.15s,
        border-color 0.15s;
}
.run-list-row:hover {
    background: #fef5f5;
    border-color: #f0d0d0;
}
.run-list-bc {
    font-weight: 600;
    color: #333;
    font-family: 'SF Mono', 'Cascadia Mono', 'Consolas', monospace;
}
.run-list-sub {
    font-size: 0.85rem;
    color: #6c757d;
}
.run-list-main {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Run button on biospecimen rows — a record glyph; as a full-height action bar it needs
   a resting background fill (like family/delete), not just an icon colour. Disabled for
   unprocessed primaries. */
.btn-icon.btn-run:not(.is-disabled) {
    color: #1a4e8b;
}
.user-row.clickable-card .btn-run:not(.is-disabled),
.user-row.row-actions-bar .btn-run:not(.is-disabled) {
    background: rgba(26, 78, 139, 0.08);
}
.user-row.clickable-card .btn-run:not(.is-disabled):hover,
.user-row.clickable-card .btn-run:not(.is-disabled):active,
.user-row.row-actions-bar .btn-run:not(.is-disabled):hover,
.user-row.row-actions-bar .btn-run:not(.is-disabled):active {
    background: rgba(26, 78, 139, 0.22);
    color: #154273;
}
.btn-icon.btn-run.is-disabled {
    color: #9aa0a6;
    opacity: 0.5;
    cursor: not-allowed;
}

/* -------------------------------------------------------------------------
   Info / hint notes in dynamic forms (the `note` field type)
   A presentation-only block: guidance for the person filling the form. It has
   no key and captures nothing, so it is styled as a callout, never as a field.
   ------------------------------------------------------------------------- */
.form-note {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 12px 14px;
    margin: 0 0 12px;
    border-radius: 8px;
    border-left: 4px solid;
    font-size: 0.85rem;
    line-height: 1.5;
}
.form-note-icon {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    /* Optically centre the 18px icon on the first line of text rather than the block. */
    height: 1.5em;
}
.form-note-content {
    min-width: 0; /* let long words wrap instead of stretching the row */
}
.form-note-title {
    font-weight: 600;
    margin-bottom: 3px;
}
.form-note-content p {
    margin: 0 0 0.5em;
}
.form-note-content p:last-child {
    margin-bottom: 0;
}
.form-note-content a {
    color: inherit;
    text-decoration: underline;
}

.form-note--info {
    background: #d1ecf1;
    border-left-color: #17a2b8;
    color: #0c5460;
}
.form-note--warning {
    background: #fff3cd;
    border-left-color: #ffc107;
    color: #856404;
}
.form-note--critical {
    background: #f8d7da;
    border-left-color: #dc3545;
    color: #721c24;
}

/* Builder row for a note. Stays a single-line card like a field row: the header column
   carries the name with the severity select beneath it, and the bilingual inputs sit in a
   2×2 grid (active language | the other) beside it. The row is presentation-only, so it
   carries none of the field row's toggles. */
.note-item {
    border-left: 3px solid #17a2b8;
}
.note-item--warning {
    border-left-color: #ffc107;
}
.note-item--critical {
    border-left-color: #dc3545;
}

/* Cap the header column instead of inheriting `.user-row-info { flex: 1 }` — it was
   claiming half the row and squeezing the inputs the author actually types into. */
.note-item-head {
    flex: 0 0 165px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    align-items: flex-start;
}

.note-item-fields {
    flex: 1;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px 12px;
    min-width: 0;
}
.note-field {
    min-width: 0; /* let the grid cell shrink instead of overflowing the card */
}

/* Match the app's standard control treatment (.form-group input/select/textarea):
   2px border, 6px radius, and the brand focus ring. */
.note-item .note-title-input,
.note-item .note-text-input,
.note-item .note-variant-input {
    width: 100%;
    padding: 5px 8px;
    border: 2px solid #adb5bd;
    border-radius: 6px;
    background: #fff;
    font-family: inherit;
    font-size: 0.82rem;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}
.note-item .note-title-input:focus,
.note-item .note-text-input:focus,
.note-item .note-variant-input:focus {
    outline: none;
    border-color: #8b1a1a;
    box-shadow: 0 0 0 3px rgba(139, 26, 26, 0.25);
}
.note-item .note-text-input {
    resize: vertical;
    min-height: 46px;
    line-height: 1.4;
}
.note-item .note-variant-input {
    width: auto;
    min-width: 130px;
}

@media (max-width: 700px) {
    .note-item-fields {
        grid-template-columns: 1fr;
    }
}

/* Screen-reader-only text: the note's severity word, so its meaning does not live
   in the icon glyph alone. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------------------
   Phase 8 — storage browser.

   Two panes: a lazily-expanded tree on the left, the selected node's contents on
   the right. The grid is a plain flex matrix rather than CSS grid because a
   container's column count is data, not a stylesheet constant — a 4-wide Koval
   box and a 12-wide cryobox share these rules.
   ------------------------------------------------------------------------- */

.storage-browser {
    display: flex;
    gap: 20px;
    align-items: flex-start;
}

.storage-tree-pane {
    flex: 0 0 260px;
    max-height: 70vh;
    overflow-y: auto;
    border-right: 1px solid #e5e5e5;
    padding-right: 12px;
}

.storage-tree-pane h3 {
    margin: 0 0 8px;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #777;
}

.storage-detail-pane {
    flex: 1 1 auto;
    min-width: 0;
}

.storage-tree-row {
    display: flex;
    align-items: center;
    gap: 4px;
}

.storage-tree-toggle {
    background: none;
    border: none;
    cursor: pointer;
    color: #999;
    width: 18px;
    padding: 0;
}

.storage-tree-children.collapsed {
    display: none;
}

.storage-detail-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 8px;
}

.storage-breadcrumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px;
}

.storage-crumb {
    background: none;
    border: none;
    cursor: pointer;
    color: #8b1a1a;
    padding: 2px 4px;
    font-size: 0.92rem;
}

.storage-crumb.active {
    color: #333;
    font-weight: 600;
    cursor: default;
}

.storage-crumb-sep {
    color: #bbb;
}

.storage-detail-meta {
    display: flex;
    gap: 6px;
    margin-bottom: 12px;
}

.storage-detail-actions {
    display: flex;
    gap: 8px;
}

/* The occupancy grid. Cells are square-ish and sized to the label they carry. */
.storage-grid {
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow-x: auto;
}

.storage-grid-row {
    display: flex;
    gap: 4px;
}

/* A container too large to draw as a grid (QA4 A29): its occupied positions, listed. */
.storage-occupied-list {
    max-height: 360px;
    overflow-y: auto;
    margin: 4px 0 0;
    padding-left: 18px;
}

.storage-cell {
    flex: 0 0 auto;
    width: 54px;
    height: 46px;
    border: 1px solid #ddd;
    border-radius: 5px;
    background: #fff;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    padding: 2px;
}

.storage-cell:hover {
    border-color: #8b1a1a;
    box-shadow: 0 2px 8px rgba(139, 26, 26, 0.15);
}

/* An occupied cell reads like the open node of the tree (`.active-storage-node`): an inset red
   outline on a light tint. It used to be filled dark red, and the attention glow of a cell a
   "show in device" link points at (`.storage-cell-located`) was lost against it. */
.storage-cell.occupied {
    outline: 2px solid #8b1a1a;
    outline-offset: -2px;
    background: rgba(139, 26, 26, 0.06);
    border-color: transparent;
}

/* Full means the cell is at cell_capacity — which for a Koval FFPE box is six
   blocks, not one, so "occupied" and "full" are genuinely different states: a full
   cell carries the same outline on a stronger tint. */
.storage-cell.full {
    outline: 2px solid #8b1a1a;
    outline-offset: -2px;
    background: rgba(139, 26, 26, 0.18);
    border-color: transparent;
}

.storage-cell-label {
    font-size: 0.72rem;
    font-weight: 600;
    color: #555;
}

.storage-cell.occupied .storage-cell-label,
.storage-cell.full .storage-cell-label {
    color: inherit;
}

.storage-cell-count {
    font-size: 0.72rem;
    font-weight: 600;
}

.storage-children {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.storage-pos {
    color: #8b1a1a;
    font-weight: 600;
    font-size: 0.85rem;
}

/* Placement rows inside a preprocessing run (the storage-placement field). */
.pp-storage {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* The container scan/lookup box. Sized like the other inline scan inputs rather than
   inheriting the browser default, which rendered it as a ~120px stub beside its label. */
.pp-storage-barcode {
    flex: 0 1 260px;
    min-width: 180px;
    padding: 6px 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-family: monospace;
    font-size: 0.9rem;
}

.pp-storage-barcode:focus {
    outline: none;
    border-color: #1a4e8b;
    box-shadow: 0 0 0 2px rgba(26, 78, 139, 0.15);
}

.pp-storage-rows {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* The container control row: input + picker button + the resolved path. */
.pp-storage-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Secondary, explanatory text — an instruction, not content. Used for the placement
   prompts and the picker's "click a cell" line, none of which had any styling at all and
   so read as ordinary body copy. */
.pp-hint {
    margin: 6px 0 0;
    color: #6b7280;
    font-size: 0.85rem;
    line-height: 1.4;
}

/* Read-only because it is filled by the scan dialog, so it must not look disabled. */
.pp-storage-barcode[readonly] {
    background: #fff;
    cursor: pointer;
}

/* The resolved coordinate for one vial (e.g. "B4"), beside its row/col inputs. */
.pp-storage-label {
    font-family: monospace;
    font-size: 0.85rem;
    color: #1a4e8b;
    min-width: 48px;
}

.pp-storage-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.pp-storage-resolved {
    font-size: 0.85rem;
    color: #555;
}

.pp-storage-resolved.error {
    color: #b00020;
}

/* A placement row is a standard .user-row card; only the coordinate inputs are its own.
   (No display/padding overrides here — the card look must stay the card look.) */
.pp-storage-row .pp-storage-code {
    font-family: monospace;
}

.pp-storage-coords {
    display: flex;
    align-items: center;
    gap: 6px;
}

.pp-storage-coords label {
    font-size: 0.85rem;
    color: #6b7280;
    margin-left: 8px;
}

.pp-storage-row-idx,
.pp-storage-col-idx {
    width: 72px;
    padding: 6px 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

/* A coordinate outside the container's geometry: the card border says so too, not just
   the badge — a red chip alone is easy to miss in a long run. */
.pp-storage-row.pp-storage-invalid {
    border-color: #dc3545;
}

.pp-storage-row.pp-storage-invalid .pp-storage-row-idx,
.pp-storage-row.pp-storage-invalid .pp-storage-col-idx {
    border-color: #dc3545;
}

.pp-storage-label {
    font-family: monospace;
}

@media (max-width: 900px) {
    .storage-browser {
        flex-direction: column;
    }

    .storage-tree-pane {
        flex: 1 1 auto;
        max-height: 260px;
        border-right: none;
        border-bottom: 1px solid #e5e5e5;
        padding-right: 0;
        width: 100%;
    }
}

/* Leading marker on any breadcrumb trail — the global header bar and the storage
   browser's in-page one. Muted: it marks the start of the trail, it is not a control. */
.breadcrumb-icon {
    /* Centred on the trail rather than sitting on the text baseline, and in the same
       crimson as the links it introduces — at #999 it read as a rendering artefact. */
    display: inline-flex;
    align-items: center;
    color: #8b1a1a;
    margin-right: 8px;
    flex: 0 0 auto;
}

.breadcrumb-icon svg {
    display: block;
}

/* Back, breadcrumb and the level's actions share one row: they are all
   "where am I and what can I do here", and splitting them over two bars pushed the grid
   below the fold on a laptop. */
/* The tail and its action group are `display: contents` so their children become direct
   flex items of `.admin-list-toolbar`. That is what makes this screen line up with every
   other CRUD toolbar: the back and add buttons pick up the shared `.admin-list-toolbar .btn`
   sizing and the toolbar's own 12px gap, so the add button ends exactly where every other
   screen's does. The wrappers exist only so the whole tail can be re-rendered on selection. */
.storage-toolbar-tail,
.storage-toolbar-tail .storage-detail-actions {
    display: contents;
}

/* The trail takes the space between the two buttons and centres itself in it. */
.storage-toolbar .storage-breadcrumb {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 2px;
}

/* Tree rows are cards, like everything they mirror in the detail pane. */
.storage-tree-card {
    flex: 1 1 auto;
    min-width: 0;
    padding: 6px 10px;
    margin: 0;
}

.storage-tree-card .user-row-name {
    font-size: 0.9rem;
}

.storage-tree-row {
    display: flex;
    align-items: stretch;
    gap: 4px;
    margin-bottom: 4px;
}

/* The open node, using the same treatment as the receive screen's active box. */
.user-row.storage-tree-card.active-storage-node {
    outline: 2px solid #8b1a1a;
    outline-offset: -2px;
    background: rgba(139, 26, 26, 0.06);
}

/* "Nothing here yet" occupies the space a card would, and says it is a placeholder rather
   than looking like a list that failed to render. It keeps the card border and hover so it
   sits in the same family as the rows around it — only the muted fill marks it as empty. */
.storage-placeholder {
    background: #f6f6f6;
    justify-content: center;
    text-align: center;
    min-height: 64px;
}

.storage-placeholder .user-row-name {
    color: #444;
    font-weight: 600;
}

.storage-placeholder .user-row-username {
    color: #777;
}

/* Tree toggles carry the same chevrons as the rest of the builder, sized down for a
   260px sidebar rather than given the full card-row button chrome. */
.storage-tree-toggle svg {
    display: block;
}

/* A field that displays rather than accepts — the specimen's storage location, which is
   written through the storage browser and only read here. */
.readonly-value {
    padding: 8px 10px;
    background: #f6f6f6;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
    color: #444;
    min-height: 38px;
    display: flex;
    align-items: center;
}

/* A read-only value with the one action that applies to it (an enrollment's status and
   "withdraw"), in the standard `.field-with-action` row. Both halves take the height of
   a standard input, so the field lines up with the ones beside it. The hidden ruler IS
   that standard: a real input, laid out like any other, which the row stretches to — so
   this stays right if the input metrics ever change. Everything here hangs off the ruler:
   a `.readonly-value` that merely gained an FK link (the specimen's storage field) is the
   same row without one, and must keep its own padding. */
.field-with-action > .field-height-ruler ~ .readonly-value {
    min-height: 0;
    padding-top: 0;
    padding-bottom: 0;
}

.field-with-action > .field-height-ruler ~ .btn {
    white-space: nowrap;
    padding-top: 0;
    padding-bottom: 0;
}

.field-with-action > .field-height-ruler {
    width: 0;
    min-width: 0;
    padding-left: 0;
    padding-right: 0;
    border-left-width: 0;
    border-right-width: 0;
    /* Takes no room of its own: this cancels the gap it would otherwise open. */
    margin-right: -6px;
    visibility: hidden;
}

/* Storage position picker (SOP-run placement). The tree it draws is the admin browser's,
   so the grid/card/breadcrumb styles are inherited; only the modal frame is new. */
.storage-picker-content {
    max-width: 760px;
    width: 90vw;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.storage-picker-body {
    overflow: auto;
    flex: 1 1 auto;
    min-height: 120px;
}

.storage-picker-crumbs {
    flex: 0 0 auto;
}

.storage-picker-hint {
    flex: 0 0 auto;
    margin: 0;
}

/* The cell a "show in device" link pointed at (the run viewer's placement rows): a stronger
   form of the app's attention glow, with the fill pulsing between white and the full cell's
   tint, three times and done. Only here — the shared `pulse-attention` stays as it is. */
.storage-cell.storage-cell-located {
    outline: 2px solid #8b1a1a;
    outline-offset: -2px;
    animation: pulse-located-cell 2s ease-in-out 3;
}

@keyframes pulse-located-cell {
    0%,
    100% {
        background: #fff;
        box-shadow: 0 0 0 0 rgba(139, 26, 26, 0.6);
    }
    50% {
        background: rgba(139, 26, 26, 0.18);
        box-shadow: 0 0 0 10px rgba(139, 26, 26, 0);
    }
}

/* Cells this run intends to fill — shown before the operator commits, so the whole span
   is visible rather than discovered one vial at a time. */
.storage-cell.planned {
    outline: 2px solid #1a4e8b;
    outline-offset: -2px;
    background: rgba(26, 78, 139, 0.12);
}

.pp-storage-pick {
    flex: 0 0 auto;
}

/* ── Specimen provenance timeline (Phase 26) ──────────────────────────────────
   A three-column grid rather than a table: the rows are read down the time column,
   and a table forced the detail cell — which holds a variable number of field
   diffs — to share a width with every other row's. */
.prov-subject {
    margin: 0;
    color: var(--text-muted, #8a8a8a);
}
.prov-timeline {
    display: flex;
    flex-direction: column;
}
.prov-line {
    display: grid;
    grid-template-columns: 8.5rem 8rem 1fr;
    gap: 4px 14px;
    padding: 8px 0;
    border-top: 1px solid var(--border-color, #e3e3e3);
    align-items: start;
}
.prov-line:first-child {
    border-top: none;
}
.prov-when {
    font-variant-numeric: tabular-nums;
    color: var(--text-muted, #8a8a8a);
    white-space: nowrap;
}
.prov-who {
    font-weight: 500;
    overflow-wrap: anywhere;
}
.prov-what-label {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}
.prov-detail {
    color: var(--text-muted, #8a8a8a);
    font-size: 0.9em;
}
/* One changed attribute per row, three cells wide, and the cells line up down the whole diff.
   A registration changes eight attributes at once; joined onto one line with separators it was
   a paragraph, and the one attribute a reader came to check was somewhere inside it.

   `display: contents` on the row is what makes the columns align: the cells become children of
   this grid, so every row's "what changed" column is as wide as the widest name, not as wide as
   its own. */
.prov-changes {
    display: grid;
    grid-template-columns: max-content max-content 1fr;
    gap: 2px 10px;
    margin-top: 2px;
}
.prov-change {
    display: contents;
}
.prov-field {
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}
.prov-from,
.prov-to,
.prov-value {
    overflow-wrap: anywhere;
}
/* The arrow belongs to the layout, not to the text: putting it in the markup would place it in
   the CSV export and in `textContent` too. */
.prov-to::before {
    content: '→ ';
}
/* Nothing changed *from* — a create, or a value recorded for the first time. The value takes
   both remaining columns rather than pretending to be the right-hand side of a pair. */
.prov-change-single .prov-value {
    grid-column: 2 / -1;
}
/* The several rows of one act, folded under it. Closed by default: the heading says what
   happened, and this says exactly what that touched. */
.prov-group > summary {
    cursor: pointer;
    color: var(--text-muted, #8a8a8a);
    font-size: 0.9em;
}
.prov-sub {
    margin-top: 6px;
    padding-left: 12px;
    border-left: 2px solid var(--border-color, #e3e3e3);
}
.prov-sub-label {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    font-size: 0.95em;
}
/* An operator's stated reason is the part a reviewer reads first — quoted, not muted. */
.prov-reason {
    font-style: italic;
    margin-top: 2px;
}
/* The boundaries of the trail. Stated plainly rather than styled as a warning: these are
   honest limits of what is recorded, not faults to alarm anybody about. */
.prov-notes {
    margin: 0;
    padding-left: 1.2em;
    color: var(--text-muted, #8a8a8a);
    font-size: 0.9em;
}
/* "Show reads": access rows interleaved into the timeline where they fall in time. Muted,
   because they are a different kind of line — somebody looked, nothing happened to the
   vial — and hidden until asked for, because they outnumber everything else. */
.prov-reads-toggle {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}
.prov-reads-status {
    color: var(--text-muted, #8a8a8a);
    font-size: 0.9em;
}
.prov-timeline:not(.prov-show-reads) .prov-read,
.prov-timeline:not(.prov-show-reads) .prov-read-more {
    display: none;
}
.prov-timeline.prov-show-reads .prov-read-hides {
    display: none;
}
.prov-read {
    padding: 4px 0;
    color: var(--text-muted, #8a8a8a);
    font-size: 0.9em;
    background: #fafafa;
}
.prov-read .prov-what-label {
    font-weight: 400;
}
.prov-read-more {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 6px 0;
    border-top: 1px dashed var(--border-color, #e3e3e3);
    color: var(--text-muted, #8a8a8a);
    font-size: 0.9em;
}

/* Phone: the three columns become two lines — time + actor, then the action and its
   detail full width — so a long field diff is not squeezed into a third of the screen. */
@media (max-width: 640px) {
    .prov-line {
        grid-template-columns: 8.5rem 1fr;
    }
    .prov-what {
        grid-column: 1 / -1;
    }
    /* Two columns, and the old value goes above the new one rather than beside it: three
       columns of free-text values do not fit a phone, and a wrapped value is worse than a
       stacked one. */
    .prov-changes {
        grid-template-columns: max-content 1fr;
    }
    .prov-to {
        grid-column: 2;
    }
}

/* ── Manual intake (Phase 25C) ────────────────────────────────────────────── */
/* One layout for both verdicts; a disabled input is how a derived field reads. */
/* The receipt form. A `builder-fieldset` like every other titled block in the app — what this
   replaced hung its rows on `.field-row`, which has no rule of its own anywhere in this file:
   only `.field-row.row-action` and `.field-row .field-row-badges`, both modifiers for rows of a
   *list*. So the form, and everything rendered inside it, had no layout at all. */
.intake-screen .intake-form {
    max-width: 720px;
}
/* The scanned code, stated rather than re-typed. Monospaced and tabular so a transposed
   character in a nine-character code is something the eye can catch. */
.intake-code-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
}
/* The enrollment preview under the attach field: what the code the operator typed resolves to,
   so they can see who they are about to attach a vial to before they do it. */
.intake-attached {
    margin-top: 0.35rem;
    padding: 0.4rem 0.6rem;
    border-left: 3px solid var(--border-color, #cbd5e0);
    color: var(--text-muted, #718096);
    font-size: 0.9em;
}
.intake-attached:empty {
    display: none;
}
.intake-donor-choice {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
}
/* The two radio labels keep their text on one line; the enrollment field is what gives way.
   Without `nowrap` + `flex: 0 0 auto` the input held its intrinsic width, the labels shrank to
   make room, and "New pre-allocated enrollment" wrapped across three lines on a screen with
   space to spare. */
.intake-donor-choice label {
    display: inline-flex;
    flex: 0 0 auto;
    gap: 0.35rem;
    align-items: center;
    font-weight: 400;
    white-space: nowrap;
}
.intake-donor-choice #intake-pd {
    flex: 1 1 12rem;
    min-width: 8rem;
}
/* Freeze/thaw cycles on a specimen's detail: the count, the action, then the rows. */
.ft-count {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    margin-bottom: 0.5rem;
}
.ft-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-bottom: 0.4rem;
}

.intake-when {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}
.intake-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1rem;
}
.intake-no-undo {
    margin-top: 0.5rem;
    font-style: italic;
}
.intake-banner-ok {
    color: #2f855a;
}
.intake-banner-warn {
    color: #975a16;
}
.intake-banner-error {
    color: #c53030;
}
@media (max-width: 640px) {
    /* The date, time and "now" of a collection time stack rather than squeeze. `.intake-code-row`
       used to be here too; the code is stated text now, with no row of its own to reflow. */
    .intake-when {
        flex-direction: column;
        align-items: stretch;
    }
}

/* ── Clinical data, read-only (Phase 25F) ─────────────────────────────────── */
.clin-subject {
    margin: 0 0 0.75rem;
}
.clin-phase {
    margin-bottom: 1.25rem;
}
.clin-phase h4 {
    margin: 0 0 0.5rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--border-color, #e2e8f0);
}
/* Each capture is a framed block titled by its own form — consents, epidemiology, medications
   — because a clinical document is a stack of separate records and, rendered as bare sequences
   of fields, they ran into one another. */
.clin-form {
    margin-bottom: 0.75rem;
}
.clin-form-title {
    font-weight: 600;
}
/* State + timestamp ride in the frame's top-right, out of the reading order of the answers. */
.clin-form-head {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: baseline;
    justify-content: flex-end;
}
.clin-badge {
    padding: 0.05rem 0.4rem;
    border-radius: 3px;
    font-size: 0.75rem;
}
.clin-draft {
    background: #e2e8f0;
    color: #2d3748;
}
/* A step the study asks for and nobody answered. Muted, not alarming: an unanswered question is
   a fact about the record, not a fault to flag — but it must be visible, because its absence is
   what a reader would otherwise mistake for "this study does not collect it". */
.clin-missing {
    background: transparent;
    border: 1px dashed var(--border-color, #cbd5e0);
    color: var(--text-muted, #718096);
}
.clin-form-missing {
    border-style: dashed;
}
.clin-form-missing > legend {
    color: var(--text-muted, #718096);
}
.clin-when {
    color: var(--text-muted, #718096);
    font-size: 0.8rem;
}
/* The captured values are rendered by the form engine now, so they carry the wizard's own
   styling. What remains here is the fallback used when a definition cannot be resolved — a
   form deleted since, or a version the server no longer serves — where there is nothing but
   stored keys to show. `.clin-sub`, `.clin-list` and `.clin-empty` went with the hand-rolled
   renderer that produced them. */
.clin-fields {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    gap: 0.2rem 0.75rem;
    margin: 0.35rem 0 0;
}
.clin-fields dt {
    color: var(--text-muted, #718096);
    word-break: break-word;
}
.clin-fields dd {
    margin: 0;
    word-break: break-word;
}
/* The fallback reading, when no definition could be loaded. Stated, not styled as an alarm:
   it is a limit of what could be shown, and a reader must not mistake it for the form. */
.clin-degraded {
    margin: 0.35rem 0 0.2rem;
    color: var(--text-muted, #718096);
    font-size: 0.85rem;
    font-style: italic;
}

/* A form's fields inside the read-only document: the engine's own layout, indented under the
   heading that names the capture so a scrollable record of a dozen forms stays legible. */
.clin-form-body {
    margin-top: 0.35rem;
}
.clin-form-body .form-group {
    margin-bottom: 0.5rem;
}
@media (max-width: 640px) {
    .clin-fields {
        grid-template-columns: 1fr;
    }
    .clin-fields dt {
        margin-top: 0.4rem;
        font-weight: 600;
    }
}

/* The manual-intake card ships hidden and is revealed once the server says the operator has a
   project that accepts it — see `screens/lab-dashboard.js` for why it is not appended later. */
.intake-card-hidden {
    display: none;
}

/* Registration: the identity taken over from a person on record is held read-only, with the
   one way to change it (screens/registered-identity.js). */
.donor-correct-identity {
    background: none;
    border: none;
    padding: 0;
    margin-top: 4px;
    color: #2f6fdb;
    cursor: pointer;
    font: inherit;
}
.donor-correct-identity:hover {
    text-decoration: underline;
}
#donor-country-picker.is-readonly {
    opacity: 0.7;
}
/* The identity of a person on record, held while it is shown (QA4 C4-03): a value to read,
   not a white box to type in. The keyboard's focus ring stays — it is where Tab is. */
.form-group input.is-readonly,
.form-group select.is-readonly {
    background: #e9ecef;
    color: #495057;
    border-color: #dee2e6;
    cursor: default;
}

/* ── QA5 round ─────────────────────────────────────────────────────────── */

/* A dialog's refusal of what was typed (bb-base `validate`): said under the field, the dialog
   still open (QA5 E5-10, M12). */
.dialog-field-error {
    margin: 8px 0 0;
    color: var(--clr-error, #dc3545);
    font-size: 0.85rem;
    font-weight: 500;
}

/* One long word in a field's label kept every row's Settings/Remove out of the composer card
   (QA5 B5-6): the label wraps, the action bar keeps its width. */
.builder-sortable-item .user-row-info {
    min-width: 0;
    overflow-wrap: anywhere;
}
.builder-sortable-item > .user-row-actions {
    flex-shrink: 0;
}
.builder-preview {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Deep nesting squeezed a condition's controls to nothing (QA5 B5-7): a nested group indents
   less, and a row's controls wrap rather than shrink. */
.cond-group .cond-group {
    padding-left: 6px;
    padding-right: 26px;
}
.cond-group .rule-card-fields {
    flex-wrap: wrap;
}
.cond-group .rule-card-field {
    min-width: 8rem;
}

/* A multi picker's list as wide as its field, however long an option (QA5 C5-05): the label
   wraps, the code column keeps its width. */
.ss-multi-picker .search-picker-dropdown {
    overflow-x: hidden;
}
.ss-option {
    gap: 8px;
}
.ss-option-label {
    min-width: 0;
    overflow-wrap: anywhere;
}
.ss-option-key {
    flex-shrink: 0;
}

/* A default value: chosen from the list its field's type offers, or typed when Custom. */
.field-default-group .default-choice + input {
    margin-top: 6px;
}

/* A scanned device reads as "name (code)": the code stays in the field's own input, which the
   form collects, and the read-only twin beside it is what the operator sees and clicks to scan. */
.device-scan-code {
    display: none;
}
.form-group .device-scan-display[readonly] {
    background: #fff;
    cursor: pointer;
}

/* A scan being answered: the field stays (read-only) and takes the next scan's keys after it
   (QA5 D5-6). */
.scan-input.scan-busy {
    opacity: 0.6;
}

/* Whose history the audit list shows, and why it may (QA5 A5-22). */
.audit-dsar-heading {
    flex-basis: 100%;
    margin: 4px 0;
    font-weight: 600;
}
