/* =====================================================================
   Night Library — view modes: the SWITCH, the MEDIUM grid, and the
   shared chrome the five layout modes are built from.

   Each layout mode owns a file of its own (css/nsl-mode-*.css); what
   lives here is what more than one of them needs, so the modes read as
   one instrument rather than five apps that happen to share a tab.

   Two rules carried in from the Library's rendering pass, and they are
   not stylistic:

     · NO backdrop-filter, anywhere. A sticky or floating pane with a
       blur behind it re-rasterises whatever scrolls under it on every
       frame — and under these panes is a grid of live iframes and
       decoding video stills. Opacity reads the same and costs nothing.

     · `contain: layout paint style` on every card-like leaf, and NEVER
       `contain: size` or `content-visibility`. Containment stops a
       thumbnail's load from re-laying-out its neighbours; sizing
       containment would make the engine guess row heights it can
       measure, which is what turns a scroll into a jitter.
   ===================================================================== */

/* The vertical budget a full-height mode may take. One token, so the
   cinema stage, the stream columns and the review desk all agree — and
   so a shell change is one edit rather than five. */
:root {
  --nsl-mode-h: clamp(420px, calc(100vh - 320px), 1180px);
  --nsl-rail-w: 320px;
}

/* ---------------------------------------------------------------------
   The switch itself - the edition's sections.

   Eight destinations a reader moves BETWEEN while comparing (small →
   review → back) should cost one click each way, so they are all on
   screen at once: one quiet row of words across the full width, ruled
   above and below, the current one underlined. It is one tab stop with
   arrow-key movement (the radiogroup contract). Narrow, the glyphs
   return and the row scrolls sideways instead of wrapping.
--------------------------------------------------------------------- */
.nsl-viewswitch {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 4px;
  border-top: 1px solid var(--nsl-rule, var(--nsf-glass-border));
  border-bottom: 1px solid var(--nsl-rule, var(--nsf-glass-border));
  overflow-x: auto;
  scrollbar-width: none;
  flex: none;
}
.nsl-viewswitch::-webkit-scrollbar { display: none; height: 0; }
.nsl-view-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--nsf-haze);
  font: 13px var(--nsf-sans);
  padding: 15px 12px 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.12s ease;
}
.nsl-view-btn:hover { color: var(--nsf-bone); }
.nsl-view-btn:focus-visible { outline: 2px solid var(--nsf-amber); outline-offset: -4px; }
.nsl-view-btn.on { color: var(--nsf-bone); }
.nsl-view-btn.on::after {
  content: '';
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 0;
  height: 2px;
  background: var(--nsf-amber);
}
.nsl-view-glyph { display: none; font-size: 12px; line-height: 1; opacity: 0.8; }

@media (max-width: 980px) {
  .nsl-viewswitch { justify-content: flex-start; }
  .nsl-view-glyph { display: inline; }
}

/* ---------------------------------------------------------------------
   MEDIUM — the same grid, one density up.

   The small card is a contact sheet: recognise it, click it. The medium
   card is the everyday one — a thumbnail big enough to judge, and the
   room to say which model and lane produced it without a hover.
--------------------------------------------------------------------- */
/* 400px, not 330. The number is chosen so the everyday window
   width lands on THREE columns rather than four: at 330 a 1600px screen
   fits four, which is the small grid with bigger gaps — a second density
   that does not read as a second density. Three columns is what makes
   the thumbnail big enough to judge without opening it, which is the
   whole reason this mode exists. Narrower screens fall to two and then
   one on their own. */
.nsl-grid[data-size="md"] {
  grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: 40px 26px;
}
/* The base card's thumb is an aspect-ratio box (16/10), NOT a
   padding-top box — adding padding here stacks on top of the ratio and
   makes the thumb taller than either rule intended. Override the ratio
   itself: 16/9 is the shape of nearly everything this app generates,
   and at three columns it is a picture you can actually judge. */
.nsl-card-md .nsl-thumb { aspect-ratio: 16 / 9; }
.nsl-card-md .nsl-card-cap { padding-top: 15px; }
.nsl-card-md .nsl-name { font-size: 15px; }
.nsl-card-md .nsl-meta-sub { font-size: 12px; }

