/* =====================================================================
   Night Library — REVIEW DESK (js/nightshift-library-mode-review.jsx).

   A two-column instrument: a scrolling desk of attempt cards, and a
   rail that does not move. The rail is the whole argument for this
   mode — comparing two attempts by opening and closing a modal twenty
   times is not comparing, so the inspector stays put and the desk moves
   under it.

   Three rules carried in from the Library's rendering pass, and none of
   them is stylistic:

     · NO backdrop-filter. A pane with a blur behind it re-rasterises
       everything scrolling under it every frame, and under this desk
       are live <iframe> thumbnails and decoding video stills.

     · `contain: layout paint style` on the card leaf (it arrives with
       .nsl-card) and NEVER `contain: size` or `content-visibility`.
       Sizing containment makes the engine guess heights it can measure,
       which is what turns a scroll into a jitter.

     · THE ROW PITCH IS NOT IN THIS FILE. The desk is virtualized, and
       the windowing arithmetic and the layout have to agree on the row
       height and the column count exactly. A media query here plus a
       breakpoint in the JS is that agreement waiting to rot, so the
       mode writes both onto the row element inline, from the same two
       constants windowSlice was called with. What is here is only what
       cannot disagree: gutters, padding, colour.
   ===================================================================== */

.nslm-review {
  /* .nsl-modeframe already supplies the flex column and the height
     budget; this only stops the modebar from inheriting a gap. */
  gap: 0;
}

/* The split. The rail is a fixed track, so the desk gets everything
   else and `minmax(0, 1fr)` is what stops a wide card from pushing the
   rail off the edge (a 1fr track's automatic minimum is its content). */
.nslm-review-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--nsl-rail-w);
  gap: 12px;
  flex: 1 1 auto;
  min-height: 0;
}
/* Narrow: the rail goes under the desk rather than squeezing it to one
   cramped column plus a 320px sliver. It still does not scroll away —
   it is its own pane with its own scroller, which is the property that
   mattered. */
@media (max-width: 1080px) {
  .nslm-review-split {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(200px, 1fr) minmax(190px, 42%);
  }
}
.nslm-review-desk, .nslm-review-rail { min-height: 0; min-width: 0; }

/* An honest warning, not a filter: the saved catalog spans jobs and the
   desk folds by attempt number. */
.nslm-review-crossjob {
  flex: none;
  margin: 0 0 10px;
  padding: 7px 11px;
  border: 1px solid rgba(224, 180, 80, 0.35);
  border-radius: 6px;
  background: rgba(224, 180, 80, 0.07);
  font: 10.5px/1.55 var(--nsf-mono);
  color: var(--nsf-split);
}

/* ---------------------------------------------------------------------
   Modebar extras.
--------------------------------------------------------------------- */
.nslm-review-counts {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 3px 9px;
  border: 1px dashed var(--nsf-glass-border);
  border-radius: 999px;
  font: 10px var(--nsf-mono);
  cursor: help;
}
.nslm-review-count { white-space: nowrap; }
.nslm-review-count.win { color: var(--nsf-supported); }
.nslm-review-count.keep { color: var(--nsf-amber); }
.nslm-review-count.reject { color: var(--nsf-contradicted); }
/* "your marks" is not a decoration — it is the label that keeps three
   counts from reading as three things the daemon recorded. */
.nslm-review-count-label {
  color: rgba(var(--nsl-haze-rgb), 0.7);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------------
   The desk.
--------------------------------------------------------------------- */
.nslm-review-deskbody { padding: 12px; }

/* One virtual row. `height` and `grid-template-columns` are written
   inline by the mode; `box-sizing` is what makes the inline padding
   (the row gap) come out of that height rather than add to it, which is
   the difference between the windower's arithmetic being right and
   being off by one gap per row. */
.nslm-review-row {
  display: grid;
  column-gap: 14px;
  box-sizing: border-box;
  align-items: stretch;
}

.nslm-review-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--nsf-panel);
}
/* Selection and marks paint as RINGS, not borders: .nsl-card:hover owns
   the border colour, and a card that lost its selection ring the moment
   the pointer touched it is a card whose state you cannot trust. */
