/*
 * Copyright (c) 2024 Biovisuals Private Limited
 * All rights reserved.
 *
 * DataLab Phase 4C — scientist-facing workspace.
 *
 * A large centred modal workspace in the Studio visual language (warm cream chrome,
 * gold primary, warm neutral text). Deliberately restrained: clear hierarchy, no
 * decorative animation.
 */

/* ────────────────────────────────
   LEFT SIDEBAR ENTRY
   ────────────────────────────────
   The sidebar entry is wrapped in a plain <div> so `hidden` actually works:
   `.image-library button` sets `all: unset` AND `display: flex`, and an author
   `display` declaration beats the UA `[hidden] { display: none }` rule. Hiding the
   wrapper instead of the button keeps the capability gate honest without editing the
   shared sidebar stylesheet. The wrapper needs width:100% because `.image-library`
   is a centring flex column and would otherwise shrink-wrap the button. */
.image-library .dl-sidebar-entry {
  width: 100%;
}
.image-library .dl-sidebar-entry[hidden] {
  display: none;
}

/* ────────────────────────────────
   OVERLAY + MODAL SHELL
   ──────────────────────────────── */
.dl-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Sits below the 42px Studio top bar so the app chrome stays recognisable. */
  padding: 50px 16px 16px;
  box-sizing: border-box;
}
/* Author `display:flex` outranks `[hidden]`, so restore it explicitly. */
.dl-overlay[hidden] { display: none !important; }

.dl-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(31, 28, 24, 0.42);
}

.dl-modal {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 92vw;
  max-width: 1520px;
  height: 90vh;
  max-height: calc(100vh - 66px);
  background: #fff;
  border: 1px solid #E6DFD3;
  border-radius: 12px;
  box-shadow: 0 18px 48px rgba(31, 28, 24, 0.22);
  overflow: hidden;
  font-size: 13px;
  color: #3A352E;
  font-family: 'Inter', 'Open Sans', sans-serif;
}
.dl-modal:focus { outline: none; }

/* HEADER */
.dl-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  background: #FAF8F4;
  border-bottom: 1px solid #E6DFD3;
  flex: 0 0 auto;
}
.dl-heading { min-width: 0; }
.dl-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #2F2A24;
  letter-spacing: -0.01em;
}
/* The public software version: small, muted, selectable, never a badge. */
.dl-version {
  margin: 1px 0 0;
  font-size: 11px;
  line-height: 1.2;
  color: #9A9186;
  font-variant-numeric: tabular-nums;
  -webkit-user-select: text;
  user-select: text;
}
.dl-subtitle {
  margin: 2px 0 0;
  font-size: 12.5px;
  color: #7A7266;
}
.dl-head-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}
.dl-new-analysis[hidden] { display: none; }