/* The provenance line. Dim on purpose: it answers "where did this come
   from", which is a question you ask AFTER the picture has interested
   you — it must never compete with the filename. */
.nsl-meta-prov {
  display: block;
  font: 10px var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.72);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------
   Shared mode chrome.
--------------------------------------------------------------------- */

/* The mode's own control row, under the filter toolbar. Each layout
   groups by a different axis and needs a different handful of controls;
   they all land here so the filter bar above stays the one place that
   narrows the POOL, and this stays the one place that arranges it. */
.nsl-modebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--nsf-glass-border);
}
.nsl-modebar-title {
  font: 10px var(--nsf-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--nsf-haze);
}
.nsl-modebar-spacer { flex: 1 1 auto; }
.nsl-modebar-note {
  font: 10px var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.7);
}

/* A pane: the panels the layout modes are assembled from. */
.nsl-pane {
  border: 1px solid var(--nsf-glass-border);
  border-radius: 10px;
  background: var(--nsf-panel-2);
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.nsl-pane-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--nsf-glass-border);
  font: 10px var(--nsf-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nsf-haze);
  flex: none;
}
.nsl-pane-title { color: var(--nsf-bone); font-weight: 600; }
.nsl-pane-count {
  font-size: 10px;
  color: rgba(var(--nsl-haze-rgb), 0.8);
  background: rgba(255, 255, 255, 0.05);
  border-radius: 999px;
  padding: 1px 7px;
}
/* The one scroller inside a pane. `overscroll-behavior: contain` stops
   a column that has hit its end from handing the wheel to the page —
   four side-by-side feeds that each scroll the whole tab when they
   bottom out are unusable. */
.nsl-pane-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}
.nsl-pane-body::-webkit-scrollbar { width: 8px; height: 8px; }
.nsl-pane-body::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.14);
  border-radius: 999px;
}
.nsl-pane-body::-webkit-scrollbar-track { background: transparent; }

/* Tabs, for the modes that stack readings of one artifact. */
.nsl-tabs {
  display: flex;
  gap: 2px;
  padding: 4px 6px;
  border-bottom: 1px solid var(--nsf-glass-border);
  flex: none;
}
.nsl-tab {
  border: 0;
  background: transparent;
  color: var(--nsf-haze);
  font: 10px var(--nsf-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 6px 10px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.12s ease, background 0.12s ease;
}
.nsl-tab:hover { color: #fff; background: rgba(255, 255, 255, 0.05); }
.nsl-tab:focus-visible { outline: 2px solid var(--nsf-haze); outline-offset: -2px; }
.nsl-tab.on { color: #fff; background: rgba(255, 255, 255, 0.1); }

/* The key/value block every mode uses to state provenance. */
.nsl-kv {
  display: grid;
  grid-template-columns: minmax(76px, auto) 1fr;
  gap: 5px 12px;
  font: 11px var(--nsf-mono);
  align-items: baseline;
}
.nsl-kv dt, .nsl-kv .k {
  color: var(--nsf-haze);
  font-size: 9.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
}
.nsl-kv dd, .nsl-kv .v {
  margin: 0;
  color: var(--nsf-bone);
  overflow-wrap: anywhere;
}
/* An absence is a WORD, never a zero and never a blank cell. */
.nsl-kv .v.none, .nsl-none { color: rgba(var(--nsl-haze-rgb), 0.55); font-style: italic; }

/* Metric tiles — score, the bar, novelty. Same rule: a metric that was
   not recorded says so; it does not render as 0. */
.nsl-metrics { display: flex; flex-wrap: wrap; gap: 6px; }
.nsl-metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 66px;
  padding: 5px 9px;
  border: 1px solid var(--nsf-glass-border);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.025);
}
.nsl-metric-k {
  font: 8.5px var(--nsf-mono);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--nsf-haze);
  white-space: nowrap;
}
.nsl-metric-v { font: 600 13px var(--nsf-mono); color: var(--nsf-bone); }
.nsl-metric.good { border-color: rgba(var(--nsl-ok-rgb), 0.42); }
.nsl-metric.good .nsl-metric-v { color: var(--nsf-supported); }
.nsl-metric.mid { border-color: rgba(224, 180, 80, 0.42); }
.nsl-metric.mid .nsl-metric-v { color: var(--nsf-split); }
.nsl-metric.bad { border-color: rgba(212, 87, 107, 0.42); }
.nsl-metric.bad .nsl-metric-v { color: var(--nsf-contradicted); }
.nsl-metric.ungraded .nsl-metric-v {
  color: rgba(var(--nsl-haze-rgb), 0.55);
  font-size: 10px;
  font-weight: 400;
  font-style: italic;
}

