/* NOBBLE additions on top of the Depot system inherited from BINMAN.
 *
 * Only what NOBBLE adds: the tab strip, the pin bar with its PEPTIDE slot, the
 * campaign progress chip, the weights panel and the benchmark cards. Colours,
 * type and spacing all come from depot.css tokens, so a theme change there
 * carries through here and the two modules cannot drift apart.
 */

/* ------------------------------------------------------------------ header */

.app-header {
  display: flex;
  align-items: center;
  gap: var(--gutter);
  flex-wrap: wrap;
  padding: 0.7rem 1.1rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 40;
}

.brand-block { display: flex; flex-direction: column; gap: 0.1rem; }

.brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-decoration: none;
  color: var(--ink);
}
.brand__mark { width: 26px; height: 26px; color: var(--accent); flex: 0 0 auto; }
.brand__word {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.12rem;
  letter-spacing: 0.08em;
}
/* The backronym, which degrades.
 *
 * The expansion dissolves left to right and leaves the initials spelling
 * NOBBLE, then returns. A degrader knocks a protein out; this knocks the
 * subtitle out.
 *
 * Only opacity, blur and colour animate. Nothing changes the box, so the
 * initials hold their position exactly and the line never reflows, which is
 * what keeps it from jittering in a sticky header at any width. */
.backronym {
  margin: 0;
  font-size: 0.68rem;
  color: var(--muted);
  letter-spacing: 0.02em;
  white-space: nowrap;
  /* It fits whole or it does not appear. Truncated at 46ch it read
     "...Benchmarking fo..." and lost "for Ligase Engagement", which takes the
     L and the E with it: two of the six letters the line exists to show. No
     ellipsis, no max-width; the breakpoints below hide it instead. */
}
.backronym b {
  font-weight: 700;
  font-style: normal;
  color: var(--accent);
  /* The initials brighten as the rest leaves, so what remains is the name. */
  animation: nobble-remains 9s ease-in-out infinite;
}
.backronym i {
  font-style: normal;
  /* --i staggers the sweep: eight steps across roughly a third of the cycle,
     so it reads as progressive loss rather than a single blink. */
  animation: nobble-degrade 9s ease-in-out infinite;
  animation-delay: calc(var(--i) * 0.11s);
}

@keyframes nobble-degrade {
  0%, 34%   { opacity: 1; filter: blur(0); }
  46%, 62%  { opacity: 0; filter: blur(2px); }
  76%, 100% { opacity: 1; filter: blur(0); }
}

@keyframes nobble-remains {
  0%, 34%   { color: var(--accent); letter-spacing: 0.02em; }
  46%, 62%  { color: var(--ink); letter-spacing: 0.1em; }
  76%, 100% { color: var(--accent); letter-spacing: 0.02em; }
}

/* An animation that loops forever in a sticky header is exactly the kind a
   vestibular trigger makes unusable, and the sentence has to stay readable
   without it: reduced motion gets the full expansion, static. */
@media (prefers-reduced-motion: reduce) {
  .backronym b, .backronym i { animation: none; }
  .backronym i { opacity: 1; filter: none; }
  .backronym b { color: var(--accent); }
}

.brand__strap {
  margin: 0;
  font-size: 0.68rem;
  color: var(--muted);
  letter-spacing: 0.02em;
}

.tabs { display: flex; gap: 0.15rem; flex-wrap: wrap; margin-left: auto; }
.tab {
  font-family: var(--display);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.45rem 0.7rem;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.tab:hover { color: var(--ink); background: var(--surface-2); }
.tab:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.tab--on { color: var(--ink); border-bottom-color: var(--accent); }

.header-tools { display: flex; align-items: center; gap: 0.6rem; }

/* ------------------------------------------------- status and health panel */

.status {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.28rem 0.5rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 0.66rem;
}
.status__dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--muted); flex: 0 0 auto;
}
/* Running is the only state that pulses: a steady light for a steady state,
   and nothing blinking once the work is finished. */
.status__dot--running { background: var(--good); animation: status-pulse 2s ease-in-out infinite; }
.status__dot--complete { background: var(--good); }
.status__dot--idle { background: var(--warn); }
.status__dot--no-data { background: var(--bad); }
.status__dot--unknown { background: var(--warn); }
@keyframes status-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

.status__state {
  font-family: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.6rem;
  color: var(--muted);
}
.status__bar {
  display: block; width: 54px; height: 5px;
  background: var(--bg); border: 1px solid var(--line); overflow: hidden;
}
.status__fill { display: block; height: 100%; background: var(--accent); }

.status__stats { display: flex; gap: 0.5rem; margin: 0; }
.status__stats > div { display: flex; flex-direction: column; line-height: 1.15; cursor: help; }
.status__stats > div:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.status__stats dt {
  font-size: 0.52rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted);
}
.status__stats dd { margin: 0; font-size: 0.72rem; color: var(--ink); }
.status__sub { color: var(--muted); font-size: 0.6rem; }