.dl-close {
  all: unset;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  line-height: 1;
  color: #7A7266;
  border-radius: 8px;
  cursor: pointer;
}
.dl-close:hover { background: #F0E7DA; color: #2F2A24; }

/* MAIN: stepper + stage */
.dl-main {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 208px minmax(0, 1fr);
  min-height: 0;
}

/* STEPPER */
.dl-steps {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 12px;
  background: #FCFBF8;
  border-right: 1px solid #EFE9DF;
  overflow-y: auto;
}
.dl-step {
  all: unset;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: 8px;
  cursor: pointer;
  color: #5C554B;
}
.dl-step:hover:not(:disabled) { background: #F3EDE3; }
.dl-step-n {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #E8E0D2;
  color: #6B6357;
  font-size: 11.5px;
  font-weight: 600;
}
.dl-step-label { font-size: 13px; }
.dl-step.is-current { background: #fff; box-shadow: 0 1px 5px rgba(31, 28, 24, 0.07); }
.dl-step.is-current .dl-step-label { color: #B0791F; font-weight: 600; }
.dl-step.is-current .dl-step-n { background: #C08A3C; color: #fff; }
.dl-step.is-locked { opacity: 0.45; cursor: not-allowed; }

/* STAGE — the ONLY vertically scrolling region */
.dl-stage {
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 20px 24px 24px;
  background: #fff;
}

/* FOOTER */
.dl-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 20px;
  background: #FAF8F4;
  border-top: 1px solid #E6DFD3;
}
.dl-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.dl-status { color: #6B6357; font-size: 12.5px; min-width: 0; }
.dl-status.is-error { color: #A3421B; font-weight: 500; }
.dl-modal[data-dl-busy="true"] .dl-status::after { content: " \2026"; opacity: 0.7; }
.dl-overlay[data-dl-busy="true"] .dl-stage { opacity: 0.72; pointer-events: none; }

/* ────────────────────────────────
   CARDS + TYPOGRAPHY
   ──────────────────────────────── */
.dl-card {
  background: #fff;
  border: 1px solid #EFE9DF;
  border-radius: 10px;
  padding: 16px 18px;
  margin-bottom: 16px;
}
.dl-card:last-child { margin-bottom: 0; }
.dl-card-title { margin: 0; font-size: 14.5px; font-weight: 600; color: #2F2A24; }
.dl-card-sub { margin: 3px 0 0; font-size: 12.5px; color: #7A7266; }
.dl-card-title + .dl-modes,
.dl-card-sub + .dl-modes { margin-top: 12px; }

.dl-hint { margin: 8px 0 0; font-size: 12px; color: #8A8275; }
.dl-meta { margin: 8px 0 0; color: #7A7266; font-size: 12px; }
.dl-warn {
  margin: 8px 0 0;
  color: #7A5410;
  background: #FDF6E7;
  border: 1px solid #F1E2C0;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 12.5px;
}

/* ────────────────────────────────
   CONTROLS
   ──────────────────────────────── */
.dl-field { display: flex; flex-direction: column; gap: 5px; margin: 12px 0 0; }
.dl-field > label { font-size: 12px; color: #6B6357; font-weight: 500; }

/* PHASE 6K-5A §2 — THE ROOT CAUSE OF "every cell looks like its own rounded text input".
 *
 * This generic rule matched the grid's cell and header inputs too, and it OUT-SPECIFIES the
 * rules written to style them: `.dl-modal input[type="text"]` scores (0,2,1) while
 * `.dl-grid .dl-c` and `.dl-grid .dl-h` score (0,2,0). So the `border: 0`,
 * `background: transparent` and tighter padding those rules ask for were being discarded,
 * and every cell drew its own 1px box with a 6px radius and a white fill inside the
 * collapsed table border — a grid of separate inputs rather than a spreadsheet.
 *
 * The fix is to stop matching grid inputs at all. `:not(:where(...))` is used rather than a
 * plain `:not(...)` because `:where()` contributes ZERO specificity: this selector still
 * scores exactly (0,2,1), so nothing else in the panel shifts in the cascade — the only
 * change is that two classes of input now fall through to the rules already written for
 * them. Those rules then apply as their authors intended, which also means `.is-invalid`,
 * `.is-duplicate`, `.is-selected` and the focus ring keep working with no restatement here. */
.dl-modal input[type="text"]:not(:where(.dl-c, .dl-h)),
.dl-modal textarea,
.dl-modal select {
  border: 1px solid #DED6C8;
  border-radius: 6px;
  padding: 7px 9px;
  font: inherit;
  color: inherit;
  background: #fff;
  box-sizing: border-box;
  max-width: 100%;
}
/* PHASE 6K-5A §2 — grid cells are excluded here for the SAME reason as the rule above, and
 * by the same zero-specificity mechanism. `outline-offset: 1px` draws the ring OUTSIDE the
 * element, which is right for a standalone form control and wrong for a cell sitting flush
 * against its neighbours: it overlapped them, and the focused cell read as smudged rather
 * than as clearly active. The grid's own `:focus` rules (see the MANUAL GRID block above
 * and the 6K-5A block at the end of this file) draw it INSIDE instead. */
.dl-modal input[type="text"]:not(:where(.dl-c, .dl-h)):focus-visible,
.dl-modal textarea:focus-visible,
.dl-modal select:focus-visible,
.dl-modal button:focus-visible {
  outline: 2px solid #C08A3C;
  outline-offset: 1px;
}

.dl-paste {
  width: 100%;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 12.5px;
  resize: vertical;
  min-height: 200px;
}

.dl-modes, .dl-errbars { display: flex; gap: 6px; flex-wrap: wrap; }

.dl-tab {
  all: unset;
  border: 1px solid #DED6C8;
  background: #fff;
  padding: 6px 14px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12.5px;
  color: #5C554B;
}
.dl-tab:hover { background: #FAF5EC; }
.dl-tab.is-active {
  background: #C08A3C;
  border-color: #C08A3C;
  color: #fff;
  font-weight: 500;
}

.dl-primary {
  all: unset;
  background: #C08A3C;
  color: #fff;
  padding: 9px 20px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
}
.dl-primary:hover:not(:disabled) { background: #A87528; }
.dl-primary:disabled { background: #DCD3C3; color: #F7F4EF; cursor: not-allowed; }
/* Async-loading feedback (busyAction.js): an IN-FLIGHT action is disabled but must not
   read as "unavailable", so it keeps the primary hue (the existing hover shade, for
   label contrast) with a progress cursor. The spinner is the Studio Font Awesome one. */
.dl-primary[aria-busy="true"]:disabled { background: #A87528; color: #fff; cursor: progress; }
.dl-btn-spinner { margin-right: 6px; }

.dl-ghost {
  all: unset;
  border: 1px solid #DED6C8;
  background: #fff;
  padding: 7px 14px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12.5px;
  color: #5C554B;
}
.dl-ghost:hover { background: #FAF5EC; }
.dl-ghost.is-active { border-color: #C08A3C; color: #A87528; font-weight: 600; }
/* Clear discards everything the scientist typed, so it is set apart from the two
   additive structure controls and warms up on hover instead of staying neutral. */
.dl-ghost-danger { margin-left: 10px; }
.dl-ghost-danger:hover { background: #FDF1EC; border-color: #E3B7A6; color: #A3421B; }

/* ── PHASE 6B UI POLISH: "Your data" card header ──────────────────────────────
   A flex ROW, not absolute positioning: the upload action sits opposite the heading at
   desktop width and wraps cleanly beneath it when the panel narrows, so it can never
   overlap the table, the scrollbar, the DataLab close control or the step nav. */
.dl-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.dl-card-head-text { flex: 1 1 260px; min-width: 0; }
.dl-card-head-actions { flex: 0 0 auto; }

/* A meaningful SECONDARY action: clearly clickable and icon-led, but deliberately not
   competing with the stage's primary CTA (Validate data). */
.dl-upload-btn {
  all: unset;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1px solid #DED6C8;
  background: #fff;
  padding: 8px 14px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12.5px;
  color: #5C554B;
  white-space: nowrap;
}
.dl-upload-btn:hover { background: #FAF5EC; border-color: #C9BFAD; }
.dl-upload-btn:focus-visible { outline: 2px solid #C08A3C; outline-offset: 2px; }
.dl-upload-btn i { font-size: 12px; color: #A87528; }

.dl-check { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: 12.5px; color: #5C554B; }
.dl-check input { margin: 0; accent-color: #C08A3C; }

/* ────────────────────────────────
   MANUAL GRID (spreadsheet-like)
   ────────────────────────────────
   `table-layout: fixed` + `width: 100%` is what keeps a two-column dataset from
   producing a horizontal scrollbar: cells share the available width instead of being
   sized by their content. */
.dl-grid-wrap { margin-top: 12px; max-width: 100%; }
/* G1: reserve the `.dl-grid-edge-col` pill's intentional 10px overhang so it cannot become
   scrollable overflow on this wrap or on `#dl-stage`. Create grid only — the linked table
   reuses `.dl-grid-wrap` and renders no edge pills. See `buildGridSection` for the reasoning. */
.dl-grid-wrap--edged { padding-right: 10px; }

.dl-grid {
  width: 100%;
  max-width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  border: 1px solid #E6DFD3;
  border-radius: 6px;
}
.dl-grid th, .dl-grid td {
  border: 1px solid #EFE9DF;
  padding: 0;
  text-align: left;
  vertical-align: middle;
}
.dl-grid thead th {
  background: #FAF8F4;
  padding: 4px 6px;
  font-size: 12px;
  font-weight: 600;
  color: #5C554B;
}
.dl-grid thead th.dl-rownum-h { width: 44px; text-align: center; color: #9A9184; }
.dl-grid thead th.dl-rowact-h { width: 34px; }

.dl-grid .dl-h {
  width: 100%;
  min-width: 0;
  border: 1px solid transparent;
  background: transparent;
  padding: 4px 6px;
  font-weight: 600;
  font-size: 12.5px;
}
.dl-grid .dl-h:hover { border-color: #DED6C8; background: #fff; }

.dl-grid td.dl-rownum {
  width: 44px;
  text-align: center;
  background: #FCFBF8;
  color: #9A9184;
  font-size: 11.5px;
  padding: 0 4px;
  user-select: none;
}
.dl-grid .dl-c {
  width: 100%;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 6px 8px;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  box-sizing: border-box;
}
.dl-grid .dl-c:focus { outline: 2px solid #C08A3C; outline-offset: -2px; background: #FFFDF8; }

.dl-grid td.dl-rowact { width: 34px; text-align: center; }
.dl-rowremove {
  all: unset;
  width: 20px;
  height: 20px;
  line-height: 1;
  border-radius: 4px;
  color: #B3AA9B;
  cursor: pointer;
  text-align: center;
}
.dl-rowremove:hover { background: #F6E7E2; color: #A3421B; }

.dl-grid-bar { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.dl-grid-count { font-size: 12px; color: #8A8275; margin-left: auto; }

/* ── PHASE 6A: unified editable table ──────────────────────────────────────────
   The column controls live INSIDE the existing header cell, so the header row keeps
   the same cell count as every data row and `table-layout: fixed` still shares the
   width evenly. `min-width: 0` on the name input is what stops the extra button from
   forcing a horizontal scrollbar. */
.dl-colhead { display: flex; align-items: center; gap: 2px; }
.dl-colhead .dl-h { flex: 1 1 auto; min-width: 0; }
.dl-colremove {
  all: unset;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  line-height: 1;
  border-radius: 4px;
  color: #B3AA9B;
  cursor: pointer;
  text-align: center;
  font-size: 12px;
}
.dl-colremove:hover:not(:disabled) { background: #F6E7E2; color: #A3421B; }
.dl-colremove:disabled { opacity: 0.3; cursor: not-allowed; }

/* An invalid cell is MARKED, never emptied or corrected. */
.dl-grid .dl-c.is-invalid {
  background: #FDF1EC;
  color: #A3421B;
  box-shadow: inset 0 0 0 1px #E3B7A6;
}

/* PHASE 6G PRE-CORRECTION A: the FIRST invalid cell — "start here".
   A stronger ring in the same warm-red family as .is-invalid, so it reads as the same
   problem rather than a new kind of one. Deliberately not a colour change: the cell must
   still look invalid, because it is. Only ever one cell at a time, and it is dropped the
   moment the value becomes a number (see markCell). No animation — a pulsing cell in a
   dense table is noise, and it would fight the focus outline that lands on the same
   element. */
.dl-grid .dl-c.is-invalid.is-flagged {
  box-shadow: inset 0 0 0 2px #A3421B;
}

/* PHASE 6B: a duplicated column NAME is marked in the same visual language as an
   invalid cell — same warm-red family, so "something here needs fixing" reads the same
   way in the header row as it does in the body. The label itself is never rewritten. */
.dl-grid .dl-h.is-duplicate {
  background: #FDF1EC;
  color: #A3421B;
  box-shadow: inset 0 0 0 1px #E3B7A6;
}
.dl-grid .dl-h.is-duplicate:hover { border-color: #E3B7A6; background: #FDF1EC; }

/* Per-group readout: how many values each column actually holds. */
.dl-colstats { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.dl-colstat {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 3px 8px;
  border: 1px solid #EFE9DF;
  border-radius: 999px;
  background: #FCFBF8;
  font-size: 11.5px;
}
.dl-colstat-k { font-weight: 600; color: #5C554B; }
.dl-colstat-v { color: #8A8275; }
.dl-colstat.is-error { border-color: #E3B7A6; background: #FDF1EC; }
.dl-colstat.is-error .dl-colstat-k, .dl-colstat.is-error .dl-colstat-v { color: #A3421B; }

.dl-grid-notice, .dl-grid-problem { margin: 8px 0 0; font-size: 12px; color: #7A7266; }
.dl-grid-notice.is-error, .dl-grid-problem.is-error { color: #A3421B; }

/* The text fallback is reachable but deliberately not a peer of the table. */
.dl-alt { margin: 10px 0 0; font-size: 12px; color: #8A8275; }
.dl-link {
  all: unset;
  color: #B0791F;
  cursor: pointer;
  text-decoration: underline;
  font-size: inherit;
}
.dl-link:hover { color: #8E5F14; }
.dl-link:focus-visible { outline: 2px solid #C08A3C; outline-offset: 2px; border-radius: 2px; }
.dl-pastewrap { margin-top: 4px; }
.dl-pastewrap .dl-field { margin-top: 10px; }

/* ── PHASE 6B: import preview ─────────────────────────────────────────────────
   A modal OVER the Data stage — deliberately not styled like the live grid, so it
   can never be mistaken for the table it is about to replace. Higher z-index than
   the workspace overlay itself so it always sits above the panel it belongs to. */
.dl-import-backdrop {
  position: fixed;
  inset: 0;
  z-index: 2100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(47, 42, 36, 0.45);
  padding: 24px;
  box-sizing: border-box;
}
.dl-import-modal {
  background: #FFFDF9;
  border-radius: 10px;
  box-shadow: 0 20px 60px rgba(31, 28, 24, 0.28);
  padding: 20px 22px;
  max-width: 560px;
  width: 100%;
  max-height: 80vh;
  overflow: auto;
}
.dl-import-stats { display: flex; gap: 8px; margin-top: 10px; }
.dl-import-sample {
  width: 100%;
  margin-top: 12px;
  border-collapse: collapse;
  font-size: 12px;
  table-layout: fixed;
}
.dl-import-sample th, .dl-import-sample td {
  border: 1px solid #EFE9DF;
  padding: 4px 6px;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dl-import-sample thead th { background: #FAF8F4; font-weight: 600; color: #5C554B; }
/* PHASE 6B: the server's canonical import confirmations (error cells / uncached
   formulas). Advisory tone, deliberately NOT the error tone — these cells were read
   as missing by design, they are not a failure. */
.dl-import-confirmations {
  margin-top: 12px;
  padding: 8px 10px;
  border: 1px solid #EADFC8;
  border-left: 3px solid #C08A3C;
  border-radius: 4px;
  background: #FDFAF4;
  font-size: 12.5px;
  color: #5C554B;
}
.dl-import-confirmations .dl-card-sub { margin: 0 0 4px; font-weight: 600; }
.dl-import-confirmations ul { margin: 0; padding-left: 18px; }
.dl-import-confirmations li { margin: 2px 0; }

.dl-import-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

/* ────────────────────────────────
   VALIDATION SUMMARY
   ──────────────────────────────── */
.dl-summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin-top: 14px;
}
.dl-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: #FCFBF8;
  border: 1px solid #EFE9DF;
  border-radius: 8px;
  padding: 10px 12px;
}
.dl-stat-v { font-size: 18px; font-weight: 600; color: #2F2A24; }
.dl-stat-k { font-size: 11.5px; color: #8A8275; }

.dl-confirm {
  background: #FCFBF8;
  border: 1px solid #E6DFD3;
  border-radius: 8px;
  padding: 12px;
  margin-top: 12px;
}
.dl-confirm-q { margin: 0 0 8px; font-size: 12.5px; color: #5C554B; }

.dl-advanced { margin-top: 14px; }
.dl-advanced > summary {
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 500;
  color: #6B6357;
  padding: 4px 0;
}
.dl-advanced > summary:focus-visible { outline: 2px solid #C08A3C; outline-offset: 2px; }

.dl-cols, .dl-stats {
  width: 100%;
  border-collapse: collapse;
  margin-top: 8px;
  font-size: 12.5px;
}
.dl-cols th, .dl-cols td, .dl-stats th, .dl-stats td {
  border: 1px solid #EFE9DF;
  padding: 6px 8px;
  text-align: left;
}
.dl-cols th, .dl-stats th {
  background: #FAF8F4;
  font-weight: 600;
  font-size: 12px;
  color: #5C554B;
}
.dl-stats td { font-variant-numeric: tabular-nums; }
.dl-role { padding: 4px 6px; font-size: 12px; }

/* ────────────────────────────────
   ANALYSIS STAGE
   ──────────────────────────────── */
.dl-grouprow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
  margin-top: 12px;
}
.dl-groupbox {
  background: #FCFBF8;
  border: 1px solid #EFE9DF;
  border-radius: 8px;
  padding: 12px 14px;
}
.dl-groupbox-t { margin: 0; font-size: 12.5px; font-weight: 600; color: #5C554B; }
.dl-groupbox .dl-field { margin-top: 10px; }
.dl-groupbox select, .dl-groupbox input[type="text"] { width: 100%; }

.dl-methods { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.dl-method {
  all: unset;
  display: flex;
  flex-direction: column;
  gap: 3px;
  border: 1px solid #DED6C8;
  border-radius: 8px;
  padding: 12px 14px;
  cursor: pointer;
}
.dl-method.is-active { border-color: #C08A3C; background: #FFFCF5; }
.dl-method-n { font-size: 13px; font-weight: 600; color: #2F2A24; }
.dl-method-d { font-size: 12px; color: #7A7266; }

/* ────────────────────────────────
   RESULTS + FIGURE
   ──────────────────────────────── */
.dl-welch {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 20px;
  margin: 12px 0 0;
}
.dl-welch dt { color: #7A7266; font-size: 12px; }
.dl-welch dd { margin: 0; font-variant-numeric: tabular-nums; font-size: 12.5px; color: #2F2A24; }

.dl-figure {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(260px, 1fr);
  gap: 16px;
  align-items: start;
}
.dl-figure > .dl-card { margin-bottom: 0; }

/* ────────────────────────────────
   FIGURE STAGE — PINNED PREVIEW SPLIT (Phase 5B-1 correction)
   The preview pane stays on screen while the controls pane scrolls internally, the
   same shape the reopen editor uses. Keyed on `.dl-stage--figure`, which
   renderFigureStage adds and render() clears, so no other stage is affected.
   ──────────────────────────────── */
.dl-stage.dl-stage--figure { overflow: hidden; }
/* `grid-template-rows: minmax(0, 1fr)` is what BOUNDS the row to the stage height:
   without it the implicit row grows to content and both columns overflow the stage
   instead of the controls column scrolling internally. `minmax(0, …)` (not `1fr`
   alone) lets the children actually shrink below their content height. */
.dl-stage--figure .dl-figure {
  height: 100%;
  grid-template-rows: minmax(0, 1fr);
  align-items: stretch;
}
/* Preview column hugs the top and does NOT scroll away with the controls. */
.dl-figure-preview { min-width: 0; min-height: 0; align-self: start; }
/* Controls column is the ONLY scrolling region on this stage. */
.dl-figure-controls {
  min-width: 0;
  min-height: 0;
  align-self: stretch;
  overflow-y: auto;
  overflow-x: hidden;
  padding-right: 4px;
}
.dl-figure-controls > .dl-card { margin-bottom: 0; }

/*
 * THE PREVIEW FRAME TRACKS THE CURRENT FIGURE, NEVER THE PANE OR A PREVIOUS RENDER.
 *
 * Owner browser acceptance (item C) reported the preview "retaining stale proportions,
 * leaving awkward empty space" after a large layout change. The audit found no stale
 * STATE — nothing caches a preview size, every repaint is `host.innerHTML = <current
 * svg>`, and the sanitizer re-asserts `viewBox`/`width`/`height` from the spec — so the
 * whole sizing chain is this CSS, and it had two defects:
 *
 *   1. `max-width: 100%` with no height bound: shrink-only in ONE axis. A figure taller
 *      than the pane was clipped by the stage's `overflow: hidden` with no scrollbar.
 *   2. (see `.dl-split-preview .dl-preview` below) the bordered box STRETCHED to the
 *      pane, so the frame kept the pane's proportions while the figure inside changed
 *      shape — which is exactly what reads as a stale preview.
 *
 * The box now HUGS the current figure in both axes and the figure is bounded in both
 * axes, so the frame's aspect always matches the figure actually on screen. `62vh` is a
 * deterministic cap (no JS, no measurement, no viewport branching in code) that stops a
 * tall figure from being clipped; it never upscales, so the preview never implies a
 * figure size the scientist did not author. `overflow: hidden` is the belt: nothing can
 * paint outside the frame even if a future renderer emits an oversized root.
 */
.dl-preview {
  margin-top: 12px;
  padding: 10px;
  border: 1px solid #EFE9DF;
  border-radius: 8px;
  background: #FCFBF8;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
/*
 * The floor belongs to the EMPTY state only. As a permanent `min-height` it was the third way
 * the frame could stop matching the figure: a short/landscape figure (e.g. 820×300) sat in a
 * 240px-tall box with dead space above and below. Both preview hosts are emptied with
 * `innerHTML = ''` when there is nothing to draw, so `:empty` is exactly the placeholder case.
 */
.dl-preview:empty { min-height: 200px; }
.dl-preview svg {
  max-width: 100%;
  max-height: 62vh;
  width: auto;
  height: auto;
  display: block;
}

/* ────────────────────────────────
   REOPEN EDITOR — PERSISTENT SPLIT
   Left: the figure, always on screen. Right: Data | Analysis | Presentation.
   Each pane scrolls INTERNALLY, so ordinary editing needs no page-level scrolling.

   PHASE 5B-2 UI-CONSISTENCY CORRECTION — the panes used to be the other way round,
   which made the reopened editor a mirror image of the create workflow's Figure
   stage. Both surfaces are now preview-left / tools-right.
   ──────────────────────────────── */
/*
 * The preview column is sized by what the FIGURE actually needs, not by a percentage:
 * 560px is the chart's intrinsic width, so it never benefits from more, and 320px is
 * the point below which the plot stops being readable. Everything left over goes to
 * the editor. `minmax(0, 1fr)` on the editor column is what prevents a wide table
 * from forcing horizontal overflow.
 */
.dl-split {
  display: grid;
  grid-template-columns: minmax(320px, 560px) minmax(0, 1fr);
  gap: 20px;
  align-items: stretch;
  min-height: 0;
  height: 100%;
}
.dl-split-preview,
.dl-split-editor { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.dl-editor-head { flex: 0 0 auto; display: flex; flex-direction: column; gap: 10px; }

.dl-tabs {
  display: flex;
  gap: 4px;
  padding: 3px;
  background: #F4F0E8;
  border: 1px solid #E6DFD3;
  border-radius: 9px;
}
.dl-tab {
  flex: 1 1 0;
  padding: 7px 12px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: #6B6357;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}
.dl-tab:hover:not(:disabled) { color: #3F3A32; }
.dl-tab.is-active {
  background: #fff;
  color: #B0791F;
  font-weight: 600;
  box-shadow: 0 1px 5px rgba(31, 28, 24, 0.07);
}
.dl-tab:disabled { opacity: 0.5; cursor: not-allowed; }

/* The pending-change line. Always present, so a mixed edit is visible from any tab. */
.dl-summary {
  margin: 0;
  padding: 7px 10px;
  border-radius: 7px;
  background: #FAF8F4;
  border: 1px solid #EFE9DF;
  color: #6B6357;
  font-size: 12.5px;
  line-height: 1.45;
}
.dl-summary.is-scientific { background: #FDF6E7; border-color: #EBDCBC; color: #8A6317; }

.dl-notices { display: flex; flex-direction: column; gap: 6px; }
.dl-notices p { margin: 0; }

/* The active tab's controls — the only scrolling region on the left. */
.dl-tabpanel {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  margin-top: 12px;
  padding-right: 4px;
}
.dl-tabpanel > .dl-card:first-child { margin-top: 0; }

.dl-data-edit {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid #EFE9DF;
}
.dl-data-edit .dl-hint { margin: 0; flex: 1 1 auto; min-width: 0; }

/* The preview pane stays put while the editor pane scrolls. */
.dl-split-preview { position: sticky; top: 0; }
.dl-previewpane-head { flex: 0 0 auto; }
.dl-previewpane-head .dl-card-title { margin: 0 0 2px; }
/*
 * `flex: 0 1 auto` (was `1 1 auto`) — the frame no longer GROWS to the pane's height.
 *
 * Stretching is what made the reopen editor's preview keep the previous proportions: the
 * bordered box took the pane's height whatever the figure's aspect ratio was, so after a
 * dimension/preset change the figure moved inside a frame that had not. The box may still
 * SHRINK (`1`), which is what keeps a tall figure inside a short pane. The height floor applies
 * to the EMPTY placeholder only, for the same reason as `.dl-preview:empty` above.
 */
.dl-split-preview .dl-preview { flex: 0 1 auto; margin-top: 10px; }
.dl-split-preview .dl-preview:empty { min-height: 240px; }

/* When the reopen editor is showing, the stage itself must not scroll — the panes do. */
.dl-overlay[data-dl-mode="linked-chart-readonly"] .dl-stage { overflow: hidden; }

/*
 * RECLAIM THE CREATE-WORKFLOW STEPPER COLUMN.
 *
 * `.dl-main` is a two-column grid whose first column (208px) belongs to the
 * create-workflow stepper. Linked-chart editing has no stepper, and `render()`
 * already empties `#dl-steps` — but emptying a node does not release layout: the grid
 * column, the nav's 12px side padding and its 1px right border all still occupied
 * space. That was the large empty gutter down the far left of the editor.
 *
 * Collapsing the column here (rather than nudging content with negative margins) is
 * what actually gives the width back, so the editor and the figure start at the normal
 * modal content edge. `.dl-steps` also needs `display: none` explicitly, because its
 * own `display: flex` would otherwise beat the `[hidden]` attribute.
 */
.dl-overlay[data-dl-mode="linked-chart-readonly"] .dl-main { grid-template-columns: minmax(0, 1fr); }
.dl-overlay[data-dl-mode="linked-chart-readonly"] .dl-steps { display: none; }

/* ────────────────────────────────
   NARROW / SHORT DESKTOP FALLBACK
   ──────────────────────────────── */
@media (max-width: 1100px) {
  .dl-main { grid-template-columns: 168px minmax(0, 1fr); }
  .dl-figure { grid-template-columns: minmax(0, 1fr); }
  .dl-split { grid-template-columns: minmax(280px, 38%) minmax(0, 1fr); gap: 14px; }
  /* Panes stack: the preview moves above the controls and the stage scrolls again,
     so nothing is cut off on a narrow window and there is no independent-scroll fight. */
  .dl-stage.dl-stage--figure { overflow-y: auto; }
  .dl-stage--figure .dl-figure { height: auto; align-items: start; }
  .dl-figure-preview { position: static; }
  .dl-figure-controls { overflow: visible; max-height: none; }
  /* PHASE 5B-3 PASS 2: paired rows unstack at the SAME breakpoint the panes do. Below
     it the controls pane is too narrow for two columns, so each field takes the full
     width again rather than being squeezed into an unusable half. No control is hidden
     or removed — only the row direction changes. */
  .dl-fieldrow { flex-direction: column; gap: 0; }
  .dl-fieldrow > .dl-field { flex: 0 0 auto; }
}
/* Below the split's usable width the preview moves above the controls and the stage
   scrolls again, so nothing is ever cut off on a narrow window. */
@media (max-width: 900px) {
  .dl-split { grid-template-columns: minmax(0, 1fr); height: auto; }
  /* The preview is already FIRST in the DOM, so stacking puts it above the controls
     without an `order` override — which is what the old right-hand layout needed. */
  .dl-split-preview { position: static; }
  .dl-tabpanel { overflow: visible; }
  .dl-overlay[data-dl-mode="linked-chart-readonly"] .dl-stage { overflow-y: auto; }
}
@media (max-width: 860px) {
  .dl-modal { width: 96vw; height: 94vh; }
  .dl-main { grid-template-columns: minmax(0, 1fr); }
  .dl-steps {
    flex-direction: row;
    overflow-x: auto;
    border-right: 0;
    border-bottom: 1px solid #EFE9DF;
    padding: 10px 12px;
  }
  .dl-step-label { white-space: nowrap; }
}

@media (prefers-reduced-motion: reduce) {
  .dl-overlay, .dl-overlay * { transition: none !important; animation: none !important; }
}

/* ── Phase 5B-1 publication-layout property groups ──────────────────────────
 * Collapsible groups INSIDE the existing Presentation card. Collapsed by default so
 * the panel does not grow a long scroll for someone who only wants a title and error
 * bars, and the panel body scrolls internally when a group is opened — the Figure
 * preview keeps its persistent position either way.
 * ─────────────────────────────────────────────────────────────────────────── */
.dl-propgroup {
  margin: 14px 0 0;
  border: 1px solid #EFE9DF;
  border-radius: 8px;
  background: #FDFBF7;
}
.dl-propgroup + .dl-propgroup { margin-top: 10px; }
.dl-propgroup-summary {
  list-style: none;
  cursor: pointer;
  padding: 9px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: #3F3A32;
  border-radius: 8px;
  user-select: none;
}
.dl-propgroup-summary::-webkit-details-marker { display: none; }
.dl-propgroup-summary::before {
  content: '\25B8';
  display: inline-block;
  width: 12px;
  color: #A79C8C;
  transition: transform 120ms ease;
}
.dl-propgroup[open] > .dl-propgroup-summary::before { transform: rotate(90deg); }
.dl-propgroup-summary:hover { background: #FAF5EC; }
.dl-propgroup-summary:focus-visible { outline: 2px solid #C9A227; outline-offset: 1px; }
.dl-propgroup > *:not(summary) { margin-left: 12px; margin-right: 12px; }
.dl-propgroup > *:last-child { margin-bottom: 12px; }

.dl-num {
  width: 100%;
  max-width: 160px;
  box-sizing: border-box;
  padding: 7px 9px;
  font-size: 13px;
  color: #2F2A24;
  background: #fff;
  border: 1px solid #E3DBCE;
  border-radius: 6px;
}
.dl-num:focus-visible { outline: 2px solid #C9A227; outline-offset: 1px; border-color: #C9A227; }

/* ────────────────────────────────
   PHASE 5B-3 PASS 2 — PAIRED FIELD ROWS + PRESET BAR
   ────────────────────────────────
   Compaction is layout only. Each half is still a complete `.dl-field` with its own
   visible label, `label[for]` association and stable id, so nothing about
   accessibility, keyboard order or the browser suite's selectors changes — the panel is
   shorter purely because naturally paired controls stop each claiming a full row. */
.dl-fieldrow { display: flex; gap: 12px; align-items: flex-start; }
.dl-fieldrow > .dl-field { flex: 1 1 0; min-width: 0; }
/* Let a paired number input use its half-column instead of the 160px solo cap. */
.dl-fieldrow > .dl-field .dl-num { max-width: 100%; }
/* Colour swatches stay a sensible size rather than stretching across half the row. */
.dl-fieldrow > .dl-field input[type="color"] { width: 100%; max-width: 72px; }

/* Phase 6 usability follow-up — per-role text sizes (Linked / Custom). One compact row per role,
   inside the existing Typography group: name · state · size · Link. No nested accordion. */
.dl-typoroles { display: grid; gap: 6px; margin-top: 10px; }
.dl-typorole {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 58px 72px 44px;
  align-items: center;
  gap: 8px;
}
.dl-typorole-name { font-size: 12px; color: #4A443B; }
.dl-typorole-status { font-size: 11px; color: #8A8275; }
.dl-typorole[data-dl-text-role-state="custom"] .dl-typorole-status { color: #1F5FA8; font-weight: 600; }
.dl-typorole .dl-num { max-width: 100%; }
.dl-typorole-link {
  font-size: 11px;
  padding: 3px 6px;
  border: 1px solid #D9D2C5;
  border-radius: 4px;
  background: #FFFFFF;
  color: #1F5FA8;
  cursor: pointer;
}
.dl-typorole-link:focus-visible { outline: 2px solid #1F5FA8; outline-offset: 1px; }

/* Presets + Reset. A row of ACTIONS, so nothing is ever rendered as "selected" —
   applying a preset writes values and is then forgotten. */
.dl-presetbar { display: flex; flex-wrap: wrap; gap: 6px; }
.dl-preset { font-size: 12px; padding: 5px 11px; }
/* Reset is separated from the presets: it is a different kind of action. */
.dl-preset.dl-reset { margin-left: auto; }

.dl-spacingrow { display: flex; align-items: center; gap: 10px; }
.dl-range { flex: 1 1 auto; min-width: 0; accent-color: #C9A227; }
.dl-range:focus-visible { outline: 2px solid #C9A227; outline-offset: 2px; }
.dl-readout {
  flex: 0 0 auto;
  min-width: 38px;
  text-align: right;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: #6B6357;
}

/* ── PHASE 6B ACCEPTANCE CORRECTION: canvas floating toolbar suppression ───────
   The Canvas floating object toolbar is `z-index: 99999` and its preset panel is
   `100000`, both far above the DataLab overlay (2000) and its import modal (2100), so
   a selected object's toolbar floated ON TOP of the workspace.

   Fixed semantically: while the DataLab workspace owns the screen, a canvas-editing
   affordance is not applicable, so `body.datalab-open` suppresses it. The toolbar's own
   z-index and visibility logic are UNCHANGED — remove the class and normal behaviour
   resumes exactly. `!important` is required because `.shape-toolbar-visible` sets
   `opacity`/`pointer-events` with `!important` too; these selectors are more specific,
   so they win. Nothing else (global nav, the canvas, the Fabric selection) is touched. */
/* NOTE: the floating style inspector (`.bv-inspector`) needs the same suppression for the same
   reason, but its rule lives in `portraitStyleInspector.css` alongside its presentation-mode
   suppression — the inspector's own stylesheet owns the inspector's own visibility. */
body.datalab-open #shapeFloatingToolbar,
body.datalab-open #tb-preset-panel {
  opacity: 0 !important;
  pointer-events: none !important;
  visibility: hidden !important;
}

/* ── PHASE 6B ACCEPTANCE CORRECTION: column selection for wide sources ───────── */
.dl-colsel { margin-top: 12px; }
.dl-colsel-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 6px;
}
.dl-colsel-count { font-size: 12.5px; font-weight: 600; color: #5C554B; }
.dl-colsel-count.is-full { color: #A87528; }
.dl-colsel-search {
  width: 100%; box-sizing: border-box; padding: 6px 8px; margin-bottom: 8px;
  border: 1px solid #EADFC8; border-radius: 4px; font-size: 13px; color: #3D3833;
}
.dl-colsel-search:focus { outline: 2px solid #C08A3C; outline-offset: -1px; }
.dl-colsel-list {
  max-height: 240px; overflow: auto;
  border: 1px solid #EFE9DF; border-radius: 4px; background: #fff;
}
.dl-colsel-item {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; font-size: 13px; color: #3D3833;
  border-bottom: 1px solid #F6F2EA; cursor: pointer;
}
.dl-colsel-item:last-child { border-bottom: none; }
.dl-colsel-item:hover { background: #FDFAF4; }
.dl-colsel-item input { margin: 0; accent-color: #C08A3C; }
.dl-colsel-item.is-disabled { opacity: .5; cursor: not-allowed; }
/* Source position — what distinguishes two columns that share a name. Pushed to the
   right so the label column stays scannable; the type chip follows it. */
.dl-colsel-pos { margin-left: auto; font-size: 11.5px; color: #A79E93; white-space: nowrap; }
.dl-colsel-type { font-size: 11.5px; color: #8A8178; min-width: 62px; text-align: right; }
.dl-colsel-empty { padding: 10px; font-size: 12.5px; color: #8A8178; }

/* ── PHASE 6B ACCEPTANCE CORRECTION: rectangular range selection ──────────────
   Cell classes only — no overlay elements, so selection costs nothing structurally and
   can never alter a cell's value. Invalid/blank cell styling stays recognizable because
   those rules colour the BORDER and background tint, which are layered separately. */
/*
 * PHASE 6C-3 UI CORRECTION — the LINKED table is styled by the same rules.
 *
 * These rules were scoped to `#dl-grid` alone, but Phase 6C-3 added a second grid
 * surface (`#dl-linked-grid`) that drives the SAME selection engine through the
 * `gridSurface('linked')` adapter. The range was therefore computed correctly and
 * `is-selected` / `is-range-active` / `is-range-dragging` were toggled correctly on the
 * linked grid, while NO rule matched them — so a linked range was invisible and,
 * because `user-select` was never suppressed, the drag read as a native text sweep
 * inside the pressed cell rather than as a range gesture.
 *
 * Widening the selectors is the whole fix: there is ONE selection engine and now one
 * set of selection styles. Deliberately NOT a linked-only rule block, which would let
 * the two surfaces drift apart visually.
 */
#dl-grid input.dl-c.is-selected,
#dl-linked-grid input.dl-c.is-selected {
  background: #FBF3E4;
  box-shadow: inset 0 0 0 1px #E2CDA4;
}
#dl-grid input.dl-c.is-range-active,
#dl-linked-grid input.dl-c.is-range-active {
  box-shadow: inset 0 0 0 2px #C08A3C;
}
/* Keep an invalid cell unmistakably invalid even while selected. `#dl-grid` only:
   the linked surface deliberately does not mark cell validity (`markCell` is a no-op
   in the linked adapter), so it never carries `data-dl-cell-state`. */
#dl-grid input.dl-c[data-dl-cell-state="invalid"].is-selected {
  background: #FBE9E7;
  box-shadow: inset 0 0 0 1px #C6584A;
}
/*
 * OWNER ACCEPTANCE (item D) — THE HEADER SPAN A PASTE JUST FILLED.
 *
 * A header row paste renames several columns at once, but only the header under the
 * caret looked touched. Headers are NOT part of the cell selection model (they have no
 * row index, and adding them would change what copy/cut/delete treat as a rectangle),
 * so this is a presentation-only mark toggled by the two grid panels and retired by the
 * next meaningful grid interaction.
 *
 * It deliberately reuses the SELECTED-RANGE vocabulary above (`#FBF3E4` fill, `#E2CDA4`
 * hairline) rather than inventing a third highlight colour, so "these cells were just
 * affected" reads identically in the header row and in the body. The 2px `#C08A3C`
 * focus ring from §1 still draws on top via `outline`, which is what keeps the ACTIVE
 * header distinguishable from the rest of the marked span.
 */
#dl-grid input.dl-h.is-paste-range,
#dl-linked-grid input.dl-h.is-paste-range {
  background: #FBF3E4;
  box-shadow: inset 0 0 0 1px #E2CDA4;
}
/* A duplicate name stays unmistakably a problem even inside a freshly pasted span —
   same precedence rule as an invalid cell inside a selected range above. */
#dl-grid input.dl-h.is-duplicate.is-paste-range,
#dl-linked-grid input.dl-h.is-duplicate.is-paste-range {
  background: #FDF1EC;
  color: #A3421B;
  box-shadow: inset 0 0 0 1px #E3B7A6;
}
/* While dragging a range, suppress native text selection so the drag reads as a
   range gesture rather than a text sweep. */
#dl-grid.is-range-dragging,
#dl-linked-grid.is-range-dragging { user-select: none; -webkit-user-select: none; }
#dl-grid.is-range-dragging input.dl-c,
#dl-linked-grid.is-range-dragging input.dl-c { cursor: cell; }

/* ────────────────────────────────
   PHASE 6I — DESIGN-FIRST ANALYSIS UX
   ──────────────────────────────── */

/* The test tile is now a CONTAINER with its own pick button plus a sibling info button, so the
   help control is reachable by keyboard without also selecting the test. The container keeps the
   frozen `.dl-method` look; the pick button carries the click target. */
.dl-method { position: relative; }
.dl-method-pick {
  all: unset;
  display: flex;
  flex-direction: column;
  gap: 3px;
  cursor: pointer;
  width: 100%;
  padding-right: 26px;
}
.dl-method-pick:focus-visible {
  outline: 2px solid #C08A3C;
  outline-offset: 2px;
  border-radius: 4px;
}

/* Statistical-family headings. The real terms stay visible — see `analysisHelp.js`. */
.dl-family-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 14px 0 2px;
}
.dl-family-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: #8A8275;
  text-transform: uppercase;
}

/* ── the accessible help control ──
   A REAL button: in the tab order, activated by Enter/Space for free, with a visible focus ring.
   Never hover-only — hover is an optional extra on pointer devices only. */
.dl-help-btn {
  all: unset;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 1px solid #DED6C8;
  border-radius: 999px;
  background: #fff;
  color: #7A7266;
  font-size: 11px;
  font-style: italic;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.dl-method > .dl-help-btn { position: absolute; top: 12px; right: 12px; }
.dl-help-btn:hover { background: #FAF5EC; color: #5C554B; }
.dl-help-btn:focus-visible { outline: 2px solid #C08A3C; outline-offset: 2px; }
.dl-help-btn[aria-expanded="true"] { background: #C08A3C; border-color: #C08A3C; color: #fff; }

.dl-help-panel {
  margin: 8px 0 0;
  padding: 10px 12px;
  border: 1px solid #EFE9DF;
  border-left: 3px solid #C08A3C;
  border-radius: 6px;
  background: #FCFBF8;
}
.dl-help-panel[hidden] { display: none; }
.dl-help-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.dl-help-title { font-size: 12.5px; color: #2F2A24; }
.dl-help-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #7A5410;
  background: #FDF6E7;
  border: 1px solid #F1E2C0;
  border-radius: 999px;
  padding: 2px 7px;
}
.dl-help-q { margin: 8px 0 0; font-size: 11.5px; font-weight: 600; color: #5C554B; }
.dl-help-a { margin: 2px 0 0; font-size: 12px; color: #7A7266; }
.dl-help-more { display: inline-block; margin-top: 8px; font-size: 12px; color: #A87428; }

.dl-help-terms { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; }
.dl-help-inline, .dl-help-row { display: inline-flex; align-items: center; gap: 5px; }
.dl-help-term { font-size: 12px; font-weight: 600; color: #5C554B; }
.dl-help-label { margin-top: 8px; }

/* ── two-factor authoring ── */
.dl-levels { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.dl-level-row { display: flex; align-items: center; gap: 6px; }
.dl-level-row .dl-grouplabel { flex: 1 1 auto; min-width: 0; }
.dl-level-rm { flex: none; font-size: 11.5px; padding: 4px 10px; }

/* The A×B cell control. The three states are visually distinct, so an undecided cell LOOKS
   undecided and an explicit "not measured" does not look like a mapped column. */
.dl-cellsel {
  width: 100%;
  min-width: 0;
  font-size: 12.5px;
  padding: 5px 6px;
  border: 1px solid #DED6C8;
  border-radius: 6px;
  background: #fff;
  color: #5C554B;
}
.dl-cellsel:focus-visible { outline: 2px solid #C08A3C; outline-offset: 1px; }
.dl-cellsel.is-mapped { border-color: #C6D8C2; background: #FBFDFA; }
.dl-cellsel.is-notmeasured {
  border-style: dashed;
  border-color: #DED6C8;
  background: #FAF8F4;
  color: #8A8275;
  font-style: italic;
}

.dl-factor-matrix th, .dl-factor-matrix td { vertical-align: middle; }
.dl-matrix-rowhead { text-align: left; white-space: nowrap; font-weight: 600; color: #5C554B; }

.dl-factor-stack { display: none; flex-direction: column; gap: 10px; margin-top: 12px; }
.dl-stack-head { font-size: 12.5px; font-weight: 600; color: #2F2A24; margin-bottom: 6px; }
.dl-stack-level { flex: 0 0 92px; font-size: 12px; color: #7A7266; }

/* ── the responsive switch ──
   ONE state, two representations. A wide viewport gets the real matrix, because that is how a
   factorial design is drawn and it makes a missing cell instantly visible. Below the breakpoint
   the stacked factor-level cards take over rather than squeezing 25 selects into a phone width or
   forcing horizontal scrolling. Both are rendered from the same state and edit it identically. */
@media (max-width: 900px) {
  .dl-factor-matrix { display: none; }
  .dl-factor-stack { display: flex; }
}

/* An explicitly unmeasured combination SAYS SO, rather than showing a row of em dashes — those
   are different statements. */
.dl-notmeasured { color: #8A8275; font-style: italic; }

/* ── 6I final polish ──
   The mapping completion counter. Quiet by design: it reports how many cells have been DECIDED,
   which is not the same as the design being valid, so the complete state gets a subtle tint rather
   than a tick or the word "valid". `factorialDesignProblem` stays the only authority on validity. */
.dl-map-progress {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 600;
  color: #8A8275;
}
.dl-map-progress.is-complete { color: #4E7A46; }

/* `Reset design` is DEMOTED to a tertiary destructive action. `Clear mappings` stays the normal
   secondary `.dl-tab`, so the recoverable action reads as the obvious one and the irreversible one
   has to be aimed at. Same button vocabulary — one modifier, no new design system. */
.dl-tab--tertiary {
  border-color: transparent;
  background: transparent;
  color: #8A8275;
  font-size: 12px;
  padding: 6px 10px;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.dl-tab--tertiary:hover {
  background: #FDF1EC;
  border-color: #E3B7A6;
  color: #A3421B;
  text-decoration: none;
}
.dl-tab--tertiary:focus-visible { outline: 2px solid #A3421B; outline-offset: 2px; }

/* ────────────────────────────────
   ANALYSIS STAGE — LAYOUT DENSITY + VISUAL HIERARCHY
   ────────────────────────────────
   Appended rather than edited in place so every rule above stays exactly as it shipped and this
   pass is reviewable as one block. Existing tokens only — no new colour system, no gradients.

   THE FLATNESS HAD ONE MAIN CAUSE: `.dl-stage` and `.dl-card` were both `#fff`, so a card had no
   edge to read against and the stage became a stack of identical boxes. Putting the stage on the
   warm off-white already used by `.dl-groupbox` (#FCFBF8) makes every white card read as raised
   without adding a single decorative element. */
.dl-stage { background: #FCFBF8; }
.dl-card {
  /* One consistent rhythm between major sections, and a hairline shadow so the raised card reads
     as a step in a guided workflow rather than a form box. */
  margin-bottom: 18px;
  box-shadow: 0 1px 2px rgba(47, 42, 36, 0.04);
}
.dl-card-sub { max-width: 76ch; }

/* ── CONDITION SELECTION: a responsive grid, not a vertical stack ──
   Each measurement column used to occupy the full content width, so a six-column dataset pushed
   the test catalogue off screen on a normal laptop. The cards are now a grid.

   The column ladder is stated in TIERS rather than derived from one `auto-fit` minimum, because
   the two consumers of `.dl-groupselect` want different maxima: the condition list benefits from a
   third column on a wide screen, while the two factor cards would look stranded at a third of the
   width. `minmax(0, 1fr)` is what stops a long column label forcing horizontal overflow. */
.dl-groupselect {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  margin-top: 12px;
}
@media (min-width: 700px) {
  .dl-groupselect { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1200px) {
  /* Up to three for the condition list; the two factor cards stay side by side at two. */
  #dl-groups { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* COMPACT condition cards. Tighter than the generic group box, because there are many of them and
   each holds one checkbox plus (when selected) one text field. The inputs stay full-size — the
   label must never become hard to read to win a few pixels. */
#dl-groups .dl-groupbox { padding: 10px 12px; min-width: 0; }
#dl-groups .dl-check { margin-top: 0; }
#dl-groups .dl-field { margin-top: 8px; }
#dl-groups .dl-groupbox-t { overflow-wrap: anywhere; }
/* The selected state was applied in markup (`is-active`) but never styled, so a ticked condition
   looked identical to an unticked one apart from the checkbox. */
.dl-groupbox.is-active {
  background: #FFFCF5;
  border-color: #E4C68F;
  box-shadow: inset 2px 0 0 #C08A3C;
}

/* PHASE 6J-6A — SELECTED MUST NOT READ AS DISABLED.
   Manual acceptance reported the two selected one-factor cards looking pale and locked. The main
   cause was behavioural (both were genuinely `is-disabled`, because the old MIN_GROUPS floor made
   them un-removable) and is fixed in the controller. This strengthens the ACTIVE state so the
   distinction is unmistakable even at a glance, using the existing accent tokens only:
   the left accent bar widens, the label takes the accent's darker ink, and the checkbox itself is
   tinted so a ticked box looks chosen rather than greyed. No new design language, and the
   unselected/eligible card is deliberately left neutral. */
.dl-groupbox.is-active {
  box-shadow: inset 3px 0 0 #C08A3C;
}
.dl-groupbox.is-active .dl-groupbox-t { color: #4A4136; }
.dl-groupbox.is-active input[type="checkbox"] { accent-color: #C08A3C; }
/* An ACTUALLY unavailable card keeps the muted dashed treatment — the two states must never
   share an appearance, which is exactly what the report was about. */
.dl-groupbox.is-disabled input[type="checkbox"] { accent-color: #B9B1A5; }

/* ── STATISTICAL FAMILY BLOCKS ──
   A hairline above each family label turns `PARAMETRIC` / `NON-PARAMETRIC · RANK-BASED` into real
   section headings instead of small grey text floating above the tiles. */
.dl-family-head {
  margin: 18px 0 8px;
  padding-top: 12px;
  border-top: 1px solid #EFE9DF;
}
.dl-family-head:first-of-type { margin-top: 12px; padding-top: 0; border-top: none; }
.dl-family-label { color: #7A7266; }

/* ── PHASE 6K-2 — THE SCHEDULED TIME POINTS ──
   One row per scheduled visit, laid out as a grid rather than a flex line so the position number,
   the label, the numeric-time input and the three controls stay in the SAME COLUMNS down the list.
   That column alignment is the point: the schedule is read vertically to check the order, and a
   ragged flex row makes an out-of-order visit hard to spot. */
.dl-schedule { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.dl-schedule-row {
  display: grid;
  grid-template-columns: 28px 1fr auto auto auto auto;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border: 1px solid #EFE9DF;
  border-radius: 6px;
  background: #FFFDF9;
}
/* The ordinal is the scientific position on the time axis, so it reads as data, not decoration. */
.dl-schedule-pos {
  font-variant-numeric: tabular-nums;
  color: #7A7266;
  text-align: right;
}
.dl-schedule-label { font-weight: 600; overflow-wrap: anywhere; }
/* Narrow, because a time coordinate is a short number and a full-width input would dominate the row. */
.dl-schedule-x { width: 96px; }
.dl-schedule-move,
.dl-schedule-remove {
  border: 1px solid #E3DACB;
  background: #FFF;
  border-radius: 4px;
  /* 28px keeps these at a usable pointer/touch target rather than an icon-sized hit area. */
  min-width: 28px;
  min-height: 28px;
  cursor: pointer;
  line-height: 1;
}
.dl-schedule-move:hover:not(:disabled),
.dl-schedule-remove:hover { border-color: #C9BBA4; background: #FBF7F0; }
/* REAL disabled semantics at the ends of the list — not activatable and visibly inert. */
.dl-schedule-move:disabled { opacity: 0.35; cursor: default; }
.dl-schedule-remove { color: #8C2F22; }
/* An EXCLUDED candidate column stays legible but visibly out of the analysis. Dimmed rather than
   hidden, because "restore the column I removed" needs the row to still be there to click. */
.dl-schedule-row.is-excluded { opacity: 0.55; }
.dl-schedule-row.is-excluded .dl-schedule-label { font-weight: 400; text-decoration: line-through; }

/* The detected-timing OFFER. Boxed so it reads as something to act on rather than a passive hint —
   it is the one place automation asks permission. */
.dl-suggestion {
  margin-top: 10px;
  padding: 8px 10px;
  border: 1px solid #E3DACB;
  border-radius: 6px;
  background: #FBF7F0;
}
.dl-suggestion .dl-hint { margin: 0 0 6px; }

/* The pre-Run review card: a key/value list, because a reader scans DOWN the keys to check what
   BioVisuals decided. Tabular alignment is the point. */
.dl-review { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.dl-review-row { display: grid; grid-template-columns: 160px 1fr; gap: 10px; align-items: baseline; }
.dl-review-k { color: #7A7266; font-size: 12px; }
.dl-review-v { font-weight: 600; overflow-wrap: anywhere; }
/* The current spacing, stated as a value rather than a hint so it is not read as advice. */
.dl-value { margin: 6px 0 0; font-weight: 600; }

/* Boxed action block with the warning tint, for a message that carries buttons. */
.dl-schedule-extra {
  margin-top: 10px;
  padding: 8px;
  border: 1px solid #E8D3B0;
  border-radius: 6px;
  background: #FFFBF2;
}
.dl-schedule-add {
  margin: 4px 6px 0 0;
  padding: 4px 10px;
  border: 1px solid #E3DACB;
  border-radius: 4px;
  background: #FFF;
  cursor: pointer;
}
.dl-schedule-add:hover { border-color: #C9BBA4; background: #FBF7F0; }
/* The DESCRIPTIVE ONLY badge. Deliberately prominent: it sits above the numbers it qualifies, and
   a reader who skims to the table must still meet it first. */
.dl-badge {
  display: inline-block;
  margin: 0 0 10px;
  padding: 3px 8px;
  border: 1px solid #C9BBA4;
  border-radius: 4px;
  background: #F6EFE2;
  color: #5B5348;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
}

/* ── TEST + DESIGN CARDS ──
   A stronger ACTIVE treatment: the accent bar makes the current choice findable at a glance in a
   list of five, which the previous border-tint alone did not. */
.dl-methods { gap: 10px; }
.dl-method {
  background: #fff;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.dl-method:hover { border-color: #D6C7AC; background: #FFFDF9; }
.dl-method.is-active {
  border-color: #C08A3C;
  background: #FFFCF5;
  box-shadow: inset 3px 0 0 #C08A3C;
}
/* The design chooser reads as the FIRST decision, so its tiles carry slightly more presence than
   a test tile. */
#dl-experiment-design .dl-method { padding: 14px 16px; }
#dl-experiment-design .dl-method-n { font-size: 13.5px; }
/* `.dl-method` starts from `all: unset`, which also strips the focus ring. The design tiles ARE
   buttons, so they need it back — a keyboard user must be able to see which tile they are on. */
.dl-method:focus-visible,
button.dl-method:focus-visible {
  outline: 2px solid #C08A3C;
  outline-offset: 2px;
}

/* ── PAIRED TWO-COLUMN CEILING ──
   A paired design takes exactly two conditions, so once two are ticked the remaining columns are
   genuinely unavailable — `disabled` on the input, not merely dimmed, so they leave the tab order
   too. Subtle rather than alarming: these are not errors, they are simply not choosable until one
   of the two is deselected. The two SELECTED cards keep their full active treatment, including an
   editable figure-name input. */
.dl-groupbox.is-disabled {
  opacity: 0.55;
  background: #FAF8F4;
  border-style: dashed;
}
.dl-groupbox.is-disabled .dl-check,
.dl-groupbox.is-disabled .dl-groupbox-t { cursor: not-allowed; }
.dl-groupbox.is-disabled .dl-groupcheck { cursor: not-allowed; }

/* ── PHASE 6K-3 — LOCKED Figure controls ──────────────────────────────────────
 * Owner decision: a control the active scientific design cannot support honestly stays VISIBLE and
 * DISABLED rather than being hidden. Hiding it silently removes the question; muting it answers
 * "no, and not for this design". Mirrors the existing `.dl-step.is-locked` treatment so the whole
 * workspace states unavailability one way.
 *
 * `<option disabled>` is natively non-selectable, so the graph selector needs no extra rule beyond
 * the browser's own greying — only the checkbox row needs muting here. */
.dl-check.is-locked { opacity: 0.45; cursor: not-allowed; }
.dl-check.is-locked input { cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════════════════════════════
   PHASE 6K-5A — DATA PAGE UX POLISH
   ══════════════════════════════════════════════════════════════════════════════════════
   PRESENTATION ONLY. Nothing below changes a validation rule, a payload, a contract or a
   statistic. Every selector targets existing DOM; the only new elements are the two edge
   "+" pills, the `<colgroup>` used to paint a column, the keyboard-help list and the drag
   overlay, all of which are decoration.

   The root-cause fix for "each cell looks like an independent rounded text input" is NOT
   here — it is the `:not(:where(.dl-c, .dl-h))` change on the generic `.dl-modal input`
   rule further up this file, which is what lets the grid's own cell rules take effect.
   ═══════════════════════════════════════════════════════════════════════════════════ */

/* ── §2/§11: the table scrolls, the controls around it do not ─────────────────────────
   `.dl-grid-frame` is the positioning context for the edge affordances, and it sits
   OUTSIDE the scroller so the pills do not slide away with the table or land on top of
   the horizontal scrollbar. The outer border and radius move from the table to the
   scroller, because a `border-collapse: collapse` table cannot clip its own corners — the
   scroller can, so the spreadsheet reads as one continuous surface with rounded edges. */
.dl-grid-frame { position: relative; }
.dl-grid-scroll {
  /* X ONLY. `overflow-y` is deliberately left alone: CSS computes a `visible` value to `auto`
     the moment the other axis is not `visible`, and the container has no height constraint, so
     it grows with the table and vertical scrolling never engages. Stating `hidden` here would
     be the only way to actually clip rows, which is the opposite of what is wanted. */
  overflow-x: auto;
  border: 1px solid #E6DFD3;
  border-radius: 6px;
  background: #fff;
  /* A visible, unobtrusive scrollbar: the horizontal overflow is the affordance that
     tells the scientist there are more columns, so it must not be invisible. */
  scrollbar-width: thin;
}
/* STRUCTURAL rules are scoped to `#dl-grid` ALONE, unlike the cosmetic ones below.
   The reopened editor's Observations table (`#dl-linked-grid`) is not wrapped in a
   `.dl-grid-scroll` and publishes no `--dl-cols`, so giving it a `min-width` or taking
   away its own border would leave it overflowing its card with nothing to scroll. It
   keeps its existing frame and simply inherits the cell-level appearance. */
#dl-grid {
  /* The table now sizes itself and the scroller handles the excess. `min-width` is derived
     from the live column count (published as `--dl-cols` by `buildGridSection`), so up to
     roughly five columns the table still shares the panel width, and past that it scrolls
     instead of squeezing numbers into unreadable slivers.
       44px row-number column + 34px row-action column + 96px per data column. */
  min-width: calc(78px + (var(--dl-cols, 2) * 96px));
  max-width: none;
  border: 0;
  border-radius: 0;
}

/* ── §2: continuous spreadsheet surface ─────────────────────────────────────────────
   One hairline between neighbours (the table is already `border-collapse: collapse`), no
   inter-cell gap, and square body cells. The cell input is a transparent pane over its
   `<td>`, which is what lets the row/column hover tints below show through it while an
   invalid cell still paints its own warm-red fill on top. */
#dl-grid input.dl-c,
#dl-linked-grid input.dl-c {
  border-radius: 0;
  /* Slightly taller than the old 6px so a row is comfortable to hit, and the same
     horizontal padding as the header input so a value lines up under its column name. */
  padding: 7px 8px;
}
/* The header row is the anchor of the table, so it is heavier than the body: a warmer
   fill and a solid bottom rule separating names from values. Deliberately NOT sticky —
   `position: sticky` would resolve against the stage's scroller, not this one, and would
   float the header over unrelated content. */
#dl-grid thead th,
#dl-linked-grid thead th {
  border-bottom: 1px solid #DED6C8;
  background: #F6F2EA;
}
#dl-grid input.dl-h,
#dl-linked-grid input.dl-h { border-radius: 3px; }

/* Row numbers are chrome, not data. The existing rule already gives them their own fill,
   centring and `user-select: none`; these two lines finish the job — a solid rule dividing
   the gutter from the first value column, and tabular figures so the numbers form a
   straight edge instead of a ragged one. */
#dl-grid td.dl-rownum,
#dl-linked-grid td.dl-rownum {
  border-right: 1px solid #DED6C8;
  font-variant-numeric: tabular-nums;
}

/* ── §2: the editing cell is unmistakable ───────────────────────────────────────────
   The BODY cell already had the right rule (`.dl-grid .dl-c:focus`, inset ring, cream
   fill) — it was simply losing to the generic panel focus ring, which the exclusion above
   fixes. Only two things are added here, both at the SAME low specificity so that an
   invalid or selected cell still wins and stays visibly invalid/selected while focused:
     • `:focus-visible` parity, so a keyboard-focused cell rings identically;
     • a ring for the HEADER name input, which had none of its own and would otherwise
       lose its only focus indicator to the exclusion above. */
.dl-grid .dl-c:focus-visible { outline: 2px solid #C08A3C; outline-offset: -2px; }
/* Outline ONLY — no fill. A duplicated header name paints its own warm-red background at
   this same specificity, and a focus rule that also set a background would erase that
   warning for exactly the cell the scientist is trying to fix. */
.dl-grid .dl-h:focus,
.dl-grid .dl-h:focus-visible { outline: 2px solid #C08A3C; outline-offset: -1px; }

/* ── §2: subtle hover reveal — cell, row, column ────────────────────────────────────
   All three are painted on TABLE structure rather than on the inputs, in deliberately
   low-specificity rules, so a marked cell (`.is-invalid`, `.is-duplicate`) and a selected
   range keep their own appearance and simply sit on top. The tints are alpha so they
   layer instead of replacing one another where a row and a column cross. */
#dl-grid tbody tr:hover > td,
#dl-linked-grid tbody tr:hover > td { background-color: rgba(192, 138, 60, 0.05); }
#dl-grid tbody td:hover,
#dl-linked-grid tbody td:hover { background-color: rgba(192, 138, 60, 0.09); }
/* The hovered/focused COLUMN, painted by one class on a single `<col>` element (see
   `attachHotColumnTracking`). A `<col>` background paints beneath the cells, so the row
   and cell tints above still read on top of it. */
#dl-grid col.is-hot,
#dl-linked-grid col.is-hot { background-color: rgba(192, 138, 60, 0.045); }
/* Keep the row-number cell tied to the row it labels. */
#dl-grid tbody tr:hover > td.dl-rownum,
#dl-linked-grid tbody tr:hover > td.dl-rownum { background-color: #F6F2EA; color: #6B6357; }

/* ── §3: hover-revealed edge affordances ────────────────────────────────────────────
   CONVENIENCE ONLY — the visible "+ Add row" / "+ Add column" buttons remain, and these
   are wired to the SAME handlers. Invisible AND `pointer-events: none` until the grid is
   hovered or focused, which is what keeps them clear of cell selection, the start of a
   range drag, the paste target and the scrollbar. They are also `tabindex="-1"`, so they
   add no duplicate stops for keyboard users. */
.dl-grid-edge {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid #DED6C8;
  border-radius: 999px;
  background: #fff;
  color: #A87528;
  font: 600 13px/1 'Inter', 'Open Sans', sans-serif;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s ease-out;
  box-shadow: 0 1px 2px rgba(47, 42, 36, 0.10);
  z-index: 3;
}
.dl-grid-frame:hover .dl-grid-edge,
.dl-grid-frame:focus-within .dl-grid-edge { opacity: 1; pointer-events: auto; }
.dl-grid-edge:hover:not(:disabled) { background: #FAF5EC; border-color: #C08A3C; }
.dl-grid-edge:focus-visible { outline: 2px solid #C08A3C; outline-offset: 2px; }
.dl-grid-edge:disabled { opacity: 0; pointer-events: none; cursor: not-allowed; }
.dl-grid-frame:hover .dl-grid-edge:disabled,
.dl-grid-frame:focus-within .dl-grid-edge:disabled { opacity: 0.3; pointer-events: none; }
/* Bottom centre sits in the 10px gap the button bar's own margin already leaves, so the
   pill occupies dead space rather than covering the last row. */
.dl-grid-edge-row { left: 50%; bottom: -10px; transform: translateX(-50%); }
/* Right centre sits inside the card's 18px horizontal padding — no clipping, no overlap
   with the stage scrollbar. */
.dl-grid-edge-col { right: -10px; top: 50%; transform: translateY(-50%); }

/* ── §9: Clear is present, not prominent ────────────────────────────────────────────
   Same button, same id, same handler, same (absent) confirmation. It simply stops looking
   like a peer of the two additive controls: borderless and link-weight at rest, warming
   to the existing danger palette on hover so the destructive meaning is still stated
   before the click. */
.dl-ghost-quiet {
  border-color: transparent;
  background: transparent;
  color: #8A8275;
  font-weight: 400;
}
.dl-ghost-quiet:hover { border-color: #E3B7A6; }

/* ── §4: neutral guidance vs. a failed check ────────────────────────────────────────
   `is-hint` is the resting tone for the readiness line — the same grey the rest of the
   panel's secondary copy uses. `is-error` is unchanged and is now applied only after the
   scientist has pressed "Validate data"/"Re-check data" AND the table is genuinely
   unsendable. Colour is not the only signal either way: the sentence itself changes. */
.dl-grid-problem.is-hint { color: #7A7266; }

/* ── §5: summary pills, neutral -> ready ────────────────────────────────────────────
   A muted green-grey at the SAME threshold the existing scientific gate uses (two values
   in a column). Deliberately quiet: it reports that a column now has enough values to
   take part, which is arithmetic, and claims nothing about whether the analysis will
   hold. The `ready` word carries the state without colour, for greyscale, high-contrast
   and screen-reader users. */
.dl-colstat.is-ready { border-color: #CFDCC9; background: #F6FAF4; }
.dl-colstat.is-ready .dl-colstat-k { color: #3F5138; }
.dl-colstat.is-ready .dl-colstat-v { color: #5E7154; }
.dl-colstat-f {
  margin-left: 2px;
  padding: 0 5px;
  border-radius: 999px;
  background: #E4EEDF;
  color: #3F5138;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* ── §6: keyboard help as scannable key/action pairs ────────────────────────────────
   Same four bindings the old paragraph listed. A list rather than prose because four
   shortcuts inside one sentence are read as one block and therefore not read at all. */
.dl-keyhelp {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 10px 0 0;
  padding: 0;
  list-style: none;
}
.dl-keyhelp li { display: inline-flex; align-items: center; gap: 6px; }
.dl-kbd {
  display: inline-block;
  min-width: 18px;
  padding: 1px 5px;
  border: 1px solid #DED6C8;
  border-bottom-width: 2px;
  border-radius: 4px;
  background: #FCFBF8;
  color: #5C554B;
  font: 500 10.5px/1.5 ui-monospace, Consolas, monospace;
  text-align: center;
  white-space: nowrap;
}
.dl-keyhelp-t { font-size: 11.5px; color: #8A8275; }
/* The missing-value rule is the single most consequential fact about this table, so it
   keeps its own line rather than trailing a list of shortcuts. */
.dl-hint-missing { margin-top: 8px; }

/* ── §7: drag-and-drop overlay ──────────────────────────────────────────────────────
   Present in the DOM but `hidden` until a real FILE drag enters the Data card, so the
   empty grid is NEVER permanently dressed as a dropzone. `#dl-data-card` is scoped
   deliberately: making every `.dl-card` a positioning context would change stacking
   across the whole workspace. */
#dl-data-card { position: relative; }
.dl-dropzone {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: rgba(255, 253, 248, 0.92);
  z-index: 6;
}
.dl-dropzone[hidden] { display: none; }
.dl-dropzone-inner {
  padding: 14px 20px;
  border: 2px dashed #C08A3C;
  border-radius: 10px;
  background: #FFFDF8;
  color: #8E5F14;
  font-size: 13px;
  font-weight: 600;
}

/* ── §11: LOW-RISK Data-page responsiveness ─────────────────────────────────────────
   Scoped to the Data card. The wider Studio tablet/mobile work is explicitly not in this
   slice: this only guarantees that the grid uses the width it has, that overflow becomes
   a scroll rather than a squeeze, and that the controls wrap instead of overlapping. */
@media (max-width: 860px) {
  /* Below this width the panel is too narrow to share between even two columns, so the
     table always scrolls and the columns keep a legible minimum. */
  #dl-grid { min-width: calc(78px + (var(--dl-cols, 2) * 104px)); }
  /* The upload action drops under the heading rather than squeezing it. */
  #dl-data-card .dl-card-head-actions { margin-top: 8px; }
  /* Row count stops being pushed to the far right, where it wrapped onto its own line
     with the buttons crushed beside it. */
  .dl-grid-count { margin-left: 0; }
  /* Touch-sized, and far enough from the edges not to sit on the scrollbar. */
  .dl-grid-edge { width: 24px; height: 24px; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════
   PHASE 6K-5B — ANALYSIS PAGE UX POLISH + TABLET ADAPTATION
   ══════════════════════════════════════════════════════════════════════════════════════
   PRESENTATION ONLY. No rule below changes which columns are selectable, which tests are
   offered, what a design means or what a Run produces. Every selector targets DOM that
   already existed, except four decoration elements added by `analysisWorkflowChrome.js`:
   the step badge, the design summary row, the pick mark and the "Selected" tag.

   RESPONSIVENESS IS DECLARATIVE. There is no tablet markup and no JS viewport branching —
   `auto-fit` + `minmax()` lets each grid choose its own column count from the width it
   actually has, which is why the same DOM serves desktop and tablet.
   ═══════════════════════════════════════════════════════════════════════════════════ */

/* ── §1: the internal step numbering ────────────────────────────────────────────────
   SUBORDINATE to the left Data/Analysis/Results/Figure rail, and it has to LOOK it: a small
   flat numeral inside the card's own heading, not a second stepper competing with the real
   navigation. The badge is `aria-hidden` — the order is already in the document. */
.dl-card-title--step { display: flex; align-items: center; gap: 9px; }
.dl-substep-n {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: #F1E9DA;
  color: #8E6A2A;
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* ── §2: the design chooser as a responsive card grid ───────────────────────────────
   Five full-width rows were costing roughly a screen of vertical space for one decision.
   `auto-fit` + a 260px floor is what makes this ONE rule serve both targets: a >=1100px
   workspace comfortably fits two columns, a 768–1099px tablet drops to one when a second
   column would be too narrow to read a design description in. No breakpoint states a
   column count, so no width is a special case. */
.dl-designgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 10px;
  margin-top: 12px;
}
/* The tiles are CONTAINERS now (pick button + info button), like the method tiles, so they
   need the container treatment rather than the button one. */
.dl-design { position: relative; }
/* Tighter than the old `#dl-experiment-design .dl-method { padding: 14px 16px }`: two columns
   of five tiles do not need extra presence to read as the first decision — being first and
   numbered does that. */
#dl-experiment-design .dl-method { padding: 11px 13px; }
#dl-experiment-design .dl-method-n { font-size: 13px; }
#dl-experiment-design .dl-method-d { line-height: 1.45; }
/* Room for the info button in the top-right corner, matching the method tiles. */
#dl-experiment-design .dl-method-pick { padding-right: 24px; }

/* ── §4: the collapsed design summary ───────────────────────────────────────────────
   The highest-value change in the slice: once the design is chosen, one line replaces the
   grid. `Change` is a text link rather than a button-shaped control because reopening a
   chooser is navigation, not an action with consequences — and the actual consequences of
   changing a design are owned by `setComparisonType`, exactly as before. */
.dl-chosen {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid #E4C68F;
  border-radius: 8px;
  background: #FFFCF5;
  box-shadow: inset 3px 0 0 #C08A3C;
}
.dl-chosen-text { min-width: 0; flex: 1 1 240px; }
.dl-chosen-name {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: #2F2A24;
}
.dl-chosen-mark { flex: none; color: #7A6A1E; font-weight: 700; }
.dl-chosen-d { margin-top: 2px; font-size: 12px; color: #7A7266; overflow-wrap: anywhere; }
/* >=40px touch target (§15) without looking like a button, achieved with padding rather than
   a fixed height so the text still sets the line box. */
.dl-chosen-change { flex: none; padding: 10px 4px; font-size: 12.5px; }

/* ── §5: compact unselected condition items, expanded selected ones ─────────────────
   THE MARKUP IS UNTOUCHED. The compaction is `:not(.is-active)` alone, which matters for a
   concrete reason: `datalabManualFixes6J6A.test.js` pins the exact class-construction source
   string for `.dl-groupbox`, so adding a class would break a passing test to achieve
   something CSS can already express. Selection semantics are therefore provably unchanged —
   no JS was involved.

   An unselected item collapses to ONE line (checkbox + label). A selected one keeps its full
   padding and grows to hold the figure-label editor, so only the item being configured takes
   vertical space. */
#dl-groups .dl-groupbox:not(.is-active) { padding: 7px 10px; }
#dl-groups .dl-groupbox:not(.is-active) .dl-check {
  /* One line, and a long column name truncates with an ellipsis rather than wrapping the row
     to two lines — the full name stays available as the checkbox's `aria-label` and title. */
  min-width: 0;
  gap: 7px;
}
#dl-groups .dl-groupbox:not(.is-active) .dl-groupbox-t {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}
/* The whole compact row is the target, so the touch area is the item and not just the 13px
   checkbox. §15 wants ~40px; 7px padding + a 20px line box + 7px reaches it. */
#dl-groups .dl-groupbox .dl-check { cursor: pointer; min-height: 22px; }
#dl-groups .dl-groupbox.is-active { padding: 10px 12px; }
#dl-groups .dl-groupbox.is-active .dl-groupbox-t { white-space: normal; overflow-wrap: anywhere; }

/* §6/§18: the condition list picks its OWN column count from the available width.

   This SUPERSEDES the fixed 1 → 2 → 3 ladder for `#dl-groups` only. Both that rule and this one
   score (1,0,0), so source order decides and this one is later in the file — the earlier
   `@media (min-width: 1200px) { #dl-groups { repeat(3, …) } }` no longer applies. A media query
   adds no specificity, which is why "later wins" holds at every width.

   `.dl-groupselect`'s own 700px/1200px tiers are untouched and still serve the two FACTOR cards,
   which want to sit side by side rather than pack — the existing comment above that ladder already
   records why its two consumers want different maxima.

   A compact one-line item is legible at 190px, so a wide desktop packs four and a tablet takes one
   or two, with no column count written down anywhere. */
#dl-groups {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 8px;
}
/* A SELECTED item spans two tracks where there is room, so its label editor is comfortably
   wide instead of squeezed into a 190px column. It falls back to one track automatically at
   tablet width, because a single-column grid has nothing to span. */
@media (min-width: 900px) {
  #dl-groups .dl-groupbox.is-active { grid-column: span 2; }
}

/* ── §10: the selected method reads as selected without relying on colour ───────────
   A ring-and-dot mark in the title row plus the word "Selected". The mark is `aria-hidden`
   because `aria-pressed` already carries the state; this is for eyes, including eyes on a
   greyscale or high-contrast display. */
.dl-pickmark {
  flex: none;
  width: 14px;
  height: 14px;
  border: 1.5px solid #C6BEB0;
  border-radius: 999px;
  background: #fff;
  box-sizing: border-box;
}
.dl-method-n { display: flex; align-items: center; gap: 8px; }
.dl-method.is-active .dl-pickmark {
  border-color: #C08A3C;
  /* The inner dot is a shadow rather than a child element, so the mark stays one node. */
  box-shadow: inset 0 0 0 3px #fff, inset 0 0 0 8px #C08A3C;
}
.dl-picked {
  align-self: flex-start;
  margin-top: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  background: #F1E9DA;
  color: #8E6A2A;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ── §11: the compact route into method help ────────────────────────────────────────
   One sentence above the cards, so the detail lives behind help instead of making every
   card verbose. The affordance is the SAME `buildHelpControl` disclosure used everywhere
   else in this stage, only worded — hence a `.dl-link` rather than the round glyph. */
.dl-methodhelp { margin: 10px 0 0; font-size: 12.5px; color: #7A7266; }
.dl-methodhelp-link { font-size: 12.5px; }
/* The round-glyph placement rule must not catch this worded control. */
.dl-method > .dl-help-btn { position: absolute; top: 10px; right: 10px; }

/* ── §14: density ───────────────────────────────────────────────────────────────────
   Scoped to the Analysis stage via `[data-dl-stage="analysis"]`, so the Data, Results and
   Figure stages keep their existing rhythm exactly. Section BOUNDARIES are preserved — the
   card border, background and shadow are untouched — and only the space inside and between
   cards tightens. Text size, focus outlines and touch targets are not reduced anywhere. */
.dl-modal[data-dl-stage="analysis"] .dl-card { margin-bottom: 12px; padding: 13px 16px; }
.dl-modal[data-dl-stage="analysis"] .dl-card-sub { margin-top: 2px; }
.dl-modal[data-dl-stage="analysis"] .dl-methods { gap: 8px; }
.dl-modal[data-dl-stage="analysis"] .dl-method { padding: 10px 13px; }
.dl-modal[data-dl-stage="analysis"] .dl-family-head { margin: 12px 0 6px; padding-top: 9px; }
.dl-modal[data-dl-stage="analysis"] .dl-groupselect { margin-top: 10px; }
.dl-modal[data-dl-stage="analysis"] .dl-review { margin-top: 6px; }
.dl-modal[data-dl-stage="analysis"] .dl-hint { margin-top: 6px; }
/* Consecutive hints were stacking three separate 8px gaps under the method list. */
.dl-modal[data-dl-stage="analysis"] .dl-hint + .dl-hint { margin-top: 3px; }

/* ── §15: TABLET (~768–1099px available workspace) ──────────────────────────────────
   No separate markup and no JS branching. Everything here either relaxes a desktop-only
   assumption or protects a touch target; nothing hides a control, because a control the
   scientist cannot reach is worse than one they have to scroll to.

   Phones (<~700px) are deliberately NOT addressed — that is the later Studio responsiveness
   project, and guessing at it here would ship rules nobody has reviewed. */
@media (max-width: 1099px) {
  /* Design tiles fall to one column when a second would be too narrow to read. The grid
     already does this from `minmax()`; the floor is lowered so the transition happens at the
     right width rather than early. */
  .dl-designgrid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
  /* Statistical cards need NO rule here, and that is deliberate: `.dl-methods` is a single-column
     flex column already, so they stay full readable width at every width. They carry the most
     prose on the stage and two narrow columns of assumptions is exactly the wrong trade. */
  /* The review card's fixed 160px key column is too wide once the value has to wrap; keys and
     values stack instead of colliding. */
  .dl-review-row { grid-template-columns: minmax(110px, 34%) 1fr; }
  /* Comfortable touch targets on the controls a tablet user actually taps. */
  .dl-help-btn { width: 24px; height: 24px; }
  .dl-method > .dl-help-btn { top: 8px; right: 8px; }
  #dl-groups .dl-groupbox .dl-check { min-height: 28px; }
  .dl-schedule-move,
  .dl-schedule-remove { min-width: 34px; min-height: 34px; }
  /* Footer actions must stay reachable: they wrap rather than overflowing the modal. */
  .dl-foot { flex-wrap: wrap; row-gap: 8px; }
  .dl-actions { flex-wrap: wrap; }
}
@media (max-width: 860px) {
  /* One column for conditions too. Below this the panel is ~96vw and a second 190px track
     leaves neither item wide enough for its label editor. */
  #dl-groups { grid-template-columns: minmax(0, 1fr); }
  /* With one track there is nothing to span, so the desktop span rule must not linger. */
  #dl-groups .dl-groupbox.is-active { grid-column: auto; }
  /* Key over value, so a long condition list or a long method name never overlaps its key. */
  .dl-review-row { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .dl-review-k { font-size: 11.5px; }
  /* `Change` drops under the design name rather than squeezing it. */
  .dl-chosen { flex-direction: column; align-items: stretch; }
  .dl-chosen-change { align-self: flex-start; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════
   PHASE 6K-5C — RESULTS UX + P-VALUE PRECISION + RESULTS EXPORT
   ══════════════════════════════════════════════════════════════════════════════════════
   PRESENTATION ONLY. No rule below changes a statistic, a threshold, a test, an interval or
   a warning. The p-value's authoritative double lives on `data-dl-p-raw`; everything here
   styles the string derived from it.
   ═══════════════════════════════════════════════════════════════════════════════════ */

/* ── §13: the Results header and the Export menu ────────────────────────────────────
   A header row of its own rather than a control floated into the first card, so the Export
   affordance belongs to the STAGE and can never be mistaken for part of one result. */
.dl-results-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.dl-results-title { margin: 0; font-size: 14.5px; font-weight: 600; color: #2F2A24; }
.dl-export-said { margin-left: auto; font-size: 12px; color: #7A7266; }
.dl-exportmenu { position: relative; flex: none; }
.dl-export-btn { display: inline-flex; align-items: center; gap: 7px; }
.dl-export-btn i { font-size: 11.5px; color: #A87528; }
.dl-caret { font-size: 9px; color: #8A8275; }
.dl-exportlist {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 8;
  min-width: 172px;
  padding: 4px;
  border: 1px solid #E6DFD3;
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 6px 18px rgba(47, 42, 36, 0.13);
}
.dl-exportlist[hidden] { display: none; }
.dl-exportitem {
  all: unset;
  display: block;
  width: 100%;
  box-sizing: border-box;
  /* >=36px so the menu is usable with a finger as well as a pointer. */
  padding: 9px 11px;
  border-radius: 5px;
  font-size: 12.5px;
  color: #5C554B;
  cursor: pointer;
}
.dl-exportitem:hover { background: #FAF5EC; color: #2F2A24; }
.dl-exportitem:focus-visible { outline: 2px solid #C08A3C; outline-offset: -2px; }

/* ── §9: the compact summary ────────────────────────────────────────────────────────
   The four numbers a scientist writes down, above the tables rather than after them. It is
   modestly emphasised — a tinted card and a slightly larger metric value — and deliberately
   NOT giant typography: the numbers below it are the same numbers, and a headline twice the
   size would imply the summary is the result and the cards are detail. */
.dl-summary {
  border-color: #E4C68F;
  background: #FFFCF5;
  box-shadow: inset 3px 0 0 #C08A3C;
}
.dl-summary-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.dl-summary-m { font-size: 13.5px; font-weight: 600; color: #2F2A24; }
.dl-summary-s { font-size: 12.5px; color: #7A7266; }

/* ── §11: the inferential metric grid ───────────────────────────────────────────────
   Replaces a long loose key/value column. `auto-fit` + `minmax()` again, so the grid chooses
   its own column count from the width it has and stacks on a tablet without a breakpoint
   declaring how many columns exist (§20). */
.dl-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px 18px;
  margin-top: 10px;
}
.dl-metric { min-width: 0; }
.dl-metric-k {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #8A8275;
}
.dl-metric-v {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 1px;
  font-size: 13.5px;
  font-weight: 600;
  color: #2F2A24;
  /* Tabular figures so a column of metrics reads down cleanly. */
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* ── §3: the p-value cell ──────────────────────────────────────────────
   Tabular figures so `< 0.001 (7.96e-9)` and `0.02341` align down a table column.

   PHASE 6K-5C.1 §3 — THE COPY-BUTTON RULES ARE GONE, with the control itself. Owner review
   found it unnecessary: the value is ordinary selectable text and the exports already carry the
   raw double as a number. `white-space: normal` because the dual form is longer than the old
   superscript one and must be allowed to wrap rather than widen a card. */
.dl-p { font-variant-numeric: tabular-nums; }
.dl-p-v { white-space: normal; overflow-wrap: anywhere; }

/* ── §8/§10: tighter Results, clearer descriptive table ─────────────────────────────
   Scoped to the Results stage, so the Data, Analysis and Figure stages keep their rhythm.
   Nothing is hidden: n, mean, SD, SEM and CI all remain, and only spacing and alignment
   change. */
.dl-modal[data-dl-stage="results"] .dl-card { margin-bottom: 12px; padding: 13px 16px; }
.dl-modal[data-dl-stage="results"] .dl-welch { gap: 5px 20px; margin-top: 8px; }
.dl-modal[data-dl-stage="results"] .dl-hint { margin-top: 6px; }
.dl-modal[data-dl-stage="results"] .dl-hint + .dl-hint { margin-top: 3px; }

/* The statistics tables: a distinct header, tighter rows, and NUMERIC RIGHT ALIGNMENT from
   the second column on — the first column is a label (a group, a pair, a time point) and the
   rest are quantities, so aligning the numbers is what lets a reader compare down a column. */
.dl-stats { width: 100%; border-collapse: collapse; }
.dl-stats th, .dl-stats td { padding: 5px 9px; font-size: 12.5px; }
.dl-stats thead th {
  border-bottom: 1px solid #DED6C8;
  background: #F6F2EA;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #5C554B;
  text-align: left;
  white-space: nowrap;
}
.dl-stats tbody tr + tr td { border-top: 1px solid #F2EDE4; }
.dl-stats tbody tr:hover td { background-color: rgba(192, 138, 60, 0.045); }
.dl-stats th:not(:first-child),
.dl-stats td:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; }
.dl-stats td:first-child { font-weight: 500; overflow-wrap: anywhere; }

/* §10/§20: a wide statistics table scrolls INSIDE its own container rather than pushing the
   stage sideways. The wrapper is the table's own parent card, so no DOM change is needed. */
.dl-modal[data-dl-stage="results"] .dl-card { overflow-x: auto; }

/* ── §12: notes stay informational unless they are actually warnings ────────────────
   Unchanged behaviour — `.dl-warn` and `.dl-note` already carry the two tones, and the
   consolidated missing-value messages keep theirs. This only stops the Notes card competing
   with the summary above it. */
.dl-modal[data-dl-stage="results"] #dl-result-notes .dl-warn { margin-top: 4px; }
.dl-modal[data-dl-stage="results"] #dl-result-notes .dl-warn + .dl-warn { margin-top: 3px; }

/* ── §20: tablet structure (no mobile redesign) ─────────────────────────────────────
   Structurally ready only: the metric grid reduces columns on its own, tables scroll in
   place, and the Export menu stays reachable. Visual acceptance is deferred with the rest of
   the Studio responsive work. */
@media (max-width: 1099px) {
  .dl-metrics { grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); }
  .dl-results-head { row-gap: 6px; }
  /* The status text drops to its own line rather than squeezing the button. */
  .dl-export-said { margin-left: 0; order: 3; flex-basis: 100%; }
}
@media (max-width: 860px) {
  /* Two metrics per row at most, so `95% CI` and a scientific p-value are not truncated. */
  .dl-metrics { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
  .dl-modal[data-dl-stage="results"] .dl-welch { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .dl-modal[data-dl-stage="results"] .dl-welch dt { margin-top: 6px; }
  /* A right-anchored menu would overflow a narrow card; it becomes full-width instead. */
  .dl-exportmenu { position: static; }
  .dl-exportlist { position: static; margin-top: 6px; min-width: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════
   TABLET DIALOG — 768-1099px
   ══════════════════════════════════════════════════════════════════════════════════════
   PRESENTATION ONLY. Nothing below changes a statistic, a test, a threshold, an interval,
   an API call, a saved dataset or a rendered figure. No DataLab JavaScript is touched by
   this slice: the workspace has ZERO viewport listeners by design, all of its state
   (`s.grid`, `s.stage`, `s.linkedGrid`, results) lives in the controller and none of it is
   DOM-derived — which is exactly why a rotation can be handled entirely by the cascade and
   cannot lose a typed value, a step or an analysis.

   WHY THIS BLOCK IS AT THE END OF THE FILE
   Media queries contribute no specificity, so source order decides. The existing
   `max-width: 1100px` block also matches at 1099px and sets `.dl-main`'s stepper column;
   these rules have to come after it to win. Same reasoning the file already records at its
   other breakpoints.

   WHAT WAS WRONG BEFORE
   `.dl-modal` was `height: 90vh; max-height: calc(100vh - 66px)` inside an overlay with
   `padding: 50px 16px 16px`. On a tablet: (a) `vh` ignores collapsing browser chrome, so
   the modal could extend past the visible area and put the footer actions off screen;
   (b) the 90vh height did not account for the overlay's own 66px of vertical padding, so
   the two fought; (c) the horizontal stepper rail only appeared below 860px, leaving a
   168px vertical gutter on a 768px screen. All three are addressed here.
   ═══════════════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1099px) {
  /* A small, deliberate margin rather than a true edge-to-edge sheet: the Studio canvas
     staying just visible behind it is what tells the user this is a workspace ON their
     project, not a separate application. */
  .dl-overlay {
    padding: 10px;
    align-items: stretch;
    justify-content: stretch;
  }

  /* `dvh`/`dvw` first with a `vh`/`vw` fallback underneath, so a browser without dynamic
     viewport units still gets a bounded modal and a browser with them tracks collapsing
     tablet chrome. `max-width`'s desktop 1520px cap is irrelevant here and is released so
     the modal can use the width it has. */
  .dl-modal {
    width: 100%;
    max-width: none;
    height: 100%;
    /* Fallback pair, then the dynamic pair. Both discount the overlay's own padding. */
    max-height: calc(100vh - 20px);
    max-height: calc(100dvh - 20px);
    border-radius: 10px;
  }

  /* The header and footer are already `flex: 0 0 auto` children of a bounded flex column,
     so they are effectively sticky once the modal itself is bounded — which the rules above
     now guarantee. Only the padding is tightened to buy vertical room for content. */
  .dl-head { padding: 10px 14px; }
  .dl-subtitle { display: none; }   /* the title + version already identify the workspace */
  .dl-foot { padding: 10px 14px; }
  .dl-stage { padding: 14px 14px 18px; }

  /* SINGLE COLUMN + COMPACT HORIZONTAL STEPPER.
     These are the existing 860px rules, moved up to the whole tablet range: a 168px
     vertical rail is not affordable at 768-1099px, and a horizontal strip of four steps
     reads better on a wide-but-short tablet in landscape too. */
  .dl-main {
    grid-template-columns: minmax(0, 1fr);
    /* EXPLICIT rows are load-bearing here. With one column and two children the grid creates
       two IMPLICIT `auto` rows, and a grid container's default `align-content: stretch`
       distributes the leftover height between them — which made the stepper strip 171px tall
       at 768x1024 and 249px at 820x1180 even though its four steps sat correctly on one row.
       Naming the rows makes the stepper content-height and gives every remaining pixel to the
       stage, which is the only region that should scroll. */
    grid-template-rows: auto minmax(0, 1fr);
  }
  .dl-steps {
    flex-direction: row;
    align-items: center;
    gap: 6px;
    overflow-x: auto;
    overflow-y: hidden;
    border-right: 0;
    border-bottom: 1px solid #EFE9DF;
    padding: 5px 10px;
    scrollbar-width: none;
  }
  .dl-steps::-webkit-scrollbar { display: none; }
  .dl-step {
    flex: 0 0 auto;
    /* 40px so a step is a comfortable tap, and no taller, so the strip stays a strip
       rather than becoming a second header. */
    min-height: 40px;
    box-sizing: border-box;
    padding: 6px 10px;
  }
  .dl-step-label { white-space: nowrap; font-size: 12.5px; }

  /* The reopened linked-chart editor has no stepper and already collapses the column.
     Restated at this width so the rules above cannot resurrect an empty rail. */
  .dl-overlay[data-dl-mode="linked-chart-readonly"] .dl-steps { display: none; }

  /* THE DATA TABLE KEEPS ITS OWN HORIZONTAL SCROLL REGION.
     `.dl-grid-scroll` already owns `overflow-x: auto`; the only change is a wider
     per-column floor so a finger-sized cell stays legible, and a taller cell so it stays
     tappable. The scroller's parent/child relationship is untouched, so every selector the
     interaction engine uses (`#dl-grid input[data-dl-cell="r-c"]`, `#dl-grid-body`,
     `data-dl-row`) still resolves and row/column editing is unaffected. */
  #dl-grid { min-width: calc(78px + (var(--dl-cols, 2) * 104px)); }
  #dl-grid input.dl-c,
  #dl-grid input.dl-h { min-height: 38px; }
  .dl-grid-scroll { scrollbar-width: thin; }
  /* Row/column remove controls and the add-row/add-column bar stay finger-sized. */
  .dl-rowremove,
  .dl-colremove { min-width: 32px; min-height: 32px; }
  .dl-grid-bar .dl-ghost { min-height: 38px; }

  /* Two columns are used ONLY where the width genuinely supports them — expressed as
     `auto-fit` + a legible floor rather than as a column count, so landscape gets two and
     portrait falls to one without either being a special case. */
  .dl-figure { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
  .dl-split { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

  /* SUPPORT BUTTON.
     `#openCallPanelBtn` is `position: fixed; z-index: 9999` and the DataLab overlay is
     2000, so the call button painted on top of the modal — including over its footer
     actions. Suppressed in CSS rather than JS, in the same idiom the file already uses for
     the canvas floating toolbar, because `planControl.js` writes
     `callBtn.style.display = ''` and a JS hide would fight it. Removing the body class
     restores normal behaviour exactly. */
  body.datalab-open #openCallPanelBtn {
    opacity: 0 !important;
    pointer-events: none !important;
    visibility: hidden !important;
  }
}

/* PORTRAIT: strictly one column. Above, `auto-fit` would still give two columns to a
   1024px-wide landscape tablet; in portrait the widest supported viewport is 899px, so a
   second 320px track leaves neither pane usable. */
@media (max-width: 1099px) and (orientation: portrait) {
  .dl-figure { grid-template-columns: minmax(0, 1fr); }
  .dl-split { grid-template-columns: minmax(0, 1fr); height: auto; }
  .dl-split-preview { position: static; }
  .dl-tabpanel { overflow: visible; }
  /* With the panes stacked, the stage is the scrolling region again on both the Figure
     stage and the reopen editor — otherwise two nested scrollers fight. */
  .dl-stage.dl-stage--figure { overflow-y: auto; }
  .dl-stage--figure .dl-figure { height: auto; align-items: start; }
  .dl-figure-preview { position: static; }
  .dl-figure-controls { overflow: visible; max-height: none; }
  .dl-overlay[data-dl-mode="linked-chart-readonly"] .dl-stage { overflow-y: auto; }
}