.nslm-review-card.on {
  box-shadow: inset 0 0 0 2px rgba(var(--nsl-amber-rgb), 0.85);
}
.nslm-review-card.mark-win { box-shadow: inset 0 0 0 1px rgba(var(--nsl-ok-rgb), 0.75); }
.nslm-review-card.mark-keep { box-shadow: inset 0 0 0 1px rgba(var(--nsl-amber-rgb), 0.6); }
.nslm-review-card.mark-reject { box-shadow: inset 0 0 0 1px rgba(212, 87, 107, 0.7); opacity: 0.66; }
.nslm-review-card.mark-reject:hover { opacity: 1; }
/* Selected wins over marked — it is the card being read right now. */
.nslm-review-card.on.mark-win,
.nslm-review-card.on.mark-keep,
.nslm-review-card.on.mark-reject {
  box-shadow: inset 0 0 0 2px rgba(var(--nsl-amber-rgb), 0.85);
  opacity: 1;
}

.nslm-review-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 5px 0 10px;
  border-bottom: 1px solid var(--nsf-glass-border);
  font: 10px var(--nsf-mono);
  color: var(--nsf-haze);
}
.nslm-review-seq {
  color: var(--nsf-bone);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.nslm-review-size { color: rgba(var(--nsl-haze-rgb), 0.72); white-space: nowrap; }
/* An attempt whose rows recorded neither bytes nor tokens prints an
   em dash, and it has to READ as an absence rather than as a very
   small file — same italic grey the shared .nsl-none uses, so one
   treatment means "nothing was recorded" everywhere in this family. */
.nslm-review-size.none { color: rgba(var(--nsl-haze-rgb), 0.55); font-style: italic; }
.nslm-review-headgap { flex: 1 1 auto; }
.nslm-review-noteflag { color: var(--nsf-amber); cursor: help; }

/* The rank chip states the PANEL's ordering, which is why it is bone
   and quiet rather than a coloured badge: it is a position, not a
   grade, and colouring it would invent a band nobody recorded. */
.nslm-review-rank {
  font: 9.5px var(--nsf-mono);
  color: var(--nsf-bone);
  border: 1px solid rgba(232, 228, 218, 0.35);
  border-radius: 999px;
  padding: 0 6px;
  white-space: nowrap;
  cursor: help;
}

.nslm-review-icon {
  flex: none;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--nsf-haze);
  font: 12px/1 var(--nsf-mono);
  cursor: pointer;
  transition: color 0.12s ease, background 0.12s ease;
}
.nslm-review-icon:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.nslm-review-icon:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -2px; }
.nslm-review-icon.on { color: var(--nsf-amber); background: rgba(var(--nsl-amber-rgb), 0.14); }

/* The picture-and-provenance block. It selects into the rail; it is not
   the lightbox, and the two icon buttons above are how you reach that. */
.nslm-review-hit {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.nslm-review-hit:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -2px; }

.nslm-review-thumb {
  flex: 1 1 auto;
  width: 100%;
  min-height: 84px;
  border-radius: 0;
}

.nslm-review-kv {
  flex: none;
  padding: 7px 10px 3px;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 2px 8px;
  font-size: 10px;
}
.nslm-review-kv .k { font-size: 8px; letter-spacing: 0.08em; }
/* One line each, clipped. The full value is in the rail, and a
   provenance line that wraps to three lines eats the metrics. */
.nslm-review-kv .v {
  min-width: 0;
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  overflow-wrap: normal;
}

/* Four tiles, always four, always on one line. They share the width
   evenly rather than wrapping, because a wrapped fifth line inside a
   fixed-height card is a clipped line. */
