/* ==========================================================================
   NITRATE — THE WALL (poster grid)  +  THE SHEET (title detail panel)

   Owned by the wall/sheet agent.  Consumes tokens.css, never redefines a token.

   Why the sheet lives in this file: index.html links exactly three stylesheets
   (tokens / screener / wall) and this agent owns no sheet.css.  Every selector
   here is prefixed .nw- (wall) or .ns- (sheet) so nothing can collide with
   screener.css.

   THE COLUMN-WIDTH PROBLEM, and the measured answer.
   Plex art is not one aspect ratio.  Fixing column WIDTH gives ragged heights
   and captions that never line up, so instead the poster HEIGHT is fixed and
   the column is as wide as the widest poster.  Measured over all 2728 cached
   posters (Pillow, at the cached height of 300px):

        200px wide  2653  (97.99%)   <- 2:3, the mode
        201-212px     47  ( 1.79%)   cumulative 99.78%
        215-253px      6  ( 0.22%)
        landscape     14            BIF mosaics / video frames, up to 720px

   --nw-ar is therefore .72 (216px at h=300): every one of the 99.78% sits in
   its column uncropped with room to spare, and six freak-wide posters plus the
   14 landscape ones cannot inflate 2733 columns.  Those 20 get .is-wide
   (object-fit:cover) at render time — clipped, never letterboxed, never
   reflowing.  Taking the widest at RUNTIME instead is what the first version
   did, and it was wrong: one 240x300 poster in the opening screenful pushed
   the ratio to .80 and grew every column by 24px.  wall.js logs once if art
   above the cap turns up, so a future rebuild can be re-measured.
   ========================================================================== */

/* -------------------------------------------------------------------- wall */
.nw {
  display: flex;
  flex-direction: column;
  min-height: 0;

  --nw-ar: .72;                       /* widest poster, height-normalised     */
  --nw-h: 300px;                      /* poster height — the density lever    */
  --nw-w: calc(var(--nw-h) * var(--nw-ar));
  --nw-gap: 18px;
  --nw-mast: 68px;                    /* masthead height, measured in JS      */
}
.nw[data-density="2"] { --nw-h: 208px; --nw-gap: 14px; }
.nw[data-density="3"] { --nw-h: 132px; --nw-gap: 10px; }

/* ---- control bar --------------------------------------------------------- */
.nw-bar {
  position: sticky;
  top: var(--nw-mast);
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  padding: 7px var(--pad);
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}

.nw-grp { display: flex; align-items: center; gap: 5px; }

.nw-lbl {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}

.nw-bar select,
.nw-bar input {
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 3px 5px;
  color: var(--ink);
  background: var(--ground);
  border: var(--hair) solid var(--rule);
  max-width: 148px;
}
.nw-bar select:hover,
.nw-bar input:hover { border-color: var(--muted); }
.nw-bar input { width: 148px; }
.nw-bar select[data-on="1"] { color: var(--accent); }

/* sort + density: the same underline language as the masthead's view buttons */
.nw-tab {
  border: 0;
  padding: 4px 7px 3px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  box-shadow: inset 0 -2px 0 transparent;
}
.nw-tab:hover { color: var(--ink); outline: var(--hair) solid var(--rule); }
.nw-tab[aria-pressed="true"] { color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); }

/* KIND — three values, so they are cells you can see and press, not a menu you
   have to open to read.  Two existing languages, no third one: the box is a
   hairline the same weight and colour as the <select> boxes beside it (KIND is
   a filter, and it should sit on that line), and the cells inside are .nw-tab
   exactly as the sort tabs are — same size, same tracking, same accent
   underline when pressed.  The only new part is the count, which is the whole
   point of taking it out of the dropdown.  Several cells can be lit at once:
   that is multi-select showing its work. */
.nw-seg {
  display: inline-flex;
  align-items: stretch;
  max-width: 100%;
  border: var(--hair) solid var(--rule);
  border-bottom: 0;                     /* the cells draw it — see below */
}
/* deliberately no :hover on the box — a select lightens because the whole box
   is one control, but this box is four, and lighting three of its sides while
   the cell-drawn bottom edge stayed put looked like a rendering fault.  Each
   cell carries the sort tabs' own hover instead. */
