/* dataview.css — the chrome the whole data tool shares: theme, header, tab
   strip, notice bar, legend, table, popover menus, stats tables and the landing
   card. Anything belonging to one block of tabs alone lives in data.css, which
   is loaded after this one. */

/* ---------- theme ---------- */
:root {
  color-scheme: light dark;

  --bg:        light-dark(#fbfbfa, #17181a);
  --panel:     light-dark(#ffffff, #1f2124);
  --panel-2:   light-dark(#f2f2f0, #26282c);
  --ink:       light-dark(#1a1c1e, #e8e9ea);
  --ink-dim:   light-dark(#6b7075, #9aa0a6);
  --line:      light-dark(#dcdcd8, #34373b);
  --line-hard: light-dark(#b9b9b4, #4a4e53);
  --accent:    light-dark(#2f6fdd, #7aa7f5);
  --focus:     light-dark(#2f6fdd, #7aa7f5);

  /* Row tint: lightness / chroma for an ordinary row vs an emphasized one
     (`hi` — sequenced in the inventory, a positive result in qPCR). These are
     bare numbers fed to oklch(), so they can't use light-dark() (that function
     only produces colours) — hence the media query below. */
  --tint-l:      0.945;
  --tint-c:      0.045;
  --tint-l-hi:   0.845;
  --tint-c-hi:   0.105;
  --tint-hover: -0.035;
}
@media (prefers-color-scheme: dark) {
  :root {
    --tint-l:     0.265;
    --tint-c:     0.040;
    --tint-l-hi:  0.415;
    --tint-c-hi:  0.090;
    --tint-hover: 0.045;
  }
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 13px/1.45 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  /* column flex rather than fixed grid rows: bars above the table come and go
     (the notice, the legend), and a row template has to be recounted every
     time one is added */
  display: flex; flex-direction: column;
  overflow: hidden;
}
body > * { flex: none; }
body > :is(.scroll, .landing) { flex: 1 1 auto; min-height: 0; }
button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; }
.hidden { display: none !important; }

/* ---------- top bar ---------- */
header {
  display: flex; flex-direction: column; gap: 8px;
  padding: 8px 12px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}
/* Two rows: identity and tabs on top, the filtering controls underneath, so
   the tab strip keeps its place no matter how wide the search box gets. */
header .row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
h1 {
  font-size: 14px; font-weight: 650; margin: 0 4px 0 0; letter-spacing: -0.01em;
  display: flex; align-items: baseline; gap: 7px;
}
h1 small { font-weight: 400; color: var(--ink-dim); font-size: 11px; }
h1 a { color: inherit; text-decoration: none; }
h1 a:hover { color: var(--accent); }
/* shown for a moment when the folder watcher pulls a change in by itself */
#live {
  font-size: 10px; font-weight: 600; letter-spacing: .02em;
  color: light-dark(#1a7f37, #56d364);
  border: 1px solid currentColor; border-radius: 999px; padding: 0 5px;
}
#live[hidden] { display: none; }
#live.go { animation: fade-out 1.8s ease-out forwards; }
@keyframes fade-out { 0%, 50% { opacity: 1 } 100% { opacity: 0 } }
@media (prefers-reduced-motion: reduce) {
  #live.go { animation-duration: 1.8s; animation-timing-function: step-end; }
}

/* ---------- tab strip ----------
   Two blocks, Inventory and Results, each its own pill: the group's name and
   then its tabs. Given the room they are both open. When the header runs out of
   width, the block you aren't in folds its tabs shut and leaves its name behind
   as the way back — layoutTabs decides which, and sets the widths the fold
   animates between. */
.tabs { display: flex; gap: 8px; flex: none; }
.tgrp {
  display: flex; align-items: center; gap: 2px;
  background: var(--panel-2); border-radius: 7px; padding: 2px;
}
.tabs button {
  border: 0; background: none; padding: 4px 12px; border-radius: 5px;
  color: var(--ink-dim); font-weight: 550; flex: none; white-space: nowrap;
}
.tabs button[aria-selected="true"] {
  background: var(--panel); color: var(--ink);
  box-shadow: 0 1px 2px light-dark(rgb(0 0 0 / .18), rgb(0 0 0 / .5));
}
/* The block's name. A label while the block is open — quiet, and the ink of
   the block you're in — and a button once it's folded. */
.tabs .gname {
  font-size: 11px; font-weight: 650; letter-spacing: .04em;
  padding: 4px 8px; opacity: .8;
}
.tgrp.on .gname { color: var(--ink); opacity: 1; }
.gname:hover { color: var(--accent); }
.tabs .tgrp.closed .gname {
  font-size: inherit; letter-spacing: 0; opacity: 1;
  padding: 4px 12px; color: var(--ink-dim);
}
.tgrp.closed .gname:hover { background: var(--panel); color: var(--ink); }
/* The fold itself. Width is set in px from the buttons' own widths, so there is
   something for the transition to run between; overflow keeps them from
   spilling out of the closing block. */
.tabset {
  display: flex; overflow: hidden;
  transition: width .26s cubic-bezier(.4, 0, .2, 1), opacity .2s ease;
}
/* On a phone even one block can be wider than the window; its tabs scroll
   rather than running off the edge. */
.tgrp:not(.closed) .tabset { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
.tgrp.closed .tabset { opacity: 0; }
.tabs.no-anim .tabset { transition: none; }
@media (prefers-reduced-motion: reduce) { .tabset { transition: none; } }

.btn {
  border: 1px solid var(--line-hard); background: var(--panel);
  padding: 4px 10px; border-radius: 6px; white-space: nowrap;
}
.btn:hover { background: var(--panel-2); }
/* The About link is a link, not a button — it opens the docs in a new tab. It
   sits in the same row as the table's controls, so it looks like one. */
a.btn { color: inherit; text-decoration: none; }
/* Kept in the layout rather than hidden when there is nothing to clear, so the
   row doesn't reflow the moment a filter is applied or a tab changes. */
.btn:disabled { color: var(--ink-dim); opacity: .55; cursor: default; }
.btn:disabled:hover { background: var(--panel); }
.btn.primary { background: var(--accent); border-color: transparent; color: light-dark(#fff, #10131a); font-weight: 600; }
.btn[aria-expanded="true"] { background: var(--panel-2); border-color: var(--line-hard); }

.search {
  border: 1px solid var(--line-hard); background: var(--panel);
  border-radius: 6px; padding: 4px 8px; min-width: 15rem; flex: 1 1 15rem; max-width: 26rem;
}
.search::placeholder { color: var(--ink-dim); }
:is(.btn, .search, .tabs button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--focus); outline-offset: 1px;
}

.spacer { flex: 1 1 0; }
.count { color: var(--ink-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.count b { color: var(--ink); font-weight: 600; }

/* ---------- notice bar ---------- */
.notice {
  display: flex; align-items: baseline; gap: 8px;
  padding: 6px 12px; border-bottom: 1px solid var(--line);
  background: light-dark(#fff6e0, #3a3016);
  color: light-dark(#5c4708, #f0dfae);
  font-size: 12px;
}
.notice .x { margin-left: auto; border: 0; background: none; color: inherit; padding: 0 4px; opacity: .7; }
.notice .x:hover { opacity: 1; }

/* ---------- legend ---------- */
.legend {
  display: flex; gap: 5px; flex-wrap: wrap; align-items: center;
  padding: 7px 12px; border-bottom: 1px solid var(--line); background: var(--bg);
  /* the qPCR panel assays run to thirty-odd targets; past a few rows of chips
     the key stops earning the space it takes from the table */
  max-height: 26vh; overflow-y: auto;
}
.legend .lbl { color: var(--ink-dim); margin-right: 2px; }
.chip {
  --tl: var(--tint-l); --tc: var(--tint-c); --cm: 1; --ld: 0;
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid transparent; border-radius: 999px; padding: 2px 9px 2px 6px;
  background: oklch(calc(var(--tl) + var(--ld)) calc(var(--tc) * var(--cm)) var(--h, 0));
  color: light-dark(#15171a, #eceef0);
  font-size: 11.5px; font-weight: 550;
}
.chip .n { color: light-dark(#00000090, #ffffff90); font-variant-numeric: tabular-nums; }
.chip .sw {
  width: 9px; height: 9px; border-radius: 2px; flex: none;
  background: oklch(calc(var(--tint-l-hi) + var(--ld)) calc(var(--tint-c-hi) * var(--cm)) var(--h, 0));
  outline: 1px solid light-dark(#0003, #fff3);
}
button.chip:hover { border-color: var(--line-hard); }
button.chip[aria-pressed="true"] { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
/* a legend that filters rather than explains a colour: no swatch to promise one */
.legend.plain .chip { padding-left: 9px; background: var(--panel-2); }
.legend.plain .chip .sw { display: none; }
.legend.plain .chip:hover { background: var(--panel); }
.legend .sep { width: 1px; align-self: stretch; background: var(--line); margin: 0 4px; }
.legend .note { color: var(--ink-dim); font-size: 11.5px; }
/* the pale→dark ramp shown next to "darker = …", in the caller's hue */
.legend .note .dk {
  display: inline-block; width: 22px; height: 10px; vertical-align: -1px; border-radius: 3px;
  background: linear-gradient(90deg,
    oklch(var(--tint-l) var(--tint-c) var(--h, 145)),
    oklch(var(--tint-l-hi) var(--tint-c-hi) var(--h, 145)));
  outline: 1px solid light-dark(#0002, #fff2);
}

/* ---------- table ---------- */
.scroll { overflow: auto; overscroll-behavior: contain; }
table { border-collapse: separate; border-spacing: 0; width: max-content; min-width: 100%; }

thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--panel); border-bottom: 1px solid var(--line-hard);
  padding: 0; text-align: left; font-weight: 600; white-space: nowrap;
  /* so a heading pinned against the sticky key column gets trimmed at the
     column's right edge (…from the right) rather than sliding out of sight
     under it (which showed "Prob" as a bare "b"). */
  overflow: hidden;
}
thead th .h {
  display: flex; align-items: center; gap: 2px; padding: 5px 4px 5px 8px;
  position: sticky; left: var(--stick, 0px);
}
thead th:first-child .h { position: static; }
thead th .name {
  cursor: pointer; user-select: none; display: flex; align-items: center; gap: 4px;
  min-width: 0;
}
thead th .name .txt { overflow: hidden; text-overflow: ellipsis; }
thead th .name:hover { color: var(--accent); }
thead th .arrow { color: var(--accent); font-size: 10px; width: 8px; flex: none; }
.funnel {
  border: 0; background: none; color: var(--ink-dim); padding: 3px; border-radius: 4px;
  line-height: 0; opacity: .45; flex: none;
}
.funnel svg { display: block; width: 11px; height: 11px; }
.funnel svg path {
  stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; fill: none;
}
th:hover .funnel, .funnel[data-on] { opacity: 1; }
.funnel[data-on] { color: var(--accent); background: light-dark(#2f6fdd18, #7aa7f528); }

tbody td {
  padding: 3px 8px; border-bottom: 1px solid light-dark(#00000012, #ffffff10);
  vertical-align: top; max-width: 34rem;
}
tbody tr {
  /* --cm scales the theme's chroma and --ld shifts its lightness, which is how
     the bacteria read as muted browns against the saturated viral hues without
     either of them hard-coding a colour: both stay relative to whatever the
     theme (and the darker "hi" variant) put in --tl / --tc. */
  --h: 0; --tl: var(--tint-l); --tc: 0; --hov: 0; --cm: 1; --ld: 0;
  background: oklch(calc(var(--tl) + var(--hov) + var(--ld)) calc(var(--tc) * var(--cm)) var(--h));
  /* one band of the multi-species stripe, measured across the 45° diagonal */
  --stripe: 9px;
}
tbody tr[data-tint] { --tc: var(--tint-c); }
tbody tr[data-tint][data-hi] { --tl: var(--tint-l-hi); --tc: var(--tint-c-hi); }
tbody tr:hover { --hov: var(--tint-hover); }
tbody tr[data-tint][data-hi] > td:first-child {
  box-shadow: inset 3px 0 0 oklch(from oklch(calc(var(--tl) + var(--ld)) calc(var(--tc) * var(--cm)) var(--h)) calc(l - .3) calc(c + .06) h);
}
tbody tr.dim { opacity: .55; }

/* key column sticks */
th:first-child, td:first-child { position: sticky; left: 0; z-index: 2; }
thead th:first-child { z-index: 4; }
tbody td:first-child {
  background: inherit; font-weight: 600; white-space: nowrap;
  border-right: 1px solid light-dark(#00000018, #ffffff14);
}

td a { color: var(--accent); }
td.num { text-align: right; font-variant-numeric: tabular-nums; }
td.nowrap { white-space: nowrap; }
td.clip { max-width: 26rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
td.clip.open { white-space: pre-wrap; max-width: 46rem; }
td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; }
td.narrow { max-width: 7rem; }
td.narrow.open { max-width: 26rem; }
td.none { color: var(--ink-dim); }

/* grouped views (reagents by category, qPCR results by run).
   The heading spans every column, so its text would otherwise be free to widen
   them — a long run summary was stretching the sticky key column across half
   the screen. Taking it out of flow (absolute inside a relative cell) means it
   contributes no width at all; the inner span then sticks to the left edge so
   the heading stays readable when the table is scrolled sideways. */
tr.grp td {
  position: relative; background: var(--panel-2); font-weight: 650; white-space: nowrap;
  padding: 0; height: 22px; border-top: 1px solid var(--line-hard);
  border-bottom: 1px solid var(--line-hard); cursor: pointer;
}
tr.grp td > div { position: absolute; inset: 0; padding: 3px 8px; }
tr.grp td > div > span { position: sticky; left: 8px; display: inline-block; }
tr.grp td:hover { color: var(--accent); }
tr.grp .n { color: var(--ink-dim); font-weight: 400; font-variant-numeric: tabular-nums; }
tr.grp .sub { color: var(--ink-dim); font-weight: 400; margin-left: 6px; }
tr.grp .tw { display: inline-block; width: 10px; color: var(--ink-dim); }

/* ---------- detail rows ----------
   The draws under a cDNA tube: a small table hung off the summary row rather
   than a second tab. It is sticky at the left edge so it stays readable when
   the (far wider) table above it is scrolled sideways, and it sets no column
   widths of its own — the cell it lives in spans them all. */
button.exp {
  border: 0; background: none; color: var(--ink-dim); padding: 0 4px 0 0;
  font-size: 10px; line-height: 1;
}
button.exp:hover { color: var(--accent); }
tr[data-tint] button.exp { color: inherit; opacity: .6; }
tr.det > td { padding: 0; background: var(--panel-2); position: static; }
tr.det > td > div {
  position: sticky; left: 0; display: inline-block;
  padding: 7px 8px 9px 26px; max-width: min(100vw, 60rem);
}
tr.det .empty { color: var(--ink-dim); font-style: italic; }
tr.det table { width: auto; min-width: 0; }
tr.det th {
  text-align: left; font-weight: 600; color: var(--ink-dim); font-size: 11px;
  padding: 0 10px 2px 0; white-space: nowrap;
}
tr.det td {
  padding: 1px 10px 1px 0; border: 0; background: none;
  font-weight: 400; position: static; max-width: 30rem;
}

.badge {
  display: inline-block; padding: 0 6px; border-radius: 999px; font-size: 11px; font-weight: 600;
  background: light-dark(#00000012, #ffffff18);
}
.tick { color: light-dark(#1a7f37, #56d364); font-weight: 700; }

/* ---------- stats pane ---------- */
.stats { padding: 16px 14px 40px; overflow: auto; }
.stats h2 { margin: 0 0 3px; font-size: 14px; }
.stats h2:not(:first-child) { margin-top: 26px; }
.stats p.sub { margin: 0 0 14px; color: var(--ink-dim); font-size: 11.5px; max-width: 46rem; }
table.stat { border-collapse: separate; border-spacing: 0; width: max-content; }
table.stat th, table.stat td {
  border-bottom: 1px solid var(--line); padding: 4px 12px; white-space: nowrap;
}
table.stat thead th {
  position: sticky; top: 0; background: var(--panel); border-bottom: 1px solid var(--line-hard);
  text-align: right; overflow: visible;
}
table.stat thead th:first-child { text-align: left; }
table.stat tbody th {
  text-align: left; font-weight: 600; position: sticky; left: 0;
  background: var(--bg); border-right: 1px solid var(--line);
}
table.stat td { text-align: right; font-variant-numeric: tabular-nums; }
table.stat tr.sep th, table.stat tr.sep td { border-top: 1px solid var(--line-hard); }
/* the heat wash is always a light green, so its text is always dark */
table.stat td.heat { background: oklch(var(--hl) var(--hc) 148); color: #15171a; }
table.stat tbody tr:hover th, table.stat tbody tr:hover td:not(.heat) {
  background: var(--panel-2);
}
table.stat td.blank { color: var(--ink-dim); }

/* ---------- popovers ---------- */
[popover] {
  position: fixed; margin: 0; inset: auto;
  border: 1px solid var(--line-hard); border-radius: 9px; padding: 0;
  background: var(--panel); color: var(--ink);
  box-shadow: 0 10px 30px light-dark(rgb(0 0 0 / .18), rgb(0 0 0 / .55));
  max-height: min(60vh, 30rem); overflow: auto; min-width: 13rem;
}
[popover]::backdrop { background: transparent; }
.menu-head {
  position: sticky; top: 0; background: var(--panel); padding: 8px 10px 6px;
  border-bottom: 1px solid var(--line); display: flex; gap: 6px; align-items: center;
}
.menu-head input { flex: 1; min-width: 0; border: 1px solid var(--line-hard); border-radius: 5px; padding: 3px 6px; background: var(--bg); }
.menu-head .lnk { border: 0; background: none; color: var(--accent); padding: 2px 4px; font-size: 11.5px; }
.menu-body { padding: 5px; }
.menu-body label {
  display: flex; gap: 7px; align-items: center; padding: 3px 7px; border-radius: 5px; cursor: pointer;
}
.menu-body label:hover { background: var(--panel-2); }
.menu-body label span.v { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.menu-body label span.c { color: var(--ink-dim); font-variant-numeric: tabular-nums; font-size: 11px; }
.menu-body .empty { color: var(--ink-dim); padding: 6px 8px; }

/* ---------- landing ---------- */
.landing { display: grid; place-items: center; padding: 24px; overflow: auto; }
.card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  padding: 26px 30px; max-width: 34rem; text-align: center;
  box-shadow: 0 1px 3px light-dark(rgb(0 0 0 / .07), rgb(0 0 0 / .4));
}
.card h2 { margin: 0 0 6px; font-size: 17px; }
.card p { color: var(--ink-dim); margin: 0 0 16px; }
.card .row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.card .btn { padding: 7px 16px; }
.card .hint { margin-top: 16px; font-size: 11.5px; color: var(--ink-dim); }
.card code { background: var(--panel-2); padding: 1px 5px; border-radius: 4px; font-size: 11px; }
/* the GitHub half of the card, below a rule */
.card .alt { border-top: 1px solid var(--line); margin-top: 22px; padding-top: 18px; }
.card .alt h3 { margin: 0 0 6px; font-size: 14px; }
.card .alt p { margin: 0 0 12px; font-size: 12px; }
.card .tok {
  background: var(--bg); border: 1px solid var(--line-hard); color: var(--ink);
  border-radius: 8px; padding: 7px 10px; font: inherit; font-size: 12px;
  min-width: 0; flex: 1 1 15rem;
}
.card .tok::placeholder { color: var(--ink-dim); }
.card .caveat { margin: 12px 0 0; font-size: 11.5px; color: var(--ink-dim); }
.drop { outline: 2px dashed var(--accent); outline-offset: -6px; }
.err { color: light-dark(#b3261e, #ff8a80); margin-top: 12px; min-height: 1em; font-size: 12px; }