.nslm-review-metrics { padding: 4px 10px 0; }
.nslm-review-metrics.dense {
  flex-wrap: nowrap;
  gap: 5px;
}
.nslm-review-metrics.dense .nsl-metric {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  padding: 4px 6px;
}
.nslm-review-metrics.dense .nsl-metric-k,
.nslm-review-metrics.dense .nsl-metric-v {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.nslm-review-metrics.dense .nsl-metric-v { font-size: 11.5px; }
.nslm-review-metrics.dense .nsl-metric.ungraded .nsl-metric-v { font-size: 8.5px; }

.nslm-review-flags {
  flex: none;
  display: flex;
  align-items: center;
  gap: 4px;
  height: 26px;
  padding: 6px 10px 0;
  overflow: hidden;
}

/* The panel's own winner and the reviewer's own pick are the two facts
   on this desk that must never be mistaken for each other, so they do
   not look alike: the recorded verdict is a solid supported-green chip,
   a local mark is a dashed one. Dashed reads as provisional, which is
   exactly what it is. */
.nslm-review-panelwin {
  color: var(--nsf-supported);
  border-color: rgba(var(--nsl-ok-rgb), 0.55);
  background: rgba(var(--nsl-ok-rgb), 0.1);
  white-space: nowrap;
}
.nslm-review-mark {
  border-style: dashed;
  white-space: nowrap;
  cursor: help;
}
.nslm-review-mark.mark-win { color: var(--nsf-supported); border-color: rgba(var(--nsl-ok-rgb), 0.6); }
.nslm-review-mark.mark-keep { color: var(--nsf-amber); border-color: rgba(var(--nsl-amber-rgb), 0.6); }
.nslm-review-mark.mark-reject { color: var(--nsf-contradicted); border-color: rgba(212, 87, 107, 0.6); }

.nslm-review-acts {
  flex: none;
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: auto;
  padding: 7px 10px 9px;
}
.nslm-review-act {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 4px 3px;
  text-align: center;
}
.nslm-review-act:disabled { opacity: 0.35; cursor: default; }
.nslm-review-act:disabled:hover { color: rgba(232, 228, 218, 0.8); background: none; }
.nslm-review-act:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -1px; }
.nslm-review-act.reject.on { color: var(--nsf-contradicted); border-color: rgba(212, 87, 107, 0.6); }
.nslm-review-act.keep.on { color: var(--nsf-amber); border-color: rgba(var(--nsl-amber-rgb), 0.6); }
.nslm-review-act.win.on { color: var(--nsf-supported); border-color: rgba(var(--nsl-ok-rgb), 0.6); }
.nslm-review-err {
  flex: none;
  max-width: 40%;
  font: 9px var(--nsf-mono);
  color: var(--nsf-contradicted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The ⋮ menu lives inside the card on purpose: .nsl-card carries
   `overflow: hidden` and paint containment, so anything that overflowed
   would simply be cut. Sized to fit the shortest card this desk draws. */
.nslm-review-menu {
  position: absolute;
  top: 31px;
  right: 6px;
  z-index: 4;
  width: 214px;
  max-width: calc(100% - 12px);
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px;
  border: 1px solid var(--nsf-glass-border);
  border-radius: 8px;
  background: var(--nsf-panel);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.6);
}
.nslm-review-menu-item {
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--nsf-bone);
  font: 10px var(--nsf-mono);
  text-align: left;
  padding: 6px 8px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nslm-review-menu-item:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }
.nslm-review-menu-item:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -2px; }

/* ---------------------------------------------------------------------
   The rail.
--------------------------------------------------------------------- */
.nslm-review-railbody { padding: 12px; }
.nslm-review-insp { display: flex; flex-direction: column; gap: 10px; }
.nslm-review-insp-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font: 11px var(--nsf-mono);
}
.nslm-review-railthumb {
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: 6px;
}
.nslm-review-insp-acts { display: flex; flex-wrap: wrap; gap: 5px; }
.nslm-review-insp-acts .nsa-act-btn:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -1px; }

.nslm-review-headline {
  font: 11px/1.55 var(--nsf-mono);
  color: var(--nsf-bone);
  border-left: 2px solid rgba(181, 126, 220, 0.55);
  padding-left: 9px;
}

/* The prompt is the JOB's, and it is 160 characters of it. The eyebrow
   says both, in the label rather than a tooltip, because a reader who
   only sees the paragraph must not come away thinking they read the
   attempt's own prompt. */
.nslm-review-prompt {
  border: 1px solid var(--nsf-glass-border);
  border-radius: 7px;
  padding: 8px 10px;
  background: rgba(255, 255, 255, 0.02);
}
.nslm-review-prompt .nsf-eyebrow { font-size: 8.5px; letter-spacing: 0.16em; margin-bottom: 5px; }
.nslm-review-prompt p {
  margin: 0;
  font: 11px/1.6 var(--nsf-mono);
  color: rgba(232, 228, 218, 0.86);
  overflow-wrap: anywhere;
}

.nslm-review-insp-kv { grid-template-columns: 58px minmax(0, 1fr); }