/* The box's bottom edge belongs to the CELLS, not to the box: a lit cell's
   accent underline IS its bottom edge, exactly as it is on a sort tab.  Keeping
   the border on the box instead left a grey hairline sitting directly under
   every orange one — a doubled line, and the one thing that made this read as a
   different control from the tabs two groups over.  The extra padding-bottom
   is the border's 1px, given back so the box still stands the same height as
   the selects beside it. */
.nw-seg .nw-tab {
  padding: 3px 7px;
  box-shadow: inset 0 calc(-1 * var(--hair)) 0 var(--rule);
}
.nw-seg .nw-tab[aria-pressed="true"] { box-shadow: inset 0 -2px 0 var(--accent); }
.nw-seg .nw-tab + .nw-tab { border-left: var(--hair) solid var(--rule); }
.nw-seg .nw-tab:hover { outline-offset: -1px; }      /* hover ring stays inside the box */
/* .nw-tab's hover outline (0,2,0) outranks the global :focus-visible (0,1,0),
   so a cell arrowed to while the pointer happens to rest on the group would
   show the quiet grey ring instead of the accent one.  Restate it here, inside
   the box so the ring cannot spill onto a neighbouring cell. */
.nw-seg .nw-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* nothing behind this cell under the current filters */
.nw-seg .nw-tab.is-zero { opacity: .38; }
.nw-seg .nw-tab.is-zero:hover { opacity: .8; }

.nw-n {
  margin-left: 5px;
  font-style: normal;
  font-variant-numeric: tabular-nums;
  font-size: 9.5px;
  letter-spacing: 0;
  color: var(--muted);
}
.nw-tab[aria-pressed="true"] .nw-n { color: var(--accent); }

.nw-dir {
  min-width: 26px;
  padding: 3px 6px 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-2);
  border: var(--hair) solid var(--rule);
}
.nw-dir:hover { color: var(--accent); }

.nw-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 2px 4px 2px 7px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent);
  outline: var(--hair) solid var(--accent);
}
.nw-chip button { border: 0; color: inherit; font-family: var(--font-mono); padding: 0 3px; }
.nw-chip[hidden] { display: none; }

.nw-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--ink-2);
  white-space: nowrap;
}
.nw-count b { color: var(--ink); font-weight: 700; }
.nw-count b.crit { color: var(--crit); }
.nw-count .sep { color: var(--rule); padding: 0 6px; }

/* ---- the grid ------------------------------------------------------------ */
.nw-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--nw-w));
  justify-content: space-evenly;      /* leftover space shared, not dumped right */
  align-content: start;
  gap: var(--nw-gap);
  padding: calc(var(--nw-gap) + 4px) var(--pad) 72px;
}

.nw-tile {
  display: block;
  width: var(--nw-w);
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
  color: inherit;
  outline-offset: 3px;
}
.nw-tile:hover { outline: var(--hair) solid var(--accent); }   /* outline, never border */
.nw-tile:hover .nw-t { color: var(--accent); }

.nw-shot {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: var(--nw-h);
}

.nw-shot img {
  height: var(--nw-h);
  width: auto;
  max-width: 100%;
  background: var(--tint, var(--panel-2));   /* the poster's own dominant tint */
  display: block;
}
.nw-shot img.is-wide { width: 100%; object-fit: cover; }

/* no art on disk (5 titles): a plate, not a broken image */
.nw-noart {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--nw-h) * .6667);
  height: var(--nw-h);
  padding: 8px;
  background: var(--panel-2);
  outline: var(--hair) solid var(--rule);
  outline-offset: -1px;
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.35;
  text-align: center;
  color: var(--muted);
  overflow: hidden;
}

/* critical only — 23 titles.  A warn strip would paint 82% of the wall. */
.nw-sev {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: var(--crit);
}

.nw-cap { padding: 6px 1px 0; }