.header-links { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.header-links a {
  font-family: var(--data);
  font-size: 0.62rem;
  padding: 0.16rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
}
.header-links a:hover, .header-links a:focus-visible {
  color: var(--accent); border-color: var(--accent);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.theme-toggle {
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 0.3rem 0.5rem;
  cursor: pointer;
  font-size: 0.9rem;
}

/* ----------------------------------------------------------------- pin bar */

.pinbar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.45rem 1.1rem;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  font-size: 0.72rem;
}
.pinbar__label {
  font-family: var(--display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  font-size: 0.66rem;
}
.pin {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}
.pin__slot {
  font-size: 0.6rem;
  letter-spacing: 0.07em;
  color: var(--muted);
  text-transform: uppercase;
}
.pin--set { border-color: var(--accent); background: var(--accent-soft); }
.pin--empty .pin__value { color: var(--muted); }
.pin__clear {
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.95rem;
  line-height: 1;
  padding: 0 0.1rem;
}
.pin__clear:hover { color: var(--bad); }
.pin__reset {
  margin-left: 0.3rem;
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 0.66rem;
  padding: 0.22rem 0.45rem;
  cursor: pointer;
}
.pin__reset:hover { color: var(--ink); border-color: var(--ink); }

/* ------------------------------------------------------------------- layout */

.app-main { padding: 1.1rem; max-width: 1680px; margin: 0 auto; }

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.95rem 1.05rem;
  margin-bottom: 1.1rem;
  box-shadow: var(--shadow);
}
.panel > h2, .panel > h3 {
  font-family: var(--display);
  margin: 0 0 0.2rem;
  font-size: 1rem;
  letter-spacing: 0.01em;
}
.panel__hint { margin: 0 0 0.8rem; color: var(--muted); font-size: 0.78rem; max-width: 76ch; }

.split { display: grid; gap: 1.1rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1100px) {
  .split--2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .split--sidebar { grid-template-columns: minmax(0, 1fr) 19rem; }
}

.mono { font-family: var(--data); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------ tables */
/* Named .ntable, not .ledger. depot.css already defines .ledger as BINMAN's
   page layout grid (display: grid with a rail column), and a table carrying
   that class is blockified: thead and tbody stop participating in table
   layout and the header paints straight over the first row. Inheriting a
   design system means inheriting its namespace, so anything NOBBLE adds is
   prefixed. */

/* border-collapse: collapse and position: sticky do not work together: the
   collapsed border model gives the browser no box to stick, and the header
   ends up painted over the first row rather than above it. Measured: thead and
   the first row both reported top = 843. separate + zero spacing looks
   identical and sticks correctly. */
.ntable { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.78rem; }
.ntable th, .ntable td {
  text-align: left;
  padding: 0.38rem 0.5rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.ntable tbody tr:last-child td { border-bottom: 0; }
.ntable th {
  font-family: var(--display);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  cursor: pointer;
  position: sticky;
  top: 0;
  background: var(--surface);
  /* Without a stacking context the first row paints through the sticky header:
     it looked like the header had grown an extra pair of cells. */
  z-index: 2;
  box-shadow: inset 0 -1px 0 var(--line);
}
.ntable tbody tr:hover { background: var(--surface-2); }
/* The pair identifier is long and uninformative past its prefix, so it is
   truncated with the full value on hover rather than setting the table's
   width. Flags wrap instead of forcing a horizontal scroll: a 12-column table
   that overflows its panel is harder to read than one that wraps its least
   precise column. */
.ntable td:first-child {
  max-width: 15ch;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.72rem;
}
.ntable td:last-child, .ntable th:last-child { white-space: normal; min-width: 9rem; }
.ntable td.num { font-size: 0.74rem; }
.ntable tbody tr.row--pinned { background: var(--accent-soft); }
.ntable td.num { text-align: right; font-family: var(--data); font-variant-numeric: tabular-nums; }
/* A scrollable region must be reachable by keyboard, or its content is
   unreachable without a pointer. */
.table-wrap { max-height: 32rem; overflow: auto; border: 1px solid var(--line); }
.table-wrap:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* ------------------------------------------------------------------- flags */

.flag {
  display: inline-block;
  font-size: 0.6rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.1rem 0.32rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  color: var(--muted);
}
.flag--glue { border-color: var(--good); color: var(--good); }
.flag--protac { border-color: var(--accent); color: var(--accent); }
.flag--warn { border-color: var(--warn); color: var(--warn); }
.flag--bad { border-color: var(--bad); color: var(--bad); }
.flag--pending { border-style: dashed; }

/* ----------------------------------------------------------------- weights */

.weights { display: flex; flex-direction: column; gap: 0.75rem; }
.weight { display: flex; flex-direction: column; gap: 0.2rem; }
.weight__head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.4rem; }
.weight__label { font-size: 0.74rem; }
.weight__value { font-family: var(--data); font-size: 0.72rem; color: var(--muted); }
.weight input[type="range"] { width: 100%; accent-color: var(--accent); }
.weight__note { font-size: 0.66rem; color: var(--muted); margin: 0; }
.weight--unvalidated .weight__label::after {
  content: ' not validated';
  font-size: 0.6rem;
  color: var(--warn);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ------------------------------------------------------------------- cards */

.cards { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr)); }
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
  box-shadow: var(--shadow);
}
/* A pending card is signalled by its dashed border and its own background, not
   by opacity. Opacity on the container blends every descendant toward the page
   and silently drops text contrast: axe measured 4.05 against the 4.5 needed,
   on text that passes at full opacity. A container-level opacity can never be
   safe for body text for that reason. */