.nslm-review-warn {
  font: 10px/1.55 var(--nsf-mono);
  color: var(--nsf-split);
  border: 1px solid rgba(224, 180, 80, 0.33);
  background: rgba(224, 180, 80, 0.07);
  border-radius: 6px;
  padding: 6px 9px;
}
.nslm-review-warn.dim { color: rgba(var(--nsl-haze-rgb), 0.8); border-color: var(--nsf-glass-border); background: none; }

.nslm-review-scribe { display: flex; flex-direction: column; gap: 6px; }
.nslm-review-scribe .nsf-eyebrow { font-size: 8.5px; letter-spacing: 0.16em; }
.nslm-review-scribe p {
  margin: 0;
  font: 11px/1.6 var(--nsf-mono);
  color: rgba(232, 228, 218, 0.8);
}
.nslm-review-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.nslm-review-tag {
  font: 9px var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.85);
  border: 1px solid var(--nsf-glass-border);
  border-radius: 4px;
  padding: 1px 5px;
}

.nslm-review-insp-sec { display: flex; flex-direction: column; gap: 7px; }
.nslm-review-insp-sec .nsf-eyebrow { font-size: 8.5px; letter-spacing: 0.16em; }

.nslm-review-rank-list { display: flex; flex-direction: column; gap: 7px; }
.nslm-review-rank-ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.nslm-review-rank-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  border: 0;
  border-radius: 5px;
  background: transparent;
  padding: 3px 7px;
  font: 10px var(--nsf-mono);
  color: var(--nsf-haze);
  cursor: pointer;
  text-align: left;
}
.nslm-review-rank-row:hover { background: rgba(255, 255, 255, 0.06); color: var(--nsf-bone); }
.nslm-review-rank-row:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -2px; }
.nslm-review-rank-row.on {
  background: rgba(var(--nsl-amber-rgb), 0.13);
  color: var(--nsf-bone);
  box-shadow: inset 0 0 0 1px rgba(var(--nsl-amber-rgb), 0.4);
}
.nslm-review-rank-n {
  flex: none;
  min-width: 20px;
  color: var(--nsf-bone);
  font-weight: 600;
}
.nslm-review-rank-seq { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nslm-review-rank-score { flex: none; color: var(--nsf-bone); font-weight: 600; }
.nslm-review-rank-w { flex: none; color: var(--nsf-supported); }
.nslm-review-rank-gap {
  padding: 0 7px;
  font: 10px var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.5);
}
.nslm-review-rank-note,
.nslm-review-rank-empty {
  font: 9.5px/1.6 var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.72);
}
.nslm-review-rank-note b { color: var(--nsf-bone); font-weight: 600; }

.nslm-review-clusterline {
  margin-top: 10px;
  font: 10px var(--nsf-mono);
  color: rgba(181, 126, 220, 0.9);
}

/* The line that has to be on every surface showing a mark. Quiet, but
   never absent — the whole shortlist is only honest because this says
   what it is. */
.nslm-review-localnote {
  margin-top: 2px;
  padding-top: 9px;
  border-top: 1px solid var(--nsf-glass-border);
  font: 9.5px/1.65 var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.72);
}
.nslm-review-localnote b { color: var(--nsf-bone); font-weight: 600; }

.nslm-review-rubric { display: flex; flex-direction: column; gap: 10px; }
.nslm-review-rubric-note {
  font: 9.5px/1.65 var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.75);
}
.nslm-review-rubric-note b { color: var(--nsf-bone); font-weight: 600; }

.nslm-review-notes { display: flex; flex-direction: column; gap: 8px; }
.nslm-review-notes-head { display: flex; align-items: center; gap: 8px; }
.nslm-review-notes-head .nsf-eyebrow { font-size: 8.5px; letter-spacing: 0.16em; }
.nslm-review-note-input {
  width: 100%;
  min-height: 150px;
  resize: vertical;
  box-sizing: border-box;
  border: 1px solid var(--nsf-glass-border);
  border-radius: 7px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--nsf-bone);
  font: 11px/1.6 var(--nsf-mono);
  padding: 9px 10px;
}
.nslm-review-note-input::placeholder { color: rgba(var(--nsl-haze-rgb), 0.5); }
.nslm-review-note-input:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -1px; }