.nw-t {
  font-size: 12.5px;
  line-height: 1.25;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.nw-m {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 2px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 10px;
  color: var(--muted);
}
.nw-m i { font-style: normal; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nw-m i.hot { color: var(--accent); flex: none; }   /* the field you sorted by */

.nw[data-density="3"] .nw-cap { display: none; }    /* contact sheet: art only */
.nw[data-density="2"] .nw-t { font-size: 11.5px; }
.nw[data-density="2"] .nw-m { font-size: 9.5px; }

.nw-empty {
  padding: 60px var(--pad);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ==========================================================================
   THE WALL IN THE HAND  (<= 760px, the MOBILE tier)

   2026-08-11, MOBILE_SPEC.md: this block used to break at 720px.  It is now
   760 — the house mobile tier — and wall.js's own PHONE_Q moved with it, so
   the CSS and the JS cannot disagree about where the phone is.  They MUST
   move together: the JS decides the density labels ("3 across", not "300"),
   and delivers the wall a chunk at a time instead of all 2,889 tiles.  A band
   where one believed it was a phone and the other did not is a wall labelled
   for a desk laid out for a hand.  Nothing above 760 moved.

   THE MILLION-PIXEL BUG, and what it actually was.
   The desktop wall fixes the poster HEIGHT and lets the column be as wide as
   the widest poster (216px at h=300).  That is right on a desk and fatal in a
   hand: 390px of screen minus a 32px gutter is 358px, which fits exactly ONE
   216px column, so 2,827 titles stacked into a single file 1,007,179px tall.
   Not slow — the images lazy-load — but not a document either: the scrollbar
   thumb was one pixel of travel per 1,200px of catalogue.

   So on a phone the model inverts.  The COLUMN COUNT is fixed and the poster
   is whatever fraction of the gutterless width that leaves, which is the only
   way a 360px screen and a 430px screen both come out looking deliberate.
   Density stops meaning "how tall is a poster" — a number nobody can act on
   at this size — and starts meaning "how many across", which is the thing you
   are actually choosing.  wall.js relabels the FRAME cells to match; a control
   that says 300 next to a 179px poster is a control that lies.

     density 1   3 across   poster + title + the one figure you sorted by
     density 2   4 across   poster + title
     density 3   5 across   art only — the contact sheet

   Height is still the whole game, so the caption is rationed rather than
   scaled: at 88px wide a title is an ellipsis with a hint of a word, and the
   second mono cell (the one you did NOT sort by) is 14px of row height buying
   nothing.  It goes.

   THE BAR WAS HALF THE SCREEN.  Nine control groups at a 44px tap height
   stacked to SIX rows — 420px of an 844px phone, measured — so the wall opened
   on a control panel with the first poster below the fold.  On a poster grid
   that is the wrong thing to lead with.  So the bar splits the way the
   Screener's toolbar already does, into what you steer with and what you set:

     always up       FIND · KIND · SORT · the count
     behind REFINE   GENRE · DRIVE · RES · FLAG · ACROSS

   FIND and KIND are how you reach a title; SORT is the one control that
   changes which poster is top-left, and it is the control John hit the
   restored-sort bug on, so it stays where it can be seen.  The four <select>
   filters and the column count are settings — you touch them once and browse
   for ten minutes.  REFINE carries a count of what is active, so a filter can
   never hide inside a closed drawer, and the split is gated at this tier only:
   on a desk the toggle does not exist and every group is up, unchanged.
   ========================================================================== */
@media (max-width: 760px) {
  .nw {
    --nw-gap: 6px;
    --nw-cols: 3;
    --nw-gut: 10px;              /* the wall trades page gutter for poster */
  }
  .nw[data-density="2"] { --nw-cols: 4; --nw-gap: 5px; }
  .nw[data-density="3"] { --nw-cols: 5; --nw-gap: 4px; }

  /* ---- the bar -----------------------------------------------------------
     It is NOT sticky here.  Nine control groups at a 44px tap height is a
     260-400px panel, and pinning that to the top of an 844px phone leaves
     half a screen of catalogue behind a control surface you touch twice a
     session.  It sits at the head of the wall and scrolls away; the document
     below it is now short enough (see wall.js) that coming back is one flick.
     Desktop keeps its sticky bar untouched. */
  .nw-bar {
    position: static;
    gap: 7px 10px;
    padding: 8px var(--nw-gut) 9px;
  }
  .nw-grp { gap: 6px; min-width: 0; }
  .nw-count { margin-left: 0; width: 100%; font-size: 12px; padding-top: 1px; }

  /* labels and figures come up off the floor — 9.5px mono is a desk size */
  .nw-lbl { font-size: 11px; letter-spacing: .1em; }
  .nw-n { margin-left: 5px; font-size: 11px; }

  /* every control in the bar is a 44px target.
     ⚠️ 16px, and it is not a type choice.  mobile.css closes the iOS zoom trap
     with a (0,4,1) selector for <input> — which beats `.nw-bar input`'s 11px —
     but its rule for <select> is the bare element, (0,0,1), which `.nw-bar
     select` outranks.  Left at 12.5px the four filter menus were the one place
     on this page that still zoomed Safari in on focus and never zoomed back.
     Per spec §10.5 the box is what absorbs it: the min-height was already
     here, and the flex-basis below keeps two of them to a line regardless. */
  .nw-bar input,
  .nw-bar select {
    max-width: none;
    min-height: 44px;
    padding: 4px 8px;
    font-size: 16px;
  }
  .nw-bar input { flex: 1 1 150px; width: auto; min-width: 0; }
  .nw-bar select { flex: 1 1 108px; min-width: 0; }
  /* FIND gets a line to itself — shared only with REFINE, which is the one
     thing that earns a place beside it; the four <select> filters pair up two
     to a line rather than being squeezed to 110px each beside their label */
  .nw-grp:has(> input) { flex: 1 1 100%; flex-wrap: nowrap; }
  .nw-grp:has(> select) { flex: 1 1 42%; flex-wrap: nowrap; min-width: 0; }

  /* ---- REFINE: the disclosure ---------------------------------------------
     One tab-shaped button on the FIND line, and the groups it owns stand down
     until it is pressed.  It is .m-only, so it is not in the desk DOM's way at
     all; the groups it hides are only ever hidden by THIS rule, at THIS tier,
     which is why nothing above 760 can be affected by it.
     The count is the honesty clause: a closed drawer with two filters inside
     it says "REFINE 2" in accent, so the wall can never appear to be showing
     you everything when it is not. */
  .nw-refine {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 4px 12px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-2);
    background: var(--ground);
    border: var(--hair) solid var(--rule);
  }
  .nw-refine[aria-expanded="true"] { color: var(--ink); background: var(--panel-2); }
  .nw-refine .nw-n { margin-left: 0; font-size: 11px; }
  .nw-refine[data-on="1"] .nw-n { color: var(--accent); }
  .nw-bar[data-refine="off"] .nw-grp.is-adv { display: none; }

  .nw-tab {
    min-width: 44px;
    min-height: 44px;
    padding: 4px 10px;
    font-size: 12px;
    letter-spacing: .06em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* A 44px box with the accent rule welded to its floor leaves the underline
     hanging 14px clear of the word it is underlining, which reads as a
     rendering fault rather than a selection.  The BOX grows to a thumb; the
     MARK stays where the type is.  Only the free-standing tabs (SORT, ACROSS)
     move — a KIND cell's bottom edge is the segment box's own border and has
     to stay on the floor. */
  .nw-grp > .nw-tab { position: relative; box-shadow: none; }
  .nw-grp > .nw-tab[aria-pressed="true"]::after {
    content: "";
    position: absolute;
    left: 7px; right: 7px; bottom: 11px;
    height: 2px;
    background: var(--accent);
  }
  /* SORT and ACROSS keep one line each and scroll inside themselves if a
     narrower phone cannot hold them — the page never scrolls sideways.
     The overflow itself is .m-scroll-x, added to these two groups in wall.js:
     it is the sanctioned container for anything wider than the screen and it
     already carries overflow-x, overscroll-behavior-x: contain and min-width:0.
     Only the flex behaviour and the hidden scrollbar are local — mobile.css
     cannot know that these particular boxes are single rows of tabs.  Nothing
     here snaps, so no scroll-padding-left is owed (spec §4.7). */
  .nw-grp.m-scroll-x {
    flex: 1 1 100%;
    flex-wrap: nowrap;
    scrollbar-width: none;
  }
  .nw-grp.m-scroll-x::-webkit-scrollbar { height: 0; }
  .nw-grp > .nw-lbl { flex: 0 0 auto; }

  /* KIND keeps its label on the same line as its four cells rather than
     spending a whole row on the word */
  .nw-grp:has(> .nw-seg) { flex: 1 1 100%; flex-wrap: nowrap; }
  .nw-seg { flex-wrap: nowrap; flex: 1 1 auto; min-width: 0; }
  .nw-seg .nw-tab { padding: 4px 7px; letter-spacing: .04em; flex: 1 1 auto; min-width: 0; }
  .nw-dir { min-width: 44px; min-height: 44px; padding: 0 8px; font-size: 13px; flex: 0 0 auto; }
  .nw-chip { min-height: 44px; padding: 2px 4px 2px 9px; font-size: 11px; }
  .nw-chip button { min-width: 40px; min-height: 40px; font-size: 13px; }

  /* ---- the grid ---------------------------------------------------------- */
  .nw-grid {
    grid-template-columns: repeat(var(--nw-cols), minmax(0, 1fr));
    justify-content: stretch;
    gap: var(--nw-gap);
    padding: var(--nw-gap) var(--nw-gut) 28px;
  }
  .nw-tile { width: auto; outline-offset: 2px; }

  /* width now comes from the track, so the box carries the 2:3 and the art is
     fitted inside it against its own dominant tint — the 47 posters a hair
     wider than 2:3 letterbox by two or three pixels onto their own colour,
     which is invisible, and the 20 freaks keep the cover treatment they
     already had rather than squashing 2,800 columns to suit them. */
  .nw-shot {
    height: auto;
    aspect-ratio: 2 / 3;
    align-items: stretch;
  }
  .nw-shot img {
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
  .nw-shot img.is-wide { object-fit: cover; }
  .nw-noart { width: 100%; height: 100%; padding: 6px; font-size: 9.5px; }

  .nw-cap { padding: 5px 1px 0; }
  .nw-t { font-size: 12.5px; line-height: 1.2; }
  .nw-m { margin-top: 1px; font-size: 11px; }
  /* One figure, not two: at 119px the second cell is 16px of row height buying
     an ellipsis.  Keep the one you sorted by — and when you sorted by TITLE,
     where nothing is hot, keep the year, which is the caption's default fact. */
  .nw-m:has(.hot) i:not(.hot) { display: none; }
  .nw-m:not(:has(.hot)) i + i { display: none; }

  .nw[data-density="2"] .nw-t { font-size: 11.5px; }
  .nw[data-density="2"] .nw-m { display: none; }
  /* density 3 already drops the whole caption */

  .nw-empty { padding: 44px var(--nw-gut); }

  /* ---- SHOW MORE (wall.js) ----------------------------------------------
     The wall is delivered a screenful at a time here, so this is the foot of
     the document and has to read as one: full width, a real target, and it
     says how far in you are because the scrollbar no longer can. */
  .nw-more-wrap {
    grid-column: 1 / -1;
    display: block;
    padding: 14px 0 4px;
    border-top: var(--hair) solid var(--rule);
  }
  .nw-more-btn {
    display: block;
    width: 100%;
    min-height: 48px;
    padding: 6px 12px;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink);
    background: var(--panel);
    border: var(--hair) solid var(--rule);
  }
  .nw-more-btn:hover { color: var(--accent); border-color: var(--accent); }
  .nw-more-n {
    display: block;
    margin-top: 3px;
    font-size: 11px;
    letter-spacing: .06em;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
  }
}

/* The NARROW tier.  Three across is not negotiable — a two-across wall is a
   list with pictures — so on a phone the gutter goes before the grid does.
   Folded up from 379px (2026-08-11, MOBILE_SPEC.md): 430 is "phone", not
   "small phone", and the 380-430 band was spending 4px more of a 430px screen
   on margin than a 360px screen was.  Two pixels of column each way. */
@media (max-width: 430px) {
  .nw { --nw-gut: 8px; --nw-gap: 5px; }
  .nw-t { font-size: 12.5px; }
}

/* ---- view transitions ---------------------------------------------------- */
/* the poster morphs into the sheet's hero; different aspect ratios cross-fade
   without letterboxing because the snapshots are replaced elements. */
::view-transition-old(nitrate-hero),
::view-transition-new(nitrate-hero) {
  object-fit: cover;
  height: 100%;
  overflow: hidden;
}
::view-transition-group(nitrate-hero) { animation-duration: .3s; z-index: 5; }

/* ------------------------------------------------------------------- sheet */
/* the shell owns #sheet's frame (fixed, right, min(560px,100vw), panel bg). */

.ns-scrim {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: rgba(0, 0, 0, .42);
  border: 0;
  padding: 0;
  cursor: default;
}
.ns-scrim[hidden] { display: none; }

.ns-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px var(--pad);
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}
.ns-kind {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
}
.ns-close {
  margin-left: auto;
  padding: 3px 8px 2px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
  border: var(--hair) solid var(--rule);
  white-space: nowrap;
}
.ns-close:hover { color: var(--accent); border-color: var(--accent); }

.ns-body { padding: 0 var(--pad) 80px; }

/* ---- hero ---------------------------------------------------------------- */
.ns-hero {
  display: flex;
  gap: 14px;
  padding: 16px 0 14px;
  border-bottom: var(--hair) solid var(--rule);
}
.ns-hero-art { flex: none; width: 132px; }
.ns-hero-art img {
  width: 132px;
  height: 198px;
  object-fit: cover;
  background: var(--tint, var(--panel-2));
  display: block;
}
.ns-hero-art .nw-noart { width: 132px; height: 198px; }
.ns-hero-txt { min-width: 0; align-self: flex-start; }
.ns-title { font-size: 21px; line-height: 1.08; color: var(--ink); overflow-wrap: anywhere; }
.ns-sub {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--ink-2);
}
.ns-sub .sep { color: var(--rule); padding: 0 6px; }
.ns-tag {
  margin-top: 8px;
  font-size: 12px;
  font-style: italic;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.ns-ids { margin-top: 8px; font-family: var(--font-mono); font-size: 10px; color: var(--muted); }
.ns-ids a { color: var(--cool); }

/* ---- sections ------------------------------------------------------------ */
.ns-sec { padding: 14px 0; border-bottom: var(--hair) solid var(--rule); }
.ns-sec:last-child { border-bottom: 0; }
.ns-h {
  margin-bottom: 9px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 10.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
.ns-h b { color: var(--ink-2); font-weight: 800; }

/* ratings — three cells, mono, tabular */
.ns-rate { display: flex; gap: 0; margin-bottom: 12px; }
.ns-rate div {
  flex: 1 1 0;
  padding: 6px 10px 5px 0;
  border-right: var(--hair) solid var(--rule);
}
.ns-rate div:last-child { border-right: 0; }
.ns-rate span {
  display: block;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}
.ns-rate b {
  display: block;
  margin-top: 1px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 19px;
  font-weight: 700;
  color: var(--ink);
}
.ns-rate b.none { color: var(--rule); }

.ns-kv { display: grid; grid-template-columns: 92px 1fr; gap: 3px 12px; }
.ns-kv dt {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 2px;
}
.ns-kv dd {
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}
.ns-kv dd.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: left;
}

.ns-syn { font-size: 13px; line-height: 1.55; color: var(--ink-2); }

.ns-people { font-size: 12.5px; line-height: 1.5; color: var(--ink-2); }
.ns-people p { margin: 0 0 6px; }
.ns-people b {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 400;
  color: var(--muted);
  margin-right: 7px;
}

/* ---- more like this ------------------------------------------------------ */
.ns-like { display: flex; flex-direction: column; }
.ns-like-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  margin: 0;
  padding: 6px 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-2);
  text-align: left;
  cursor: pointer;
}
.ns-like-row:last-child { border-bottom: 0; }
.ns-like-row:hover,
.ns-like-row:focus-visible { color: var(--accent); }
.ns-like-y {
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.ns-like-k {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---- flags --------------------------------------------------------------- */
.ns-flag {
  display: grid;
  grid-template-columns: 132px 1fr;
  gap: 2px 10px;
  padding: 5px 0;
  border-top: var(--hair) solid var(--rule);
}
.ns-flag:first-of-type { border-top: 0; }
.ns-flag code {
  font-size: 10px;
  letter-spacing: .06em;
  overflow-wrap: anywhere;
}
.ns-flag p { margin: 0; font-size: 12px; color: var(--ink-2); }
.ns-flag[data-sev="crit"] code { color: var(--crit); }
.ns-flag[data-sev="warn"] code { color: var(--warn); }
.ns-flag[data-sev="info"] code { color: var(--muted); }

/* ---- storage / files ----------------------------------------------------- */
.ns-drives { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.ns-drive {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 7px 2px;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--ink);
  outline: var(--hair) solid var(--rule);
}
.ns-drive i { font-style: normal; font-size: 9.5px; color: var(--muted); letter-spacing: .08em; }

.ns-season { border-top: var(--hair) solid var(--rule); }
.ns-season > summary {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--ink-2);
}
.ns-season > summary::-webkit-details-marker { display: none; }
.ns-season > summary::before { content: "+"; color: var(--muted); width: 10px; }
.ns-season[open] > summary::before { content: "\2212"; }
.ns-season > summary:hover { color: var(--accent); }
.ns-season > summary b { color: var(--ink); font-weight: 700; letter-spacing: .08em; }
.ns-season > summary .sep { color: var(--rule); padding: 0 3px; }

.ns-file { padding: 8px 0 9px; border-top: var(--hair) solid var(--rule); }
.ns-file:first-child { border-top: 0; }
/* The row a deep-link landed on (recent-episodes rail -> this episode). Fades
   after the eye has found it; a permanent highlight would still be shouting on
   the next visit. ⚠️ Lives here because wall.css owns .ns-*, not sheet.js. */
.ns-file.is-pointed {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px;
  animation: ns-point 4.5s ease forwards;
}
@keyframes ns-point { 0%, 55% { outline-color: var(--accent); } 100% { outline-color: transparent; } }
@media (prefers-reduced-motion: reduce) { .ns-file.is-pointed { animation: none; } }
.ns-file-hd { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.ns-file-hd .ns-drive { flex: none; }
.ns-ep {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--accent);
  flex: none;
}
.ns-name { font-size: 12.5px; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }

.ns-nums {
  display: flex;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--ink-2);
}
.ns-nums span { white-space: nowrap; }
.ns-nums span + span::before { content: "\00b7"; color: var(--rule); padding: 0 6px; }
.ns-nums .over { color: var(--warn); }