/* A tile-sized thumbnail holder, for the rows the layout modes list.
   Same containment discipline as .nsl-card. */
.nsl-tile-thumb {
  position: relative;
  display: block;
  flex: none;
  border-radius: 5px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.35);
  contain: layout paint style;
}
.nsl-tile-thumb .nsl-thumb-glyph { transform: scale(0.8); }

/* The virtualized list's spacers. Height only — nothing paints. */
.nsl-vpad { flex: none; width: 100%; pointer-events: none; }

/* An empty pane still has to say something true. */
.nsl-pane-empty {
  padding: 22px 16px;
  text-align: center;
  font: 11px var(--nsf-mono);
  color: rgba(var(--nsl-haze-rgb), 0.62);
  line-height: 1.6;
}

/* Modes that own the full height stop the outer page from scrolling
   past them — the panes scroll, the page does not. */
.nsl-modeframe {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: var(--nsl-mode-h);
}

/* ---------------------------------------------------------------------
   Framed modes — the ones whose panes scroll instead of the page.

   The shell is one scrollport (`.scroll`, height:100%, overflow-y:auto)
   holding a flex COLUMN (`.nsf-night-screen`, min-height:100%). A mode
   that wants a rail to stay put while the list beside it scrolls has to
   stop the page scrolling first, which means becoming a flex item that
   fills the column and refusing to grow past it.

   `min-height: 0` is the whole trick and it is not optional: a flex
   item defaults to `min-height: auto`, which means "never shrink below
   my content" — so without it the inner scrollers never get a definite
   height to scroll inside, the content pushes the column taller, and
   the page scrolls after all.
--------------------------------------------------------------------- */
.nsl-view.nsl-framed {
  flex: 1 1 auto;
  min-height: 0;
  /* the shell's own min-height:100% would keep it page-tall */
  height: auto;
  display: flex;
  flex-direction: column;
  padding-bottom: 14px;
}
/* Everything around the mode - masthead, sections, toolbar, notices,
   collections, the selection bar - keeps its natural height and never
   shrinks; the mode root (below, more specific) takes what is left. */
.nsl-framed > * { flex: none; }
/* NOT `height: auto` here — that was the bug. .nsl-modeframe carries
   the whole point of a framed mode in its `height: var(--nsl-mode-h)`,
   and resetting it to auto handed the height back to the content: the
   cinema stage, the review desk and the stream columns each grew to ten
   or thirteen thousand pixels, their inner scrollers never got a
   definite height to scroll inside, and the page scrolled instead —
   exactly the thing framing exists to prevent. `flex: 1 1 auto` lets
   the frame take any LEFTOVER column space beyond that budget, and
   `min-height: 0` lets it shrink below its content, which is what makes
   the panes inside it scroll. */
.nsl-framed > .nsl-modeframe,
.nsl-framed > .nsl-mode-root {
  /* `height: var(--nsl-mode-h)` from .nsl-modeframe stands as the
     flex BASIS, which is what makes this work in both worlds. Where the
     rule below gives the night screen a definite height, grow+shrink
     resolve the basis to exactly the leftover space and the page does
     not scroll at all. Where :has() is unavailable, the basis is all
     there is and the mode gets its stated budget instead of the
     content's own height. Either way it never grows to 13 000px.

     The floor is the same escape hatch .nsf-stage uses: squeezed below
     420px the frame stops shrinking and the page scrolls, because a
     220px-tall cinema is worse than a scrollbar. */
  flex: 1 1 auto;
  min-height: 420px;
}

/* The night screen is `min-height: 100%`, not `height`, so its height is
   max(content, viewport) — and a child asking for "the leftover space"
   in a container sized by its content is circular: the frame grows to
   its content, the content grows the column, and nothing is left over.
   Giving the column a DEFINITE height only while a framed mode is on
   screen breaks that cycle, and does it without touching the Night
   screen's own stylesheet or making every other sub-tab pay for it.

   :has() is already load-bearing three rules up the same chain
   (.scroll > .tab-view:has(> .nsf-night-screen)), so this asks nothing
   new of the engine — and the fallback above covers it if it ever does. */