.card--pending { border-style: dashed; background: var(--surface-2); }
.card--pending .card__title,
.card--pending .card__role,
.card--pending .empty p,
.card--pending .empty h3 { color: var(--ink); }
.card--pending .card__head .mono { color: var(--muted); }
.card__head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
.card__title { font-family: var(--display); font-size: 0.95rem; margin: 0; }
.card__role { font-size: 0.72rem; color: var(--muted); margin: 0.25rem 0 0.7rem; }
.card__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); gap: 0.5rem; }
.stat { border-left: 2px solid var(--line); padding-left: 0.45rem; }
.stat--good { border-left-color: var(--good); }
.stat--warn { border-left-color: var(--warn); }
.stat--bad { border-left-color: var(--bad); }
.stat__label { display: block; font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.stat__value { display: block; font-family: var(--data); font-size: 0.95rem; }
.stat__sub { display: block; font-size: 0.62rem; color: var(--muted); }

/* ------------------------------------------------------------------ viewer */

.viewer {
  position: relative;
  width: 100%;
  height: 30rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
}
.viewer__canvas { position: absolute; inset: 0; }
.viewer__canvas canvas { width: 100% !important; height: 100% !important; display: block; }
.viewer__empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1rem;
  background: var(--surface-2);
  color: var(--muted);
}
.viewer__empty h3 { font-family: var(--display); margin: 0 0 0.3rem; color: var(--ink); font-size: 0.95rem; }
.viewer__empty p { margin: 0; max-width: 46ch; font-size: 0.78rem; }
.viewer__readout {
  position: absolute;
  left: 0.5rem;
  bottom: 0.5rem;
  font-size: 0.68rem;
  color: var(--ink);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.2rem 0.45rem;
  pointer-events: none;
}
.viewer__controls { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-top: 0.5rem; }
.viewer__controls button {
  font-family: var(--display);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.3rem 0.55rem;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink);
  border-radius: var(--radius);
  cursor: pointer;
}
.viewer__controls button:hover { border-color: var(--accent); color: var(--accent); }
.viewer__controls button[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); }

.chart { width: 100%; height: 26rem; }
.chart--short { height: 18rem; }

/* ----------------------------------------------------------------- tooltip */

.tip {
  position: fixed;
  z-index: 90;
  max-width: 24rem;
  padding: 0.5rem 0.6rem;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: 0.72rem;
  line-height: 1.45;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.1s;
}
.tip--on { opacity: 1; }
.tip__row { display: block; color: var(--muted); }
.tip__src { display: block; margin-top: 0.3rem; color: var(--muted); font-size: 0.66rem; font-style: italic; }

/* ------------------------------------------------------------------- empty */

.empty {
  padding: 1.6rem;
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}
.empty h3 { font-family: var(--display); margin: 0 0 0.3rem; color: var(--ink); }
.empty p { margin: 0.2rem auto; max-width: 54ch; font-size: 0.8rem; }

/* ------------------------------------------------------------------ footer */

.app-footer {
  padding: 1rem 1.1rem 2rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.72rem;
}
.app-footer p { margin: 0.2rem 0; }
.app-footer a { color: var(--accent); }
.app-footer__caveat { font-style: italic; }

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
  z-index: 100;
  background: var(--surface);
  border: 2px solid var(--accent);
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius);
}

/* Below this the header has no room for the expansion without squeezing the
   tabs, so it goes rather than being clipped. The visually hidden copy keeps
   it available to a screen reader at every width. */
@media (max-width: 1180px) {
  .backronym span[aria-hidden] { display: none; }
}

@media (max-width: 760px) {
  /* The status panel keeps its dot, state and bar on a phone and drops the
     four numbers, which are unreadable at that size and are all on the
     About page anyway. */
  .status__stats { display: none; }
  .header-links a { font-size: 0.58rem; }
  .tabs { margin-left: 0; width: 100%; }
  .app-main { padding: 0.7rem; }
  .viewer { height: 22rem; }
  .chart { height: 20rem; }
}