.ns-path {
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.ns-strm {
  display: grid;
  grid-template-columns: 46px 1fr;
  gap: 0 8px;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-2);
}
.ns-strm > b {
  font-weight: 400;
  letter-spacing: .1em;
  color: var(--muted);
}
.ns-strm u { text-decoration: none; }
.ns-strm u + u::before { content: "\00b7"; color: var(--rule); padding: 0 5px; }
.ns-strm .df { color: var(--ink); }
.ns-strm .fo { color: var(--warn); }
.ns-strm em { font-style: normal; color: var(--muted); }

.ns-more {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2);
  border: var(--hair) solid var(--rule);
}
.ns-more:hover { color: var(--accent); border-color: var(--accent); }

.ns-note {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: .08em;
  color: var(--muted);
}

/* ==========================================================================
   THE SHEET IN THE HAND  (<= 760px, the MOBILE tier — folded up from 720)

   THE FORM, and why it is not a bottom sheet.  On a phone this is not a panel
   beside something, it IS the screen: the shell gives it every pixel but a
   44px dismiss column down the left (index.html), and that is the right shape
   for what is in it.  A bottom sheet earns its keep when the content is short
   enough to peek at over the page you came from — this one is a hero, four
   ratings, seven facts, a synopsis, the flags, every season and every FILE
   with its path and its streams.  A peek height would put all of that behind
   a drag, and the thing underneath is a poster grid that has already handed
   over its subject.  A bottom sheet would also mean a drag-to-dismiss gesture
   and a grabber, which is a borrowed app-shell pattern (§7), and it would mean
   this stylesheet redefining the shell's #sheet frame from a view file.
   What a bottom sheet WOULD have given, and this now has, is a way out that
   lives at the bottom edge under the thumb.  See below.

   THE CLOSE, twice.  It began as 67 x 21.5px in the top-right corner — the
   smallest control on the site, in the one corner of a 390 x 844 screen a
   thumb cannot reach, and the sheet's only exit from inside it.  It became a
   46px pill parked bottom-right, which fixed the reach and the size but left
   it floating ON the text: measured mid-synopsis it sat across "help her
   revisit her" with a 3px halo, i.e. a control occluding the sentence it was
   sitting on.  It is now the sheet's FOOT: a full-width bar on the bottom
   edge, the same 52px, the same panel, the same hairline as the shell's own
   destination bar — which the sheet covers (z-60 over z-40), so while a sheet
   is open the bottom edge of the phone means CLOSE and nothing else.  Text
   scrolls UNDER a solid bar instead of behind a floating one, the target is
   the width of the sheet rather than 97px of it, and it keeps the word.
   It is fixed rather than sticky because .ns-head — its DOM parent — is
   itself sticky at the top; left is var(--tap) because at this tier the shell
   pins the sheet to the right edge at exactly `calc(100% - var(--tap))`.

   THE FIGURES.  Everything mono came up to 11px and everything read as prose
   to 12.5px.  A drive path or a flag code at 9.5px on a phone is a decoration
   of a fact, not the fact.
   ========================================================================== */
