/* ─────────────────────────────────────────────────────────────────────────
   Holter Dashboard — Clinical Theme Overrides
   ───────────────────────────────────────────────────────────────────────── */

/* Compact cards */
.card-header { padding: 0.5rem 0.75rem; }
.card-body   { padding: 0.6rem 0.75rem; }

/* Summary stat cards */
.card .card-body h5 { margin-bottom: 0; font-weight: 700; }
.card .card-body .text-muted { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; }

/* Tighter tab content */
.tab-content { padding-top: 0 !important; }

/* Dark log area scroll */
pre::-webkit-scrollbar       { width: 6px; }
pre::-webkit-scrollbar-thumb { background: #555; border-radius: 3px; }
pre::-webkit-scrollbar-track { background: #1e1e1e; }

/* Graph cards: remove inner padding for cleaner look */
.card .js-plotly-plot { margin: 0 !important; }

/* Navbar branding */
.navbar-brand { font-weight: 600; font-size: 1.15rem; }

/* Responsive grid tweaks for small screens */
@media (max-width: 992px) {
    .card-body .row > [class*="col-"] { margin-bottom: 0.4rem; }
}

/* Progress bar thin style */
.progress { border-radius: 2px; }

/* Analysis log monospace */
#analysis-log {
    line-height: 1.45;
    word-break: break-word;
}

/* ECG strip browser slider */
.rc-slider-track { background-color: #2196F3 !important; }
.rc-slider-handle { border-color: #2196F3 !important; }

/* ── Flag panel ─────────────────────────────────────────────────────────── */
#flag-panel .offcanvas-body { padding: 0.75rem; }
#flag-list-container .list-group-item { padding: 0.4rem 0.6rem; font-size: 0.85rem; }
#flag-list-container .list-group-item:hover { background-color: #fff8e1; }
#flag-list-container .list-group { border-radius: 4px; }

/* Flag toggle button badge */
#flag-toggle-btn .badge { font-size: 0.7rem; vertical-align: middle; }

/* ── M11.c.1 — Beats grid active-cell highlight ─────────────────────────── */
/* Active-cell visual state is toggled by a clientside callback in
   callbacks/beats_grid.py — server doesn't re-render the grid on cell
   clicks anymore. The .beats-grid-cell-active class is added to the
   clicked cell and removed from the others. */
/* Dark cells, as CardiAro's drill-in (lab 2026-10-08). */
#beats-grid-grid { background: #161616; padding: 6px; border-radius: 4px; }
.beats-grid-cell {
    border: 1px solid #8c8c8c;
    border-radius: 4px;
    background: #242424;
    cursor: pointer;
    width: 100%;
    text-align: left;
    padding: 0;
    transition: border-color 80ms, box-shadow 80ms;
    /* The cells are drawn by assets/beats_grid_scroll.js. A long list holds
       thousands of them; the browser skips the ones scrolled out of view. */
    position: relative;
    overflow: hidden;
    content-visibility: auto;
    contain-intrinsic-size: auto 212px;
}
.beats-grid-wrap {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 8px;
}
/* The 'n n v n n' letter row above each cell picture. */
.beats-grid-letters {
    padding: 2px 4px;
    line-height: 1.0;
    white-space: nowrap;
    overflow: hidden;
    font-family: ui-monospace, monospace;
    font-size: 12px;
    position: relative;
    min-height: 17px;
}
.beats-grid-letters span { margin-right: 2px; }
/* A letter placed over its own beat (the dashed line under it). */
.beats-grid-letters span.at { position: absolute; top: 2px; margin: 0; transform: translateX(-50%); }
.beats-grid-letters span.focal { font-weight: bold; text-decoration: underline; }
/* The letters of the neighbouring beats are quieter than the cell's own
   beat: a neighbour is another cell's beat shown again, and at full strength
   a relabel looked as if it had changed the beats next to it (lab 2026-10-08). */
.beats-grid-letters span:not(.focal) { opacity: 0.55; }
.beats-grid-picture { pointer-events: none; }
/* Similarity to the template, shown only on the outlier cells. */
.beats-grid-similarity {
    position: absolute;
    right: 5px;
    bottom: 3px;
    font-size: 12px;
    font-weight: 700;
    color: #f2f2f2;
    pointer-events: none;
}
/* The selected cell: CardiAro's orange frame. */
.beats-grid-cell-active {
    border: 2px solid #FF9800 !important;
    box-shadow: 0 0 0 1px #FF9800 inset !important;
}

/* ── M11.d — Beats grid brush-mode multi-select highlight ─────────────────── */
.beats-grid-cell-brushed {
    border: 2px solid #26C6DA !important;     /* cyan — distinct from the */
    box-shadow: 0 0 0 1px #26C6DA inset !important;  /* orange active ring */
}
/* The frame drawn while dragging over the cells to select several
   (assets/beats_grid_scroll.js). */
.beats-grid-band { position: fixed; z-index: 2000; pointer-events: none;
                   border: 1px dashed #26C6DA; background: rgba(38, 198, 218, 0.15); }
#beats-grid-grid { user-select: none; }
/* When in brush mode, give cells a subtle hover cue so the analyst sees */
/* the cell is clickable for selection. */
.beats-grid-brush-active .beats-grid-cell:hover {
    background: #33342a;
    cursor: cell;
}

/* ── P1-3 (closed 2026-05-05) — Disabled-button cursor feedback ───────── */
/* Bootstrap's btn:disabled keeps a default cursor on the disabled state */
/* in some browsers, which makes the button feel "frozen" rather than */
/* deliberately unavailable. Force not-allowed so the analyst gets a */
/* clear visual rejection signal when they click Prev on page 1 / Next */
/* on the last page. Matches the audit's requested affordance. */
.btn[disabled],
.btn:disabled,
button[disabled] {
    cursor: not-allowed !important;
}

/* ── BUG-008 (round 7 fix, 2026-05-07) — html / body scroll viewport ─── */
/* Round-1 attempt was wrong: `html, body { height: auto !important }` */
/* made BOTH html AND body grow to content height (1604px), so they */
/* matched and produced ZERO overflow — `window.scrollTo()` was a no-op */
/* because there was no scrolling viewport. */
/* */
/* Correct fix: */
/*   html  → height 100% (locked to viewport — scrolling container) */
/*   body  → min-height 100%, height auto (grows past viewport on tall */
/*           pages, creating overflow that html scrolls) */
/* */
/* Combined with the BUG-001/002 overflowY:visible on the admin */
/* DataTable, wheel events bubble from the table to the document and */
/* the page scrolls naturally. */
html {
    height: 100% !important;
}
body {
    min-height: 100% !important;
    height: auto !important;
}

/* ── BUG-007 / BUG-011 (closed 2026-05-07) — outline-secondary contrast ─ */
/* Bootstrap's btn-outline-secondary on a white card surface reads as */
/* "disabled" — the audit reported analysts not noticing the Refresh / */
/* Refresh users / Edit / Legacy .dat buttons because they look greyed */
/* out. Bump the border + text weight on enabled outline-secondary so */
/* the affordance is visible. Disabled state still grey via Bootstrap */
/* :disabled, so the contrast between enabled and disabled is preserved. */
.btn-outline-secondary:not(:disabled):not(.disabled) {
    color: #424242;
    border-color: #757575;
    border-width: 1.5px;
    font-weight: 500;
}
.btn-outline-secondary:not(:disabled):not(.disabled):hover {
    background-color: #424242;
    border-color: #424242;
    color: white;
}

/* ── BUG-017 (closed 2026-05-07) — Events kind-row affordance ──────── */
/* Audit reported the Events tree kind-rows ("Pause", "SVE Run", etc.) */
/* rendering in light grey "disabled-looking" text so analysts couldn't */
/* tell they were clickable. The rows are dbc.Button(color="light", */
/* outline=True) — make them visibly clickable. */
[id*="events-v2-kind-row"] {
    cursor: pointer !important;
    color: #212529 !important;
    transition: background-color 80ms;
}
[id*="events-v2-kind-row"]:hover {
    background-color: #FFF8E1 !important;
}

/* ── KWP-11 (closed 2026-05-05) — Borsam ingest mode radio ─────────────── */
/* Audit reported BOTH "Folder path on SERVER" and "ZIP upload" radios */
/* appearing selected simultaneously. Native HTML radios are correctly */
/* exclusive (shared name, browser handles single-select). The visual */
/* leak is bootstrap's default form-check-inline styling not differentiating */
/* the checked label from the unchecked label hover state. */
/* Make the active radio's label visibly bold + branded so the analyst */
/* can never mistake the unchecked option for an active one. */
.ib-mode-radios .form-check-inline .form-check-label {
    color: #757575;
    font-weight: 400;
    transition: color 80ms, font-weight 80ms;
}
.ib-mode-radios .form-check-inline .form-check-input:checked ~ .form-check-label,
.ib-mode-radios .form-check-inline .form-check-input:checked + .form-check-label {
    color: #1565C0;
    font-weight: 600;
}

/* ── BUG-C-003 (closed 2026-05-05) — PageScan opacity anchor ──────────── */
/* The audit reported PageScan content rendering at ~10–20 % opacity */
/* after interacting with an event-kind row on the Events tab. The */
/* root cause was not a single bleeding rule we could trace inline, */
/* but the symptom is consistent with a parent-tree style cascade or */
/* with stale CSS state during a renderer-freeze recovery (the audit */
/* coincided with the BUG-D-004 freeze condition, now closed). */
/* */
/* Defensive anchor: PageScan's tab body and the strip-render */
/* containers are explicitly pinned to opacity: 1 so no inherited or */
/* cascaded opacity can collapse the analyst's primary reading */
/* surface. The events_tab_v2 mosaic intentionally fades unaccepted */
/* event cards via inline style="opacity: 0.55" — that's scoped to */
/* the Card element so it doesn't leak through this anchor. */
#page-scan-tab-content,
#page-scan-scroll-container,
#page-scan-window-container,
#page-scan-window-container .js-plotly-plot {
    opacity: 1 !important;
}

/* PageScan: the scan rows, drawn by the page script in callbacks/page_scan.py
   from page-scan-scan-rows-store. The image takes the row minus the 62 px
   clock label and the 4 px gap; the tick positions rely on that. */
.ps-row {
    display: flex;
    align-items: flex-start;
    gap: 4px;
    cursor: crosshair;
    position: relative;
}
/* Light on dark, as the row images are (utils/page_scan_raster.py). */
.ps-row-label {
    width: 62px;
    flex: 0 0 62px;
    font-size: 11px;
    font-weight: 600;
    font-family: monospace;
    color: #f0f0f0;
    padding-top: 9px;
}
.ps-row img {
    width: calc(100% - 66px);
    height: auto;
    display: block;
    user-select: none;
}
/* PageScan: ectopy ticks drawn over the scan rows (callbacks/page_scan.py). */
.ps-tick {
    position: absolute;
    width: 4px;
    margin-left: -2px;
    pointer-events: none;
    opacity: 0.85;
}
.ps-tick-v { top: 24%; height: 52%; background: #f01414; }
.ps-tick-s { top: 34%; height: 32%; background: #c75cff; }

/* AFIB tab: right-click menu on the ECG strip (assets/afib_strip_menu.js). */
.afib-menu {
    position: fixed;
    z-index: 3000;
    min-width: 330px;
    background: #fff;
    border: 1px solid #b8b8b8;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
    padding: 4px 0;
    font-size: 12px;
}
.afib-menu-head { padding: 4px 12px; color: #666; font-size: 11px; }
.afib-menu-sep { border-top: 1px solid #e3e3e3; margin: 4px 0; }
.afib-menu-item { padding: 5px 12px; cursor: pointer; }
.afib-menu-item:hover { background: #eef3fb; }
.afib-menu-range {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 12px;
}
.afib-menu-range-name { flex: 1 1 auto; }
.afib-menu-range button {
    font-size: 11px;
    padding: 1px 8px;
    border: 1px solid #c9c9c9;
    border-radius: 3px;
    background: #fafafa;
    cursor: pointer;
}
.afib-menu-range button:hover:not(:disabled) { background: #ffe9c7; border-color: #ff9800; }
.afib-menu .disabled, .afib-menu-range button:disabled { color: #aaa; cursor: default; }

/* Template tab, CardiAro style (callbacks/template_tab.py). */
.tpl-card {
    width: 100%;
    padding: 0;
    border: 1px solid #1b2a4e;
    border-radius: 2px;
    background: #fff;
    cursor: pointer;
    text-align: left;
    overflow: hidden;
}
.tpl-card-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: #0b2265;
    color: #fff;
    font-family: ui-monospace, monospace;
    font-size: 12px;
    font-weight: 700;
    padding: 1px 4px;
}
.tpl-card .tpl-count, .tpl-card .tpl-num { color: #ffeb3b; }
.tpl-card.tpl-selected { border-color: #ff8c00; box-shadow: 0 0 0 1px #ff8c00; }
.tpl-card.tpl-selected .tpl-card-head { background: #ff8c00; }
.tpl-card.tpl-selected .tpl-count, .tpl-card.tpl-selected .tpl-num { color: #fff; }
.tpl-card-note { font-size: 10px; min-height: 0; padding: 0 4px; color: #666; }
.tpl-card-img { display: block; width: 100%; height: 150px; pointer-events: none; }
.tpl-relabel-bar {
    border-top: 1px solid #e0e0e0;
    padding: 6px 8px;
    margin: 0 8px;
}
.tpl-list-head {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    flex: 0 0 auto;
    background: #ff8c00;
    color: #fff;
    font-family: ui-monospace, monospace;
    font-size: 12px;
    font-weight: 700;
    text-align: center;
    padding: 2px 4px;
}
.tpl-beat-list {
    height: 48vh;
    overflow-y: auto;
    overflow-x: hidden;
    border: 1px solid #c8cfe0;
    background: #fff;
}
.tpl-beat-row {
    border-bottom: 1px solid #eef0f6;
    cursor: pointer;
    content-visibility: auto;
    contain-intrinsic-size: auto 45px;
}
.tpl-beat-row:hover { background: #f4f7ff; }
/* The row shown in the strip is drawn in red, as in CardiAro. */
.tpl-beat-row.active { background: #fff3ee; }
.tpl-beat-row.active polyline { stroke: #e01010; stroke-width: 1.4; }
.tpl-beat-row svg { pointer-events: none; }
.tpl-list-note { color: #888; font-size: 11px; text-align: center; padding: 20px 6px; }

/* Events tab: the scrolling event list (assets/events_list.js). */
.ev-list {
    /* Rows settle on a card edge, so the first card on screen (the selected
       one) is a whole card. */
    scroll-snap-type: y proximity;
    height: 36vh;
    min-height: 240px;
    overflow-y: auto;
    overflow-x: hidden;
    border: 1px solid #1b2a4e;
    background: #fff;
    outline: none;
}
.ev-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px; }
.ev-card {
    scroll-snap-align: start;
    position: relative;
    border: 1px solid #c9cfdd;
    cursor: pointer;
    background: #fff;
    content-visibility: auto;
    contain-intrinsic-size: auto 112px;
}
.ev-card.selected { outline: 2px solid #ff8c00; outline-offset: -2px; }
.ev-card.rejected svg { opacity: 0.35; }
.ev-card-head, .ev-card-foot {
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: ui-monospace, monospace;
    font-size: 11px;
    font-weight: 700;
    padding: 0 4px;
    line-height: 1.3;
}
.ev-card-foot { justify-content: space-between; }
.ev-card-head input { margin: 0; cursor: pointer; }
.ev-badge {
    font-size: 9px;
    font-weight: 600;
    border-radius: 3px;
    padding: 0 4px;
    color: #fff;
}
.ev-badge.rej { background: #c62828; }
.ev-badge.acc { background: #2e7d32; }
.ev-badge.nop { background: #757575; }
.ev-badge.art { background: #ef6c00; }
.ev-card svg { pointer-events: none; }

/* ── Fixed review frame (Template, Events, AFIB, PageScan) ────────────────
   Lab 2026-10-05, from CardiAro: "the frame is fixed and the ECG plots in
   the section below all the time". The tab is exactly as tall as the window
   leaves it (--rv-top is the top of the tab area, kept by
   assets/review_frame.js). The top pane scrolls inside itself; the bottom
   pane is the ECG strip and never moves. */
.rv-frame {
    display: flex;
    flex-direction: column;
    height: calc(100vh - var(--rv-top, 170px) - 4px);
    min-height: 560px;
    overflow: hidden;
}
.rv-frame > .rv-top {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
}
.rv-frame > .rv-bottom {
    flex: 0 0 auto;
    border-top: 2px solid #1b2a4e;
    background: #fff;
}
/* A top pane made of columns: each column scrolls by itself. */
.rv-frame > .rv-top.rv-top-cols {
    display: flex;
    flex-wrap: nowrap;
    overflow: hidden;
}
.rv-top-cols > [class*="col"] { min-height: 0; overflow-y: auto; }
/* Template: the grid scrolls, the relabel bar stays under it; the beat list
   takes what the right column has left. */
.rv-top-cols > .tpl-middle,
.rv-top-cols > .tpl-right,
.rv-top-cols > .ev-right { display: flex; flex-direction: column; overflow: hidden; }
.rv-frame .tpl-beat-list { height: auto; flex: 1 1 120px; min-height: 90px; }
.rv-frame .tpl-right .dash-graph { flex: 0 0 auto; }
/* Events: the list takes what is left above the action bar. */
.rv-frame .ev-list { height: auto; flex: 1 1 140px; min-height: 120px; }
/* PageScan: the scan takes what is left under its controls. */
.rv-frame > .rv-top.ps-top { display: flex; flex-direction: column; overflow: hidden; }
.rv-frame .ps-scan { flex: 1 1 140px; min-height: 110px; }
/* PageScan controls: one toolbar row and one thin status line, so the scan
   gets the height (lab 2026-10-14). The toolbar wraps on a narrow window. */
.ps-toolbar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
    margin-bottom: 2px;
}
.ps-toolbar .btn { padding: 1px 7px; }
.ps-toolbar .form-select { padding-top: 1px; padding-bottom: 1px; }
.ps-tool-sep { width: 1px; height: 18px; background: #ccd; margin: 0 4px; }
.ps-statusline {
    display: flex; align-items: center; gap: 12px;
    font-size: 11px; min-height: 15px; line-height: 15px;
    margin-bottom: 3px; white-space: nowrap; overflow: hidden;
}
/* Slimmer bars above the frame. */
.navbar { padding-top: 0.2rem; padding-bottom: 0.2rem; }
.rv-header { font-size: 13px; }

/* Analysis Dashboard inside the fixed frame (2026-10-05). The strip grows
   with the chosen width (2.5 s is far taller than 25 s); it never takes more
   than this share of the frame and scrolls inside its pane beyond that. */
.rv-frame > .rv-bottom.rv-bottom-cap { max-height: 80%; overflow-y: auto; overflow-x: hidden; }
.rv-dash .rv-frame > .rv-top { padding-top: 6px; }

/* Template QRS list: channel selector in the header and its right-click menu. */
.tpl-ch-select {
    width: 72px; height: 20px; min-height: 0; padding: 0 20px 0 6px;
    font-size: 11px; font-weight: 700; line-height: 18px;
    background-position: right 4px center;
}
.tpl-ch-menu {
    position: fixed; z-index: 3000; min-width: 96px; padding: 3px 0;
    background: #fff; border: 1px solid #9aa3b5;
    box-shadow: 2px 3px 8px rgba(0, 0, 0, .25); font-size: 13px;
}
.tpl-ch-menu div { padding: 3px 14px 3px 26px; cursor: pointer; position: relative; }
.tpl-ch-menu div:hover { background: #dbe6ff; }
.tpl-ch-menu div.on::before { content: "\2713"; position: absolute; left: 8px; }
/* "C": the template the current beat belongs to (CardiAro's orange badge). */
.tpl-card { position: relative; }
.tpl-card.tpl-current::after {
    content: "C"; position: absolute; top: 24px; right: 4px;
    width: 18px; height: 18px; line-height: 18px; text-align: center;
    background: #ff8c00; color: #fff; font-weight: 700; font-size: 12px;
    border-radius: 3px; pointer-events: none;
}
/* Dashboard, compact inside the frame: thin statistics chips, the filter
   note beside the position label, tighter card padding. */
.rv-dash #summary-stats-row { margin-bottom: 6px !important; }
.rv-dash #summary-stats-row .card-body { padding: 2px 6px; }
.rv-dash #summary-stats-row h5 { font-size: 1rem; margin: 0; }
.rv-dash .rv-top .card { margin-bottom: 6px !important; }
.rv-dash .rv-top .card-header { padding: 3px 10px; }
.rv-dash .rv-bottom .card-header { padding: 4px 10px; }
.rv-dash .rv-filter-note { float: left; font-size: 11px; margin: 3px 0 0 !important; }
.rv-dash #strip-position-label { margin-bottom: 2px !important; }
/* The "C" count and its Clear button under the Visible Type list. */
.tpl-c-line { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 11px; color: #555; }
.tpl-c-key { background: #ff8c00; color: #fff; font-weight: 700; border-radius: 3px; width: 16px; height: 16px; line-height: 16px; text-align: center; font-size: 11px; }
.tpl-c-clear { margin-left: auto; border: 1px solid #c8cfe0; background: #fff; border-radius: 3px; font-size: 10px; padding: 0 6px; color: #555; }
.tpl-c-clear:hover { background: #f1f3f8; }

/* The recording scroll bar under a review strip (assets/strip_scrubber.js). */
.rec-scrub { display: flex; align-items: center; gap: 4px; height: 18px; margin: 2px 0 3px; user-select: none; }
.rec-scrub-step { flex: 0 0 auto; width: 18px; height: 16px; line-height: 12px; padding: 0; font-size: 11px;
                  border: 1px solid #b9c1d3; background: #f3f5fa; color: #334; border-radius: 2px; }
.rec-scrub-step:hover { background: #e2e7f3; }
.rec-scrub-track { position: relative; flex: 1 1 auto; height: 12px; background: #eef1f7; border: 1px solid #c8cfe0;
                   border-radius: 2px; cursor: pointer; touch-action: none; }
.rec-scrub-thumb { position: absolute; top: -1px; width: 16px; height: 12px; margin-left: -8px; background: #8792ab;
                   border-radius: 2px; pointer-events: none; }
.rec-scrub.dragging .rec-scrub-thumb, .rec-scrub-track:hover .rec-scrub-thumb { background: #1b2a4e; }
.rec-scrub-time { flex: 0 0 auto; min-width: 150px; text-align: right; font-family: ui-monospace, monospace;
                  font-size: 11px; color: #445; white-space: pre; }

/* Trend tab: the chart fills the top pane; the readout line sits above it. */
.rv-frame > .rv-top.trend-top { display: flex; flex-direction: column; overflow: hidden; }
.trend-bar { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; padding: 3px 8px 1px; }
.trend-readout { flex: 1 1 auto; font-family: ui-monospace, monospace; font-size: 12px; font-weight: 600; color: #223; white-space: pre; overflow: hidden; }
.trend-graph { flex: 1 1 200px; min-height: 200px; }

/* The confirm dialog before a stretch is marked or unmarked as AFIB
   (assets/rhythm_range_tools.js). */
.rhythm-confirm-back { position: fixed; inset: 0; z-index: 4000; background: rgba(0, 0, 0, .18);
                       display: flex; align-items: center; justify-content: center; }
.rhythm-confirm { min-width: 380px; background: #fff; border: 1px solid #8a93a8; border-radius: 4px;
                  box-shadow: 0 6px 24px rgba(0, 0, 0, .35); font-size: 13px; }
.rhythm-confirm-title { padding: 6px 12px; background: #1b2a4e; color: #fff; font-weight: 700; border-radius: 3px 3px 0 0; }
.rhythm-confirm-body { padding: 12px 14px 6px; }
.rhythm-confirm-range { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; margin-top: 10px;
                        font-family: ui-monospace, monospace; }
.rhythm-confirm-range span { color: #667; }
.rhythm-confirm-range b { margin-right: 12px; }
.rhythm-confirm-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 14px 12px; }
.rhythm-confirm-foot button { min-width: 76px; padding: 3px 10px; border: 1px solid #9aa3b5; border-radius: 3px; background: #f3f5fa; }
.rhythm-confirm-foot .rhythm-confirm-ok { background: #ff9800; border-color: #e08600; color: #fff; font-weight: 700; }
.afib-top-range { border-left: 1px solid #0a0; border-right: 1px solid #0a0; }
.scan-drag-area { border-left: 1px solid #ff8c00; border-right: 1px solid #ff8c00; }

/* Whatever is in the strip pane, the pane above it keeps its room: a strip
   that is taller than this share of the frame scrolls inside its own pane. */
.rv-frame > .rv-bottom { max-height: 76%; overflow-y: auto; overflow-x: hidden; }

/* PageScan: the scan with its side panel (lab 2026-10-06). */
.rv-frame .ps-scan-row { flex: 1 1 140px; min-height: 110px; display: flex; gap: 6px; overflow: hidden; }
.ps-scan-row > .ps-scan { flex: 1 1 auto; min-height: 0; min-width: 0; }
.ps-side { flex: 0 0 236px; overflow-y: auto; overflow-x: hidden; padding: 2px 6px 4px;
           border: 1px solid #d9deea; border-radius: 4px; background: #f6f8fc; font-size: 11px; }
.ps-side-title { font-weight: 700; text-transform: uppercase; font-size: 10px; color: #556; margin: 5px 0 2px; }
.ps-side-checks .form-check { min-height: 0; margin-bottom: 0; }
.ps-side-checks label { font-size: 11px; }
.ps-play-row { display: flex; gap: 4px; }
.ps-play-btn { flex: 1 1 0; border: 1px solid #b9c1d3; background: #fff; border-radius: 3px; font-size: 11px; padding: 1px 0; }
.ps-play-btn:hover { background: #e9eefb; }
.ps-play-go.playing { background: #1b8a3a; border-color: #1b8a3a; color: #fff; }
.ps-speed-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-top: 3px; }
.ps-speed { width: 68px; height: 20px; font-size: 11px; padding: 0 4px; border: 1px solid #b9c1d3; border-radius: 3px; }
.ps-gauges { display: flex; gap: 4px; justify-content: space-between; }
.ps-gauge { flex: 1 1 0; text-align: center; }
.ps-gauge-name { font-weight: 700; font-size: 10px; }
.ps-gauge-bar { position: relative; height: 92px; margin: 4px 2px 2px; border: 1px solid #c8cfe0; background: #fff; }
.ps-gauge-tick { position: absolute; left: 2px; transform: translateY(50%); font-size: 8px; color: #778; line-height: 1; }
.ps-gauge-mark { position: absolute; left: 0; right: 0; height: 5px; margin-bottom: -2px; background: #ff8c00; opacity: .85; }
.ps-gauge-value { font-family: ui-monospace, monospace; font-weight: 700; font-size: 11px; }
.ps-tick-p { top: 8%; height: 84%; width: 3px; background: #39c5ff; }
.ps-hide-v .ps-tick-v, .ps-hide-s .ps-tick-s, .ps-hide-p .ps-tick-p { display: none; }
.afib-menu-sub { display: flex; justify-content: space-between; gap: 18px; padding: 5px 12px; cursor: pointer; }
.afib-menu-sub:hover, .afib-menu-sub.open { background: #eef3fb; }
.afib-menu-sub.disabled { color: #aaa; cursor: default; }
/* The side panel must show its gauges without scrolling in a 1000 px window. */
.ps-side-title { margin: 3px 0 1px; }
.ps-side-checks .form-check-inline { margin-right: 6px; }
.ps-gain { font-size: 11px; height: 22px; min-height: 0; padding: 0 22px 0 6px; }
.ps-gauge-bar { height: 62px; margin-top: 3px; }
.ps-speed-row { margin-top: 2px; }

/* The long submenu (CardiAro's whole list) stays on screen. */
.afib-submenu { max-height: calc(100vh - 16px); overflow-y: auto; }
.afib-submenu .afib-menu-item { padding: 2px 14px; font-size: 12px; }
.afib-submenu .afib-menu-head { padding: 2px 12px; }
.afib-submenu .afib-menu-sep { margin: 2px 0; }

/* Trend tab readout: the cursor time, then one small box a value. */
.trend-bar { gap: 8px; padding: 5px 10px 3px; border-bottom: 1px solid #e3e7f0; background: #f7f9fc; }
.trend-readout { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-family: inherit; white-space: normal; }
.trend-time { font-family: ui-monospace, monospace; font-weight: 700; font-size: 13px; color: #1b2a4e; margin-right: 6px; }
.trend-chip { display: inline-flex; align-items: baseline; gap: 5px; padding: 1px 8px; border: 1px solid #d5dbe8; border-radius: 10px; background: #fff; }
.trend-chip-label { font-size: 10px; color: #6b7487; text-transform: uppercase; letter-spacing: .02em; }
.trend-chip-value { font-family: ui-monospace, monospace; font-weight: 700; font-size: 12px; color: #1b2a4e; }
.trend-note { font-size: 10px; color: #8a92a3; margin-left: 6px; font-weight: 400; }
.trend-st { display: flex; align-items: center; flex: 0 0 auto; }

/* The area dragged on a strip, and the notice after a menu choice. */
.strip-drag-area { border-left: 1px solid #ff8c00; border-right: 1px solid #ff8c00; }
.rhythm-toast { position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 4500;
                max-width: 80vw; padding: 7px 16px; border-radius: 4px; background: #1b2a4e; color: #fff;
                font-size: 13px; box-shadow: 0 4px 16px rgba(0, 0, 0, .35); }
.rhythm-toast.warn { background: #8a4b00; }

/* The area dragged on a strip (CardiAro's look: over the signal only, a thin
   dark frame), with its duration on a small tab. */
.strip-drag-area { position: absolute; pointer-events: none; z-index: 4; box-sizing: border-box;
                   background: rgba(255, 170, 60, 0.26); border: 1px solid rgba(40, 30, 10, 0.85); }
.strip-drag-area.open-left { border-left: 0; }
.strip-drag-area.open-right { border-right: 0; }
.strip-drag-label { position: absolute; top: 4px; left: 50%; transform: translateX(-50%); white-space: nowrap;
                    display: flex; align-items: baseline; gap: 8px; padding: 1px 9px; border-radius: 10px;
                    background: rgba(27, 42, 78, 0.92); color: #fff; font-size: 11px;
                    box-shadow: 0 1px 4px rgba(0, 0, 0, .3); }
.strip-drag-label b { font-family: ui-monospace, monospace; font-size: 12px; }
.strip-drag-label span { opacity: .82; font-family: ui-monospace, monospace; font-size: 10px; }

/* The "General | More" panel at the right of a review strip
   (layouts/strip_tools.py, assets/strip_tools.js). */
.strip-tools-row { display: flex; align-items: stretch; gap: 6px; }
.strip-tools-main { position: relative; flex: 1 1 auto; min-width: 0; }
.strip-tools { flex: 0 0 168px; display: flex; flex-direction: column; min-height: 0; font-size: 12px;
               border: 1px solid #c8cfe0; border-radius: 3px; background: #eef1f7; overflow: hidden; }
.strip-tools-tabs { display: flex; flex: 0 0 auto; }
.strip-tools-tab { flex: 1 1 0; padding: 2px 0; font-size: 11px; color: #6b7487; background: #dfe4ee;
                   border: 0; border-bottom: 1px solid #c8cfe0; }
.strip-tools-tab + .strip-tools-tab { border-left: 1px solid #c8cfe0; }
.strip-tools-tab.active { color: #1b2a4e; font-weight: 600; background: #ffd9a8; }
.strip-tools-pane { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 6px 8px; }
.strip-tools-general { padding: 4px; }
.strip-tools-actions { display: flex; gap: 4px; margin-bottom: 4px; }
.strip-tools-btn { flex: 1 1 0; padding: 2px 0; font-size: 11.5px; color: #1b2a4e; background: #dfe4ee;
                   border: 1px solid #9aa3b5; border-radius: 3px; }
.strip-tools-btn:hover { background: #ffd9a8; }
.strip-tools-info { padding: 5px 6px; background: #fff; border: 1px solid #d8dde8;
                    font-family: ui-monospace, Consolas, monospace; font-size: 11.5px; color: #3c4658; }
.strip-tools-more, .strip-tools[data-pane="more"] .strip-tools-general { display: none; }
.strip-tools[data-pane="more"] .strip-tools-more { display: block; }
.strip-tools-check { display: flex; align-items: center; gap: 6px; margin: 0 0 4px; cursor: pointer; user-select: none; }
.strip-tools-box { flex: 0 0 13px; height: 13px; border: 1px solid #6b7487; background: #fff; font-size: 11px;
                   line-height: 11px; text-align: center; color: #1b2a4e; }
.strip-tools-check.on .strip-tools-box::before { content: "\2713"; }
.strip-info-head { margin-top: 3px; color: #1b2a4e; font-weight: 600; }
.strip-info-head:first-child { margin-top: 0; }
.strip-info-row { display: flex; gap: 4px; white-space: nowrap; }
.strip-info-row .v { margin-left: auto; color: #111; font-weight: 600; }
.strip-info-row .u { flex: 0 0 26px; color: #7c879a; }

/* "Grid" and "Display HR" switched off. The grid lines stay in the page,
   invisible: the square-grid fit measures them. */
.ecg-grid-off .strip-tools-main .gridlayer path,
.ecg-grid-off .strip-tools-main .minor-gridlayer path { stroke-opacity: 0 !important; }
.ecg-hr-off .strip-tools-main .tracerrhr { display: none; }

/* The compass (caliper) over a strip. One colour, unlike anything else on
   the strip: the traces are blue and green, the grid pink, the click cursor
   black, a VE red. Nothing of it is drawn outside the signal's area. */
.ecg-compass { position: absolute; z-index: 12; pointer-events: none; overflow: hidden; color: #b000d8; }
.ecg-compass-grab { pointer-events: auto; touch-action: none; }
.ecg-compass-leg { position: absolute; top: 0; bottom: 0; width: 9px; margin-left: -4px; cursor: ew-resize; }
.ecg-compass-leg::before, .ecg-compass-mid::before { content: ""; position: absolute; top: 0; bottom: 0; left: 4px;
                                                     border-left: 1px solid currentColor; }
.ecg-compass-knob { position: absolute; left: 0; width: 9px; height: 9px; margin-top: -4px;
                    border: 1px solid currentColor; background: rgba(255, 255, 255, .55); box-sizing: border-box; }
.ecg-compass-bar { position: absolute; height: 0; border-top: 1px solid currentColor; }
.ecg-compass-mid { position: absolute; width: 9px; margin-left: -4px; cursor: move; }
.ecg-compass-mid .ecg-compass-knob { top: 100%; margin-top: -9px; }
.ecg-compass-label { position: absolute; white-space: nowrap; font: 600 12px ui-monospace, Consolas, monospace;
                     color: #111; background: rgba(255, 255, 255, .8); padding: 0 3px; }

/* The Save and Print dialogs of the strip panel, and the print preview
   (assets/strip_tools.js). They use the frame of the range menu's confirm
   dialog (.rhythm-confirm). */
.strip-dialog .rhythm-confirm-body { display: grid; gap: 8px; font-size: 13px; }
.strip-dialog label { display: flex; align-items: center; gap: 6px; margin: 0; }
.strip-dialog .f { display: grid; gap: 2px; }
.strip-dialog .f > span { color: #667; font-size: 11.5px; }
.strip-dialog input[type="text"], .strip-dialog select { width: 100%; box-sizing: border-box; padding: 2px 5px; border: 1px solid #9aa3b5;
                                                         border-radius: 3px; }
.strip-dialog .cols { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.strip-dialog fieldset { margin: 0; padding: 4px 8px 6px; border: 1px solid #c8cfe0; border-radius: 3px;
                         display: flex; flex-wrap: wrap; gap: 4px 14px; }
.strip-dialog legend { float: none; width: auto; margin: 0; padding: 0 4px; font-size: 11.5px; color: #667; }
.strip-dialog label.off { color: #9aa3b5; }
.strip-dialog .note { color: #667; font-size: 11.5px; }
.strip-print-view { width: min(1100px, 94vw); height: 92vh; display: flex; flex-direction: column; }
.strip-print-view iframe { flex: 1 1 auto; border: 0; background: #666; }

/* The click cursor on a strip (assets/relabel_cursor.js): a little thicker than a
   grid line, so it is not lost among them (lab 2026-10-09). */
:root { --holter-cursor-width: 2px; }

/* The Tabular tab (layouts/tabular_tab.py): the table fills the top pane and
   scrolls inside it, under its header row. */
.rv-frame > .rv-top.tabular-top { overflow: hidden; padding: 4px 8px 2px; }
.tabular-top .dash-table-container, .tabular-top .dash-spreadsheet-container { height: 100%; max-height: 100%; }
.tabular-top .dash-spreadsheet-inner { max-height: 100% !important; }

/* The Strips tab (layouts/strips_tab.py): the saved strips as cards, as
   CardiAro draws them, and the buttons under them. */
.rv-frame > .rv-top.strips-top { display: flex; flex-direction: column; overflow: hidden; padding: 4px 8px 2px;
                                 background: #e3e4f0; }
.strips-cards { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: grid; align-content: start; gap: 3px;
                grid-template-columns: repeat(auto-fill, minmax(440px, 1fr)); }
.strips-card { position: relative; height: 100px; background: #fff; border: 1px solid #8f96ad; cursor: pointer;
               overflow: hidden; }
.strips-card.chosen { border: 2px solid #ff9800; }
.strips-card-pic { position: absolute; inset: 0; width: 100%; height: 100%; }
.strips-card-time, .strips-card-name, .strips-card-len { position: absolute; font: 600 12px ui-monospace, Consolas, monospace;
                                                         color: #111; background: rgba(255, 255, 255, .7); padding: 0 3px; }
.strips-card-time { top: 1px; left: 2px; }
.strips-card-tick { margin-right: 3px; font-weight: 400; }
.strips-card-name { top: 1px; right: 2px; max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strips-card-len { bottom: 1px; right: 2px; }
.strips-bar { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 4px 0 2px; }
.strips-bar .strip-tools-btn { flex: 0 0 auto; padding: 2px 14px; }
.strips-status { font-size: 12px; color: #3c4658; }

/* ── One ECG strip size on every tab (lab 2026-10-29) ─────────────────────
   "Make sure the ECG strip browser width is the same over each tab; it
   should be fixed even when scrolling." The strip's width is set by the
   WINDOW, not by the tab's container: every tab (Dashboard, Template,
   Events, AFIB, PageScan, Trend, Tabular, Strips, the beats-grid view)
   draws it at the same width, so a 15 s strip has the same paper scale
   everywhere, and nothing inside a tab can widen or narrow it (a scrollbar
   appearing, a side column, a long label). The height follows from the
   square grid and the recording's fixed lane layout, so it is the same too.
   Reserve (280 px), measured on the Dashboard, the most nested tab: the
   strip's tools panel (168 px + 6 px gap), the tab and Dashboard padding
   (16 + 24 px), the card border and padding (2 + 16 px), the strip pane's
   scrollbar (17 px), and the page's own scrollbar when a short window makes
   the page scroll (17 px), plus a few px for browser zoom. */
:root { --ecg-strip-w: max(600px, calc(100vw - 280px)); }
.strip-tools-main {
    flex: 0 0 var(--ecg-strip-w);
    width: var(--ecg-strip-w);
    max-width: var(--ecg-strip-w);
}
.strip-tools-row { overflow-x: auto; }
.ecg-fixed-w { width: var(--ecg-strip-w); max-width: var(--ecg-strip-w); }
/* A scrollbar appearing in a strip pane must not take width from the strip
   (it did on the Dashboard: the strip narrowed, the square grid made it
   shorter, the scrollbar went away, and the strip grew back). */
.rv-frame > .rv-bottom,
.rv-frame > .rv-bottom.rv-bottom-cap { scrollbar-gutter: stable; }