.nsf-night-screen:has(> .nsl-view.nsl-framed) { height: 100%; }
/* A framed mode's toolbar has nothing scrolling under it to stick to. */
.nsl-framed > .nsl-toolbar { position: static; }
/* A framed mode owns the viewport, so the masthead gives back height:
   one line of title, a tighter rule. */
.nsl-framed { padding-top: 22px; }
.nsl-framed > .nsl-masthead { padding-bottom: 16px; }
.nsl-framed > .nsl-masthead .nsl-eyebrow { margin-bottom: 10px; }
.nsl-framed > .nsl-masthead .nsl-title { font-size: clamp(22px, 2.1vw, 32px); -webkit-line-clamp: 1; }
.nsl-framed > .nsl-masthead .nsl-mast-sub { margin-top: 8px; }
.nsl-framed > .nsl-masthead .nsl-note-big { font-size: 15px; margin: 7px 0 4px; }
.nsl-framed > .nsl-verdicts { margin-bottom: 14px; }

/* =====================================================================
   NS UX friction review (2026-09-01) — F1–F14 chrome.
   Kept here rather than in nightshift-field.css so the library's own
   additions live beside the other library chrome. Same rules as above:
   no backdrop-filter, no size containment.
   ===================================================================== */

/* F2 — the human's own thumb, amber like ⬟ kept (both are things a
   person did, not things a judge found) */
.nsl-flag-rated { color: var(--nsf-amber); border-color: rgba(var(--nsl-amber-rgb), 0.5); background: rgba(var(--nsl-amber-rgb), 0.08); }

/* F5 / F10 — a notice that carries an error (role=alert) and a verb */
.nsl-notice.nsl-notice-err {
  color: var(--nsf-contradicted, #D4576B);
  border-color: rgba(212, 87, 107, 0.35);
  background: rgba(212, 87, 107, 0.08);
}
.nsl-notice { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.nsl-notice .nsl-notice-act { margin-left: auto; }

/* F10 — the export while it runs: the moving number and the cancel */
.nsl-export-busy { display: inline-flex; align-items: center; gap: 6px; }
.nsl-export-busy .nsa-act-btn[aria-disabled="true"] { opacity: 0.75; cursor: progress; font-variant-numeric: tabular-nums; }

/* F3 — the no-compute verbs on the bulk bar, and their progress */
.nsl-bulkbar-sep { width: 1px; height: 18px; background: rgba(255, 255, 255, 0.12); margin: 0 2px; }
.nsl-bulkbar-save { color: var(--nsf-amber); }
.nsl-bulkbar-progress {
  display: inline-flex; align-items: center; gap: 8px;
  font: 11px var(--nsf-mono); color: var(--nsf-bone);
  font-variant-numeric: tabular-nums;
}

/* F4 — the export sheet: a small dialog, not a browser confirm */
.nsl-sheet-overlay { z-index: 60; }
.nsl-sheet {
  width: min(92vw, 520px);
  background: var(--nsf-bg, #0b0d12);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 10px;
  padding: 18px 20px;
  color: var(--nsf-bone);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}
.nsl-sheet-title { font: 600 14px var(--nsf-sans, inherit); margin-bottom: 8px; }
.nsl-sheet-body { font-size: 12.5px; line-height: 1.5; color: rgba(232, 228, 218, 0.8); margin-bottom: 14px; }
.nsl-sheet-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.nsl-sheet-acts .nsl-sheet-primary { color: var(--nsf-amber); border-color: rgba(var(--nsl-amber-rgb), 0.5); }

/* F7 — the artifact preview's reach note (the file that owns the
   preview has no stylesheet of its own; the field's .nsa-* rules are
   shared, so the two new spans get their look here) */
.nsa-preview-reach-glyph { color: var(--nsf-amber); padding: 0 2px; }
.nsa-preview-reach { color: var(--nsf-amber); font-size: 10.5px; white-space: nowrap; }
.nsa-preview-reach:empty { display: none; }
.nsa-preview-err { color: var(--nsf-contradicted, #D4576B); max-width: 42ch; white-space: normal; }