@media (max-width: 760px) {
  .ns-head { padding: 9px var(--pad); gap: 10px; }
  .ns-kind { font-size: 11px; letter-spacing: .14em; }

  .ns-close {
    position: fixed;
    left: var(--tap); right: 0; bottom: 0;
    z-index: 5;
    margin-left: 0;
    min-height: 52px;
    padding: 0 var(--m-pad);
    padding-bottom: var(--safe-b);
    padding-right: max(var(--m-pad), var(--safe-r));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    font-size: 12.5px;
    letter-spacing: .14em;
    color: var(--ink);
    background: var(--panel);
    border: 0;
    border-top: var(--hair) solid var(--rule);
  }
  .ns-close:hover { color: var(--accent); border-color: var(--rule); }

  /* clear the foot, and the hardware inset that is inside its own height */
  .ns-body { padding-bottom: calc(72px + var(--safe-b)); }

  /* ---- hero ---- */
  /* 132px of poster against a 346px sheet leaves 186px for a title that is
     often five words long; 108 gives it back a fifth of a line.

     THE ACTION ROW GETS THE WHOLE WIDTH.  WATCH / PLAY ON PLEX / STREAM are
     built inside .ns-hero-txt, so beside a poster they were laid out in a
     198px column and stacked into three ragged lines pushed against the right
     edge — the primary act of the entire site ("open a title and watch it",
     spec §5) rendered as an afterthought in the margin.  The hero becomes a
     two-column GRID and .ns-hero-txt `display: contents`, which promotes the
     title, the sub, the tagline, the ids and the action row to items of the
     hero itself: everything textual stays in column 2 beside the poster, and
     the action row alone spans both columns, under the poster, full width.
     The poster spans four rows because four is how many text lines the hero
     can have (title, sub, tagline, ids); an absent one collapses to zero and
     the row after the poster is where the actions land either way.
     ⛔ Do not "simplify" this back to flex — the row cannot escape its own
     parent's column in a flex layout, and moving it in sheet.js would move it
     on the desk too, where it is right where it belongs. */
  .ns-hero {
    display: grid;
    grid-template-columns: 108px minmax(0, 1fr);
    column-gap: 12px;
    row-gap: 0;
    align-items: start;
    padding: 14px 0 13px;
  }
  .ns-hero-art { width: 108px; grid-column: 1; grid-row: 1 / span 4; }
  .ns-hero-txt { display: contents; }
  .ns-title, .ns-sub, .ns-tag, .ns-ids { grid-column: 2; }
  .ns-play { grid-column: 1 / -1; margin-top: 12px; }
  .ns-hero-art img,
  .ns-hero-art .nw-noart { width: 108px; height: 162px; }
  .ns-title { font-size: 22px; }
  .ns-sub { font-size: 11.5px; }
  .ns-tag { font-size: 12.5px; }

  /* the ids line is two links reading 60 x 11px.  It becomes a row of real
     targets — the line grows to 44px, which it can afford once per sheet. */
  .ns-ids {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 14px;
    margin-top: 4px;
    font-size: 11.5px;
  }
  .ns-ids a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    text-decoration-color: var(--cool);
  }

  /* ---- sections ---- */
  .ns-h { font-size: 11px; letter-spacing: .12em; }
  .ns-rate b { font-size: 20px; }
  .ns-rate span { font-size: 11px; letter-spacing: .08em; }
  .ns-kv { grid-template-columns: 84px 1fr; gap: 5px 10px; }
  .ns-kv dt { font-size: 11px; letter-spacing: .06em; }
  .ns-kv dd { font-size: 12.5px; }
  .ns-syn { font-size: 13.5px; }
  .ns-people { font-size: 12.5px; }
  .ns-people b { font-size: 11px; }

  /* a 132px code column against a flag description leaves 200px of prose in
     a 4-word column; the code takes its own line instead */
  .ns-flag { grid-template-columns: 1fr; gap: 3px; padding: 8px 0; }
  .ns-flag code { font-size: 11.5px; }
  .ns-flag p { font-size: 12.5px; }

  /* ---- storage ---- */
  .ns-drive { min-height: 30px; padding: 4px 9px; font-size: 11.5px; }
  .ns-drive i { font-size: 11px; }
  .ns-season > summary { min-height: 46px; align-items: center; font-size: 12.5px; }
  .ns-season > summary::before { width: 14px; font-size: 15px; }
  .ns-ep { font-size: 12px; }
  .ns-name { font-size: 12.5px; }
  .ns-nums { font-size: 11.5px; }
  .ns-path { font-size: 11px; }
  .ns-strm { grid-template-columns: 44px 1fr; font-size: 11px; }
  .ns-more { min-height: 44px; font-size: 11.5px; }
  .ns-note { font-size: 11px; }
}
