/* ============================================================
   NPJ — Nashville Peoples' Journalism
   "Activist Newsprint" design system
   (fonts are loaded via <link> in the host HTML)
   ============================================================ */
:root {
  /* brand */
  --yellow: #ffec01;
  --yellow-deep: #f2d400;
  --ink: #16140d;
  --ink-soft: #3a362b;
  --paper: #f6f1e4;
  --paper-2: #efe9d8;
  --card: #fffdf6;
  --rule: rgba(22,20,13,0.16);
  --rule-strong: rgba(22,20,13,0.4);
  /* functional (newsprint-muted) */
  --verified: #1f6f4a;   /* accepted / primary source */
  --review: #9a6a12;     /* under review / stale */
  --reject: #b23a26;     /* rejected */
  --data: #2b5f8a;       /* data source */
  /* type */
  --display: 'Anton', 'Arial Narrow', sans-serif;
  --cond: 'Barlow Condensed', 'Arial Narrow', sans-serif;
  --serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --quote: 'Spectral', Georgia, 'Times New Roman', serif;  /* pull-quotes: a light literary serif */
  --mono: 'Spline Sans Mono', ui-monospace, monospace;
  /* spacing measure */
  --measure: 64ch;
}

* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* selection in NPJ yellow */
::selection { background: var(--yellow); color: var(--ink); }

/* ---- primitives ---- */
.np-cond { font-family: var(--cond); }
.np-mono { font-family: var(--mono); }
.np-eyebrow {
  font-family: var(--cond);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 600;
  font-size: 12px;
}
.np-rule { height: 1px; background: var(--ink); border: 0; }
.np-rule-thin { height: 1px; background: var(--rule); border: 0; }

button { font-family: var(--cond); cursor: pointer; }
a { color: inherit; }

/* masthead "from the creators of" credit — quiet until hovered */
.npj-creator img { transition: filter .15s ease; }
.npj-creator:hover .npj-creator-name { text-decoration: underline; text-underline-offset: 2px; }
.npj-creator:hover img { filter: brightness(0.94); }

/* scrollbars */
.np-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.np-scroll::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 0; }
.np-scroll::-webkit-scrollbar-track { background: transparent; }

/* ---- claim affordances (the spine) ---- */
.claim { position: relative; transition: background .15s ease; }
/* The claim only carries an affordance — pointer, underline, focus ring — when the
   transparency layer is on (Standard/Full add `previews-on` to the article). In the
   Clean read the sources are deliberately hidden, so a sourced sentence is inert:
   plain prose with no dotted hint and no tap target that opens nothing. */
.previews-on .claim { cursor: pointer; }
/* keyboard users get a clear focus ring on the claim they're about to open */
.previews-on .claim:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 2px; }

/* clean read (transparency layer on, audit off): low-key dotted underline */
.read-clean .previews-on .claim {
  background: none;
  border-bottom: 1.5px dotted var(--rule-strong);
  padding-bottom: 1px;
}
.read-clean .previews-on .claim:hover { background: rgba(255,236,1,0.35); }

/* audit mode: every claim highlighted + numbered margin tab */
.read-audit .claim {
  background: linear-gradient(transparent 62%, rgba(255,236,1,0.55) 62%);
  border-bottom: 1.5px solid var(--ink);
}
.read-audit .claim:hover { background: rgba(255,236,1,0.85); }

.claim-marker {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  vertical-align: super;
  line-height: 0;
  margin-left: 2px;
  color: var(--ink);
  background: var(--yellow);
  border: 1px solid var(--ink);
  padding: 1px 3px;
  border-radius: 2px;
  user-select: none;
}
.read-clean .claim-marker { background: transparent; border-color: var(--rule); color: var(--ink-soft); }

/* ---- the lede drop cap: a dark serif initial on a brand-yellow block ---- */
/* The opening paragraph's first character is lifted into this floated box (see
   ArticleRead) and the prose wraps to its right. The box stands about three
   lines tall — a bold lede presence — the letter centered in the body serif. */
.np-dropcap-box {
  float: left;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 1.22em;
  height: 1.22em;
  margin: 0.04em 0.26em 0.04em 0;
  font-family: var(--serif);
  font-weight: 600;
  font-size: 3.5em;
  line-height: 1;
  background: var(--yellow);
  color: var(--ink);
}
/* A single capital centers high in its line box — the empty descender space
   rides below the baseline — so nudge the glyph down to sit optically centered
   (em-relative, so it holds at any size). */
.np-dropcap-box > span {
  display: block;
  transform: translateY(0.08em);
}
@media (max-width: 560px) {
  /* a touch smaller so the block never dominates a narrow measure */
  .np-dropcap-box { font-size: 3.0em; }
}

/* the grounded glyph (⊥) — a claim standing on its source: a stem planted on
   solid ground. Used in legends (e.g. the front-page manifesto strip) to mean
   "this mark means the text above it is grounded in an archived source". */
.ground-glyph {
  position: relative;
  display: inline-block;
  width: 15px;
  height: 13px;
  border-bottom: 3px solid var(--yellow);
}
.ground-glyph::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 3px;
  height: 10px;
  background: var(--yellow);
}

/* claim with open suggestions gets a small flag */
.claim[data-sugg="1"]::after {
  content: "";
  position: absolute;
  top: -3px; right: -5px;
  width: 6px; height: 6px;
  background: var(--review);
  border-radius: 50%;
}

/* every span a source backs lights up when you hover that source in the
   ledger / evidence panel — you SEE the precise passages it grounds */
.claim[data-active="1"] {
  background: rgba(255,236,1,0.7) !important;
  border-bottom: 1.5px solid var(--ink);
}
/* clicking one of a source's cited passages scrolls to it and flashes it */
@keyframes claimflash {
  0%, 30% { background: var(--yellow); box-shadow: 0 0 0 4px var(--yellow); }
  100% { background: rgba(255,236,1,0); box-shadow: 0 0 0 4px rgba(255,236,1,0); }
}
.claim-flash { animation: claimflash 1.7s ease-out; border-radius: 2px; }

/* ---- the transparency lens ----
   The reader (and the editor's Preview) can switch on a lens that colours every
   grounded span by HOW it stands, using the SAME vocabulary as the editor's
   Grounding workspace (GroundingWorkspace.jsx proseShade): a yellow fill for a
   grounded/cited claim (⊤ / ⊨), violet for one the author owns (⊢ / ⊨ / ⊩),
   orange + dashed for one that still needs a source (⊥), red for sources that
   disagree (¬). Off, the prose is clean; the class only lands on the article
   while the lens is on, so these rules never touch a normal read. */
.ground-lens .claim[data-ground],
.ground-lens .gowned[data-ground] {
  padding: 0 1px;
  border-radius: 2px;
  transition: background-color .15s ease, filter .15s ease;
}
/* grounded / multiple sources — the cited yellow */
.ground-lens .claim[data-ground="grounded"],
.ground-lens .claim[data-ground="multi"] { background: rgba(255,236,1,.22); border-bottom: 1.5px solid #d8c520; }
/* owned by the author — violet */
.ground-lens .gowned[data-ground="own-analysis"],
.ground-lens .gowned[data-ground="own-account"],
.ground-lens .gowned[data-ground="own-position"] { background: rgba(124,116,222,.18); border-bottom: 1.5px solid #7C74DE; }
/* a void (asserted absence) — a blue family, shaded by how the absence stands:
   shown (point to it) reads solid + strong; located is dashed; inferred (the
   weakest — unrecorded / ambient) is dotted + faint. So the lens shows at a
   glance whether the reader is being shown an absence or told to infer one. */
.ground-lens .gowned[data-ground="absence"] { background: rgba(77,126,168,.14); border-bottom: 1.5px solid #4D7EA8; }
.ground-lens .gowned[data-ground="absence"][data-void="shown"] { background: rgba(47,102,144,.18); border-bottom: 1.5px solid #2F6690; }
.ground-lens .gowned[data-ground="absence"][data-void="located"] { background: rgba(77,126,168,.14); border-bottom: 1.5px dashed #4D7EA8; }
.ground-lens .gowned[data-ground="absence"][data-void="inferred"] { background: rgba(122,147,168,.11); border-bottom: 1.5px dotted #7A93A8; }
/* needs a source — orange, dashed */
.ground-lens .claim[data-ground="needs"] { background: rgba(216,99,46,.18); border-bottom: 1.5px dashed #D8632E; }
/* sources disagree — red */
.ground-lens .claim[data-ground="conflict"] { background: rgba(216,65,44,.18); border-bottom: 1.5px solid #D8412C; }
/* hovering a claim keeps its grounding colour (it doesn't flip to the yellow
   citation-hint of audit/clean mode) — just a touch deeper so it still reads as
   interactive. */
.ground-lens .claim[data-ground]:hover,
.ground-lens .gowned[data-ground]:hover { filter: brightness(.96) saturate(1.18); }
/* the small logic glyph after a span (mirrors the workspace's proseSup); colour
   is set inline per grounding kind. */
.ground-lens .gmark { font-family: var(--mono); font-size: 8.5px; vertical-align: super; line-height: 0; margin-left: 2px; user-select: none; }

/* Owned claims & voids read as clean prose — no resting mark; revealing them all
   at once is the Transparency lens's job. But with Previews on they join the
   hover-to-reveal behaviour the sourced claims and footnotes already have: a help
   cursor says the word carries grounding you can open, and (when the lens isn't
   already colouring everything) a faint hint of its grounding colour on hover
   — violet for the author's own, blue for a void. The card (GroundingPop) reads it
   out. Previews off → fully inert, truly clean. */
/* Hovering an owned assertion, the cursor IS the assertion glyph (⊢, the
   "your analysis" turnstile from citey-states) rather than a generic help "?" —
   the pointer itself names what the word is: the author standing behind a claim.
   SVG data-URI cursor with a white halo so it reads on any background; hotspot
   at the top of the stem (9 4). Falls back to `help` where custom cursors aren't
   honoured. */
.previews-on .gowned { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2228%22%20height%3D%2228%22%20viewBox%3D%220%200%2028%2028%22%3E%3Cpath%20d%3D%22M9%204V24M9%2013H23%22%20fill%3D%22none%22%20stroke%3D%22%23ffffff%22%20stroke-width%3D%225.5%22%20stroke-linecap%3D%22round%22%2F%3E%3Cpath%20d%3D%22M9%204V24M9%2013H23%22%20fill%3D%22none%22%20stroke%3D%22%237C74DE%22%20stroke-width%3D%223%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E") 9 4, help; }
.previews-on:not(.ground-lens) .gowned[data-ground^="own-"]:hover { background: rgba(124,116,222,.16); border-radius: 2px; }
.previews-on:not(.ground-lens) .gowned[data-ground="absence"]:hover { background: rgba(77,126,168,.14); border-radius: 2px; }
.gowned:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 2px; }

/* a clickable passage snippet (source → its spans in the article) */
.cite-span {
  display: flex; gap: 7px; align-items: baseline; width: 100%; text-align: left;
  background: transparent; border: 0; border-top: 1px solid var(--rule);
  padding: 6px 8px; cursor: pointer; font: inherit;
}
.cite-span:hover { background: var(--paper-2); }
.cite-span[data-current="1"] { background: rgba(255,236,1,0.35); }
/* a passage as a TWO-WAY pivot: the snippet jumps to the story, "in source"
   opens the document itself at that exact span */
.cite-span-row { display: flex; align-items: stretch; border-top: 1px solid var(--rule); }
.cite-span-row .cite-span { border-top: 0; flex: 1; min-width: 0; }
.cite-span-src {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  border: 0; border-left: 1px solid var(--rule); background: transparent; color: var(--data);
  padding: 0 9px; font-size: 10px; letter-spacing: .02em; white-space: nowrap;
}
.cite-span-src:hover { background: rgba(255,236,1,0.4); }

/* ---- source hover card ---- */
.srccard {
  position: fixed;
  z-index: 4000;
  width: 340px;
  background: var(--card);
  border: 1.5px solid var(--ink);
  box-shadow: 6px 6px 0 rgba(22,20,13,0.16);
  font-family: var(--serif);
  animation: pop .12s ease;
}
@keyframes pop { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ---- docked side drawer ---- */
/* On a wide window the citation/note/grounding card docks as a persistent side
   panel flush to the right edge — full height, border only on the left, sliding
   in from the side (the reading column has reflowed left to make room). Overrides
   the floating card's all-around border, box-shadow and pop animation. Shared by
   .srccard / .fnpop / .gpop via the npj-dock-drawer modifier. */
.npj-dock-drawer {
  border-width: 0 0 0 1.5px;
  box-shadow: -18px 0 44px rgba(22,20,13,.13);
  animation: npj-drawer-in .32s cubic-bezier(.22,.61,.36,1);
}
@keyframes npj-drawer-in { from { transform: translateX(100%); } to { transform: translateX(0); } }

/* ---- footnote inline preview (FootnotePop) — the Substack hover/tap card ----
   The note floats up next to its marker so the reader never loses their place;
   it's the same paper card as a citation, just lighter. */
.fnpop {
  position: fixed; z-index: 4000; width: 340px;
  background: var(--card); border: 1.5px solid var(--ink);
  box-shadow: 6px 6px 0 rgba(22,20,13,0.16);
  font-family: var(--serif); animation: pop .12s ease;
}
.fnpop-h { display: flex; align-items: center; gap: 8px; padding: 9px 12px 0; }
.fnpop-n {
  min-width: 18px; height: 18px; padding: 0 4px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; color: var(--ink); background: var(--yellow); border: 1px solid var(--ink);
}
.fnpop-b { padding: 7px 14px 12px; font-size: 14.5px; line-height: 1.52; color: var(--ink); }
.fnpop-b a { color: var(--data); text-underline-offset: 2px; }
.fnpop-jump {
  display: block; width: 100%; text-align: left; cursor: pointer;
  border: 0; border-top: 1.5px solid var(--ink); background: var(--card);
  color: var(--ink-soft); padding: 8px 14px; letter-spacing: .04em;
}
.fnpop-jump:hover { background: var(--paper-2); color: var(--ink); }
/* the marker: a hair of breathing room so the small number is easy to aim at */
.fnmark { margin: 0 1px; }
.fnmark a { cursor: pointer; }

/* ---- owned-claim & void inline preview (GroundingPop) ----
   The Previews twin of the citation card, for grounded things with no source to
   cite: an assertion the author owns, or a documented void. The same paper card as
   a footnote, with a coloured spine keyed to the grounding (violet = the author's
   own; blue = a void). */
.gpop {
  position: fixed; z-index: 4000; width: 320px;
  background: var(--card); border: 1.5px solid var(--ink);
  box-shadow: 6px 6px 0 rgba(22,20,13,0.16);
  font-family: var(--serif); animation: pop .12s ease;
}
.gpop-h { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1.5px solid var(--ink); }
.gpop-g { font-size: 15px; line-height: 1; flex: 0 0 auto; }
.gpop-b { padding: 9px 14px 12px; }
.gpop-b a { color: var(--data); text-underline-offset: 2px; }
/* the "Open" affordance that promotes a margin card to the main stage — shared by
   the grounding card and the footnote card. Subtle until hovered, then it reads as
   the clear escalation it is. */
.gpop-open { transition: background-color .12s ease, color .12s ease, border-color .12s ease; }
.gpop-open:hover { background: var(--paper-2) !important; color: var(--ink) !important; border-color: var(--ink) !important; }

/* ---- source document, expanded full-screen ----
   The thumbnail in a citation card (SourceFilePreview) is a button that opens
   the real file at full size IN the app (SourceLightbox) — no new tab. A hover
   badge says so; the scrim dims the story behind the sheet and is itself the
   exit (so are the ✕ and Esc). */
.srcfile-frame { cursor: zoom-in; transition: filter .12s ease; }
.srcfile-frame:hover { filter: brightness(.96); }
.srcfile-frame .srcfile-expand {
  position: absolute; top: 8px; right: 8px;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 9.5px; letter-spacing: .04em; line-height: 1;
  padding: 4px 7px; color: var(--paper); background: rgba(16,14,10,.74);
  border: 1px solid rgba(255,255,255,.25); opacity: 0;
  transition: opacity .12s ease; pointer-events: none;
}
.srcfile-frame:hover .srcfile-expand,
.srcfile-frame:focus-visible .srcfile-expand { opacity: 1; }

.srclb-scrim {
  position: fixed; inset: 0; z-index: 7000;
  display: flex; align-items: center; justify-content: center;
  padding: 22px; background: rgba(8,7,5,0.62);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.srclb {
  display: flex; flex-direction: column;
  width: min(1100px, 100%); max-height: calc(100vh - 44px);
  background: var(--paper); color: var(--ink);
  border: 1.5px solid var(--ink); box-shadow: 10px 10px 0 rgba(22,20,13,0.22);
  animation: pop .14s ease;
}
.srclb-head {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 14px; border-bottom: 1.5px solid var(--ink); background: var(--paper-2);
}
.srclb-body { flex: 1; min-height: 0; overflow: auto; padding: 14px; }
/* gallery nav: step through every source full-size without leaving the sheet */
.srclb-nav {
  flex: 0 0 auto; width: 36px; height: 36px; align-self: center;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--paper); border: 1.5px solid var(--ink); cursor: pointer;
  font-size: 22px; line-height: 1; color: var(--ink); padding: 0;
}
.srclb-nav:hover { background: var(--yellow); }
@media (max-width: 760px) {
  .srclb-scrim { padding: 0; }
  .srclb { width: 100%; height: 100%; max-height: 100vh; border-width: 0; box-shadow: none; }
  .srclb-nav { width: 30px; height: 30px; font-size: 19px; }
}

/* the passages a source backs, shown inside the full-size viewer — click one to
   jump back into the story (stays on the page, no new tab) */
.srclb-cited { margin-top: 14px; padding-top: 12px; border-top: 1.5px solid var(--ink); }

/* the filmstrip — every source as a thumbnail along the foot of the browser, so
   the whole set is in view and any one is a click away (not just ‹ › paging) */
.srclb-strip {
  flex: 0 0 auto; display: flex; gap: 8px; align-items: center;
  padding: 9px 12px; border-top: 1.5px solid var(--ink); background: var(--paper-2);
  overflow-x: auto; overflow-y: hidden;
}
.srclb-thumb {
  position: relative; flex: 0 0 auto; padding: 0; border: 0; background: none; cursor: pointer;
  opacity: .62; transition: opacity .12s ease, transform .12s ease; line-height: 0;
}
.srclb-thumb:hover { opacity: .9; }
.srclb-thumb.is-on { opacity: 1; }
.srclb-thumb.is-on .srcthumb { outline: 2.5px solid var(--yellow-deep); outline-offset: 1px; }
.srclb-thumb-n {
  position: absolute; top: -5px; left: -5px; min-width: 15px; height: 15px; padding: 0 3px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ink); color: var(--paper); font-size: 9px; font-weight: 700; border-radius: 8px; line-height: 1;
}
@media (max-width: 760px) { .srclb-strip { padding: 7px 10px; } }

/* the cited passage, marked + scrolled to when a source is opened "at" a span */
.src-locate {
  background: color-mix(in srgb, var(--yellow) 62%, transparent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--yellow) 62%, transparent);
  border-radius: 1px; color: inherit; scroll-margin: 40px 0;
}

/* ---- the Sources explorer (the receipts, made browsable) ----
   A faceted footer: filter chips by kind, a search over titles + quoted passages,
   a sort, and cards that open each source full-size IN THE APP. */
.srcx-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-bottom: 16px; }
.srcx-search {
  display: inline-flex; align-items: center; gap: 7px; flex: 1 1 230px; min-width: 170px;
  border: 1.5px solid var(--ink); background: var(--card); padding: 6px 9px;
}
.srcx-search input { flex: 1; min-width: 0; border: 0; background: none; outline: none; font: inherit; font-size: 13.5px; color: var(--ink); }
.srcx-search input::placeholder { color: var(--ink-soft); }
.srcx-clear { border: 0; background: none; cursor: pointer; color: var(--ink-soft); font-size: 13px; line-height: 1; padding: 0 2px; flex: 0 0 auto; }
.srcx-clear:hover { color: var(--ink); }

.srcx-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.srcx-chip {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-family: var(--cond); font-weight: 600; font-size: 13px; letter-spacing: .02em;
  padding: 5px 10px; border: 1.5px solid var(--ink); background: var(--card); color: var(--ink);
}
.srcx-chip:hover { background: var(--paper-2); }
.srcx-chip[aria-pressed="true"] { background: var(--ink); color: var(--paper); }
.srcx-chip[aria-pressed="true"] .srcx-n { color: var(--paper); }
.srcx-n { font-family: var(--mono); font-size: 10.5px; color: var(--ink-soft); }
.srcx-sort {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  font-family: var(--cond); font-weight: 600; font-size: 12.5px;
  padding: 5px 9px; border: 1.5px solid var(--rule-strong); background: var(--card); color: var(--ink-soft);
}
.srcx-sort:hover { border-color: var(--ink); color: var(--ink); }
.srcx-reset { border: 0; background: none; cursor: pointer; color: var(--data); font: inherit; font-size: 12px; text-decoration: underline; text-underline-offset: 2px; }

/* the source list packs into two columns (one on a phone) with no row gaps —
   each source is a self-contained card that never splits across a column */
.srcx-list { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 22px; }
.srcx-card {
  break-inside: avoid; -webkit-column-break-inside: avoid; page-break-inside: avoid;
  display: block; margin: 0 0 16px; border: 1.5px solid var(--ink); background: var(--card);
  box-shadow: 3px 3px 0 rgba(22,20,13,.10);
}
@media (max-width: 720px) { .srcx-list { columns: 1; } }
.srcx-open {
  display: flex; gap: 11px; align-items: flex-start; text-align: left; width: 100%;
  background: none; border: 0; padding: 11px 11px 10px; cursor: pointer; font: inherit; color: var(--ink);
}
.srcx-open:hover { background: var(--paper-2); }
/* the thumbnail carries the source's number as a corner badge — a picture you can
   scan, not a bare numeral */
.srcx-thumbwrap { position: relative; flex: 0 0 auto; }
.srcx-num {
  position: absolute; top: -7px; left: -7px; flex: 0 0 auto; line-height: 1; margin: 0;
  box-shadow: 0 1px 3px rgba(8,7,5,.3);
}
/* ---- the Sources page gallery: every source as a thumbnail you open full-size ---- */
.srcg-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px;
}
@media (max-width: 560px) { .srcg-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; } }
.srcg-tile {
  display: flex; flex-direction: column; text-align: left; cursor: pointer; font: inherit; color: var(--ink);
  border: 1.5px solid var(--ink); background: var(--card); padding: 0; overflow: hidden;
  box-shadow: 3px 3px 0 rgba(22,20,13,.10); transition: transform .1s ease, box-shadow .1s ease;
}
.srcg-tile:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0 rgba(22,20,13,.16); }
.srcg-tile .srcthumb { border-width: 0 0 1.5px 0 !important; }
.srcg-body { display: flex; flex-direction: column; gap: 4px; padding: 9px 11px 11px; min-width: 0; }
.srcg-title {
  font-family: var(--cond); font-weight: 600; font-size: 15px; line-height: 1.12;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.srcg-meta { display: flex; flex-wrap: wrap; gap: 4px; font-family: var(--mono); font-size: 10px; color: var(--ink-soft); }

/* the hero entry into the source browser */
.srcx-browse {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer; flex: 0 0 auto;
  border: 1.5px solid var(--ink); background: var(--yellow); color: var(--ink);
  padding: 6px 13px; font-family: var(--cond); font-weight: 700; font-size: 13px;
  text-transform: uppercase; letter-spacing: .05em;
}
.srcx-browse:hover { background: var(--ink); color: var(--yellow); }
.srcx-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.srcx-title {
  font-family: var(--cond); font-weight: 600; font-size: 15px; line-height: 1.14; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.srcx-outlet { font-family: var(--serif); font-size: 12.5px; color: var(--ink-soft); line-height: 1.2; }
.srcx-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 2px; font-family: var(--mono); font-size: 9.5px; color: var(--ink-soft); }
.srcx-medium { display: inline-flex; align-items: center; gap: 4px; color: var(--ink); }
.srcx-view { flex: 0 0 auto; align-self: center; color: var(--ink-soft); opacity: .55; transition: opacity .12s ease, transform .12s ease; }
.srcx-open:hover .srcx-view, .srcx-open:focus-visible .srcx-view { opacity: 1; transform: scale(1.08); }
/* the source → spans connection, the heart of the card: a labelled strip over
   the list of passages this source grounds */
.srcx-foot { border-top: 1.5px solid var(--ink); background: color-mix(in srgb, var(--yellow) 9%, var(--card)); }
.srcx-foot-h {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .04em; line-height: 1.4;
  color: var(--ink-soft); padding: 8px 11px 4px;
}
.srcx-foot .cite-span { padding-left: 11px; padding-right: 11px; background: transparent; }
.srcx-foot .cite-span:hover { background: rgba(255,236,1,0.4); }
.srcx-more {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: none; border: 0; border-top: 1px solid var(--rule); padding: 7px 11px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .03em; color: var(--data);
}
.srcx-more:hover { background: var(--paper-2); }

/* ---- status chips ---- */
.chip {
  font-family: var(--cond);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  font-size: 11px;
  padding: 2px 7px;
  border: 1px solid var(--ink);
  display: inline-flex; align-items: center; gap: 5px;
  white-space: nowrap;
}
.chip-proposed { background: var(--card); }
.chip-review   { background: #fbeccb; border-color: var(--review); color: var(--review); }
.chip-accepted { background: #d9efe2; border-color: var(--verified); color: var(--verified); }
.chip-rejected { background: #f4dcd6; border-color: var(--reject); color: var(--reject); }
.chip-stale    { background: repeating-linear-gradient(45deg,#efe9d8,#efe9d8 4px,#e4dcc6 4px,#e4dcc6 8px); }

.dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }

/* ---- span feedback painted into the prose (CSS Custom Highlight API) ----
   Spans with open reader feedback wear a soft underline in the article itself,
   the way a Google-Docs suggestion shows where a change is proposed. The flash
   highlight is the rail's "show in text" jump. Silent no-op where unsupported. */
::highlight(npj-feedback) {
  background-color: color-mix(in srgb, var(--review, #b5701b) 16%, transparent);
  text-decoration: underline;
  text-decoration-style: dashed;
  text-decoration-color: var(--review, #b5701b);
  text-underline-offset: 3px;
}
::highlight(npj-feedback-flash) {
  background-color: var(--yellow);
  color: var(--ink);
}
/* the private, e2ee collaboration layer (CollabRail) marks its anchored comments
   with a distinct registry so it never collides with the public feedback paint. */
::highlight(npj-collab) {
  background-color: color-mix(in srgb, var(--verified, #1f8a5b) 16%, transparent);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-color: var(--verified, #1f8a5b);
  text-underline-offset: 3px;
}
::highlight(npj-collab-flash) {
  background-color: var(--yellow);
  color: var(--ink);
}
/* the floating selection bubble — Suggest edit / Comment on any span */
.fb-bubble {
  position: fixed; z-index: 4200; display: flex; gap: 0;
  background: var(--ink); color: var(--paper);
  border: 1.5px solid var(--ink); box-shadow: 0 6px 20px rgba(0,0,0,.32);
  transform: translate(-50%, 0); animation: pop .14s ease;
}
.fb-bubble button {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; color: var(--paper); cursor: pointer;
  font-family: var(--cond); font-weight: 600; font-size: 13px;
  text-transform: uppercase; letter-spacing: .04em; padding: 7px 12px;
}
.fb-bubble button + button { border-left: 1px solid rgba(255,255,255,.22); }
.fb-bubble button:hover { background: rgba(255,255,255,.12); color: var(--yellow); }

/* trust badges */
.trust-editor    { color: var(--verified); }
.trust-preferred { color: var(--review); }
.trust-open      { color: #6b6b6b; }

/* ---- buttons ---- */
.btn {
  font-family: var(--cond); text-transform: uppercase; letter-spacing: 0.06em;
  font-weight: 600; font-size: 14px;
  border: 1.5px solid var(--ink); background: var(--card); color: var(--ink);
  padding: 8px 14px; transition: transform .08s ease, background .12s ease;
}
.btn:hover { background: var(--paper-2); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--yellow); }
.btn-primary:hover { background: var(--yellow-deep); }
.btn-ghost { background: transparent; border-color: var(--rule); }
.btn-sm { padding: 4px 9px; font-size: 12px; }

/* upvote pill */
.vote {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  border: 1.5px solid var(--ink); background: var(--card);
  padding: 2px 8px; transition: background .1s;
}
.vote[data-on="1"] { background: var(--yellow); }
.vote:hover { background: var(--paper-2); }
.vote[data-on="1"]:hover { background: var(--yellow-deep); }

/* links list */
.headline-link { transition: background .12s; }
.headline-link:hover { background: var(--yellow); }

/* fade/slide helpers */
.fade-in { animation: fade .25s ease; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

/* entity highlight (eoreader3 figures) — distinct from source-claim yellow */
.ent-mark {
  background: rgba(43,95,138,0.16);
  border-bottom: 2px solid var(--data);
  padding: 0 1px;
  color: inherit;
}

/* ── inline definitions ───────────────────────────────────────────────────────
   A defined term wears a quiet dotted underline in the prose; hover or focus
   floats up its first sourced definition. Distinct from the yellow claim
   highlight and the entity mark — a hairline cue that there's a definition to
   read, not a source to audit. The full glossary still rides the footer. */
.def-term {
  position: relative;
  cursor: help;
  border-bottom: 1px dotted var(--ink-soft, #595139);
  text-underline-offset: 2px;
}
.def-term:hover, .def-term:focus-visible { background: rgba(255,236,1,.16); outline: none; border-bottom-style: solid; }
.def-pop {
  position: absolute; left: 0; top: calc(100% + 7px); z-index: 45;
  width: max-content; max-width: min(320px, 78vw);
  background: var(--card); color: var(--ink);
  border: 1.5px solid var(--ink); box-shadow: 4px 4px 0 rgba(22,20,13,.16);
  padding: 9px 11px; display: none;
  font-family: var(--serif); font-size: 14px; font-style: normal; line-height: 1.45;
  white-space: normal; text-align: left; cursor: auto;
}
.def-term:hover .def-pop, .def-term:focus .def-pop, .def-term:focus-within .def-pop { display: block; }
.def-pop-term { display: block; font-family: var(--cond); font-weight: 700; font-size: 13px; letter-spacing: .01em; margin-bottom: 3px; }
.def-pop-text { display: block; }
.def-pop-more { display: block; font-family: var(--mono); font-size: 10px; color: var(--ink-soft); margin-top: 6px; }
@media (max-width: 760px) { .def-pop { position: fixed; left: 12px; right: 12px; width: auto; max-width: none; } }

/* newsroom (private) inverts the palette — themeable. Dark is the default;
   adding .nr-light on the newsroom root flips the whole editor to a paper
   scheme. Every piece of newsroom chrome reads these vars, so the theme
   toggle is a single class. */
.newsroom {
  --nr-bg: #121009;            /* canvas behind the document page */
  --nr-rail: #15130e;          /* contents rail + mobile tab bar */
  --nr-panel: #1d1b15;         /* sources panel */
  --nr-field: #14130f;         /* inputs */
  --nr-page: #1b1913;          /* the document sheet itself */
  --nr-line: rgba(255,255,255,.13);
  --nr-line-strong: rgba(255,255,255,.28);
  --nr-text: #e3ddcc;
  --nr-soft: #b3ad9c;
  --nr-muted: #8c8676;
  --nr-quote: #cfc8b6;
  --nr-link: #9cc6e8;
  --nr-code-bg: rgba(255,255,255,.07);
  --nr-ok: #9fe0b8;
  --nr-warn: #e6b07f;
  --nr-page-shadow: 0 18px 44px rgba(0,0,0,.45);
  background: var(--nr-bg);
  color: var(--nr-text);
}
.newsroom.nr-light {
  /* a real depth ramp instead of one flat beige: the canvas recedes so the
     white document sheet floats, rails/panels step up toward it, and the
     fields you actually write in read as clean paper. Secondary tones and
     hairlines are tuned so every text/UI pairing clears WCAG AA. */
  --nr-bg: #e0d8c5;            /* canvas behind the sheet — the deepest tone */
  --nr-rail: #efe9dc;          /* contents rail + tab/strip chrome */
  --nr-panel: #f4efe4;         /* side panels — a step brighter than the rail */
  --nr-field: #fffdf6;         /* inputs + table fields — paper white */
  --nr-page: #fffdf6;          /* the document sheet */
  --nr-line: rgba(22,20,13,.26);
  --nr-line-strong: rgba(22,20,13,.5);
  --nr-text: #16140d;          /* ~12:1 on every surface */
  --nr-soft: #3f3a2b;          /* ~7:1 — confident secondary text */
  --nr-muted: #595139;         /* ~4.8:1 on the deep canvas — clears AA */
  --nr-quote: #3f3a2b;
  --nr-link: #1a527c;          /* ~5:1 — links stay legible */
  --nr-code-bg: rgba(22,20,13,.06);
  --nr-ok: #15663f;            /* ~5.5:1 */
  --nr-warn: #7e5410;          /* ~5.4:1 */
  --nr-page-shadow: 5px 5px 0 rgba(22,20,13,.14);
}
.newsroom ::selection { background: var(--review); color: #fff; }
.newsroom .np-scroll::-webkit-scrollbar-thumb { background: var(--nr-line-strong); }

/* top chrome (top bar + format toolbar) sits on a brighter rail surface than
   the editing canvas, with a defined bottom edge — so the controls read as a
   clean header band instead of muddy text on the deep canvas (the old "hard to
   read at the top"). Applies in both themes; light gains the most. */
.newsroom .nr-chrome { background: var(--nr-rail); }
.newsroom.nr-light .nr-chrome { border-bottom-color: var(--nr-line-strong) !important; }
/* Phosphor glyphs inherit text color + size; keep them optically centered in
   the dense toolbar and never let a slow font swap collapse their box */
.newsroom i[class*="ph-"] { line-height: 1; }

/* the document sheet — in the editor the draft renders as a bounded paper sheet
   centered on the canvas, carrying its own masthead (the Title/Subtitle/byline
   fields) above the body. The masthead and the body share one sheet at the
   reader's measure and type scale, so the editor opens looking like the page it
   becomes — closing the daylight between writing and the published article. */
.nr-sheet {
  max-width: 800px;
  margin: 0 auto;
  background: var(--nr-page);
  border: 1.5px solid var(--nr-line-strong);
  box-shadow: var(--nr-page-shadow);
}
.nr-page {
  --page-pad: 52px;
  --page-pad-top: 18px;
  min-height: 58vh;
  background: transparent;
  padding: var(--page-pad-top) var(--page-pad) 72px;
}
/* banner bleeds to the sheet edge, but no longer pulls up under the masthead */
.md-preview figure.nr-banner { margin: 0 calc(-1 * var(--page-pad, 0px)) 26px; }
.nr-sheet .nr-page > figure.nr-banner:first-child { margin-top: 4px; }
/* body prose is justified by default; per-block alignment from the toolbar's
   align menu (incl. Select-all → Justify) sets an inline text-align that wins. */
.nr-page p, .nr-page li { text-align: justify; }
/* The prose editor keeps inline images at the text-column width — a calm WYSIWYG
   measure to write against. The wider-than-column bleed is a reader treatment
   (ArticleRead's wideFig/heroFig), applied in the published article and its
   Preview, not while editing. The full-bleed banner keeps its own bleed (above). */
/* the dek — one subtitle line under the headline. The placeholder shows while
   it's empty (covering both a truly empty <p> and the <p><br></p> editors
   normalize it to). */
.nr-page .nr-dek { font-family: var(--serif); font-style: italic; font-size: 21px; line-height: 1.35; color: var(--nr-soft); margin: 2px 0 18px; }
.nr-page .nr-dek:empty::before,
.nr-page .nr-dek:has(> br:only-child)::before { content: attr(data-ph); color: var(--nr-muted); pointer-events: none; }

/* The headline + dek are filled via the explicit Title/Subtitle fields in the
   sheet's masthead; the in-canvas nodes stay (the publish pipeline reads them)
   but are hidden so the canvas holds body prose only. */
.md-preview.nr-fielded > h1:first-of-type,
.md-preview.nr-fielded > .nr-dek { display: none; }

/* ── the masthead: Title / Subtitle / byline, on the paper sheet ──────────────
   Padded to the body's text column so the headline sits exactly over the prose
   that follows, and scaled to the reader's own header (display headline, italic
   serif dek) so what you write reads like what publishes. */
.nr-fields { padding: 34px 52px 0; }
.nr-field-title { display: block; width: 100%; border: 0; background: transparent; color: var(--nr-text);
  font-family: var(--display); font-weight: 400; line-height: 1.02; letter-spacing: -.01em; padding: 0; margin: 0 0 14px; outline: none; }
.nr-field-dek { display: block; width: 100%; border: 0; background: transparent; color: var(--nr-soft);
  font-family: var(--serif); font-style: italic; line-height: 1.32; padding: 0; margin: 0 0 18px; outline: none; }
.nr-field-byline { padding-top: 14px; border-top: 1px solid var(--nr-line); }
/* readable placeholders + a visible focus ring (the masthead inputs are
   borderless, so the ring is the only focus cue) */
.nr-fields input::placeholder { color: var(--nr-muted); opacity: 1; }
.nr-field-byline input:focus { border-bottom-color: var(--nr-line-strong); }
.nr-fields input:focus-visible { outline: 2px solid var(--yellow); outline-offset: 3px; }
/* the drop-slot's shadow-DOM colors are black-on-black in the dark room —
   re-skin its exposed parts to the theme */
.newsroom image-slot::part(frame) { background: var(--nr-code-bg); }
.newsroom image-slot::part(ring) { border-color: var(--nr-line-strong); }
.newsroom image-slot::part(empty) { color: var(--nr-muted); }
@media (max-width: 760px) {
  .nr-page { --page-pad: 18px; --page-pad-top: 12px; padding-bottom: 44px; min-height: 50vh; }
  .nr-fields { padding: 22px 18px 0; }
}

/* ---- in-document block drag (the Google-Docs grip) ----
   A handle that floats in the page's left gutter on hover; grab it to drag a
   paragraph, image or whole section to a new spot. Editing chrome only — these
   live outside the contentEditable, so nothing here ever publishes. */
/* grip + its delete button stack vertically in the gutter; the group carries
   the absolute position so both children share one hover zone. */
.nr-grip-group {
  position: absolute; z-index: 5;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.nr-grip {
  width: 20px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  color: var(--nr-muted); font-size: 13px; line-height: 1; user-select: none;
  cursor: grab; border-radius: 4px; background: transparent;
  opacity: 0; transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.nr-grip.show { opacity: .55; }
.nr-grip.show:hover { opacity: 1; background: var(--nr-field); color: var(--nr-text); box-shadow: inset 0 0 0 1px var(--nr-line); }
.nr-grip:active { cursor: grabbing; }
.nr-grip-del {
  width: 20px; height: 20px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--nr-muted); border: 0; border-radius: 4px;
  background: var(--nr-field); box-shadow: inset 0 0 0 1px var(--nr-line);
  cursor: pointer; opacity: .7; transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.nr-grip-del:hover { opacity: 1; color: var(--nr-warn); box-shadow: inset 0 0 0 1px var(--nr-warn); }
.nr-drop-line {
  position: absolute; z-index: 6; height: 2px; pointer-events: none;
  background: var(--yellow); border-radius: 2px;
}
.nr-drop-line::before {
  content: ""; position: absolute; left: -4px; top: -3px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--yellow);
}
.nr-block-dragging { opacity: .4; }
/* click-to-delete × over an image / embed block's top-right corner. Dark glass
   so it stays legible over any photo; turns red on hover to read as "delete". */
.nr-media-del {
  position: absolute; z-index: 6;
  width: 28px; height: 28px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  color: #fff; border: 0; border-radius: 6px; cursor: pointer;
  background: rgba(0,0,0,.55); box-shadow: 0 1px 4px rgba(0,0,0,.3);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: background .12s ease;
}
.nr-media-del:hover { background: rgba(179,38,30,.95); }
/* the touch surfaces have no gutter to spare — grip is desktop-only (gated in JS too) */
@media (max-width: 760px) { .nr-grip-group, .nr-grip, .nr-drop-line, .nr-media-del { display: none; } }

/* ---- composer ---- */
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(127,216,166,.6); } 70% { box-shadow: 0 0 0 7px rgba(127,216,166,0); } 100% { box-shadow: 0 0 0 0 rgba(127,216,166,0); } }
.cmp-body:empty::before, .cmp-body > p:only-child:empty::before { content: attr(data-ph); color: var(--ink-soft); font-style: italic; pointer-events: none; }
.cmp-body h2 { font-family: var(--cond); font-weight: 700; font-size: 30px; line-height: 1.06; margin: 22px 0 6px; }
.cmp-body blockquote { border-left: 3px solid var(--yellow-deep); margin: 22px 0; padding-left: 20px; font-family: var(--quote); font-weight: 300; font-size: 20px; line-height: 1.55; letter-spacing: -.01em; }
.cmp-body blockquote.np-pull { border-left: 0; padding: 14px 0; border-top: 1.5px solid var(--yellow-deep); border-bottom: 1.5px solid var(--yellow-deep); font-size: 28px; line-height: 1.26; text-align: center; }
/* callout — a highlighted aside / note box (Substack-style). A tinted panel with
   a heavy accent bar; holds rich inline prose (bold, links, citations). */
.cmp-body aside.np-callout { border-left: 4px solid var(--yellow-deep); background: color-mix(in srgb, var(--yellow) 12%, var(--card)); margin: 22px 0; padding: 14px 16px; font-family: var(--serif); font-size: 16px; line-height: 1.55; }
.cmp-body aside.np-callout p:last-child { margin-bottom: 0; }
.cmp-body p { margin: 0 0 14px; }
.cmp-body a { text-decoration: underline; text-underline-offset: 2px; }
.cmp-hr { border: 0; border-top: 2px solid var(--ink); width: 60px; margin: 26px 0; }
.cmp-embed { margin: 18px 0; }
.cmp-embed figcaption { font-size: 11px; color: var(--ink-soft); margin-top: 5px; }
/* editable caption + photo credit + description lines under a figure. The credit
   takes a markdown hyperlink ([label](url)) like a contributor bio; the
   description is the photo's alt text (screen readers + search), shown here only
   so the author can write it — it never renders on the published page. The
   placeholder is shown via data-ph while a line is empty (or holds a stray <br>). */
.cmp-cap, .cmp-credit, .cmp-desc { outline: none; min-height: 1.2em; }
/* ENTERED caption/credit/description text reads as confident secondary copy
   (--nr-soft): in dark mode --nr-muted on the page only clears ~4.8:1 at 11px,
   which read as barely-legible. --nr-soft lifts it to ~7.7:1 (and helps light
   too). The EMPTY-state placeholders below stay --nr-muted — a hint, not copy. */
.cmp-cap, .cmp-credit, .cmp-desc { color: var(--nr-soft, var(--ink-soft)); }
.cmp-cap:empty::before, .cmp-cap:has(> br:only-child)::before,
.cmp-credit:empty::before, .cmp-credit:has(> br:only-child)::before,
.cmp-desc:empty::before, .cmp-desc:has(> br:only-child)::before {
  content: attr(data-ph); color: var(--nr-muted, var(--ink-soft)); pointer-events: none; }
.cmp-cap:focus, .cmp-credit:focus, .cmp-desc:focus { box-shadow: inset 0 -1px 0 0 var(--yellow-deep); }
.cmp-credit a { text-decoration: underline; text-underline-offset: 2px; }
/* the description line wears a small "alt" tag so it's never mistaken for a
   second caption — it's metadata, not body copy. */
.cmp-desc::after { content: "alt"; float: right; font-size: 8.5px; letter-spacing: .08em;
  text-transform: uppercase; opacity: .55; margin-top: .15em; pointer-events: none; user-select: none; }
.cmp-widget { border: 1.5px dashed var(--rule-strong); background: repeating-linear-gradient(135deg, transparent, transparent 9px, rgba(43,95,138,.06) 9px, rgba(43,95,138,.06) 10px), var(--card); padding: 14px 16px; }
.cmp-widget-h { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cmp-widget-h .np-mono { font-size: 12px; color: var(--data); }
.cmp-tag { font-family: var(--cond); text-transform: uppercase; letter-spacing: .06em; font-size: 10px; font-weight: 600; color: var(--ink-soft); border: 1px solid var(--rule); padding: 1px 6px; }
.cmp-widget-b strong { font-family: var(--cond); font-size: 18px; display: block; }
.cmp-widget-b span { font-family: var(--serif); font-size: 13px; color: var(--ink-soft); }
.cmp-widget-f { font-size: 10px; color: var(--ink-soft); margin-top: 8px; }
.cmp-cite { background: rgba(43,95,138,.12); border: 1px solid var(--data); padding: 1px 6px; font-family: var(--cond); font-weight: 600; font-size: .92em; white-space: nowrap; }
.cmp-cite .np-mono { font-size: 10px; color: var(--data); }

/* ---- newsroom markdown preview ---- */
.md-preview { font-family: var(--serif); font-size: 16px; line-height: 1.62; }
.md-preview h1 { font-family: var(--display); font-weight: 400; font-size: 34px; line-height: 1; margin: 4px 0 14px; }
.md-preview h2 { font-family: var(--cond); font-weight: 700; font-size: 23px; line-height: 1.08; margin: 22px 0 8px; }
.md-preview h3 { font-family: var(--cond); font-weight: 700; font-size: 18px; margin: 18px 0 6px; }
.md-preview p { margin: 0 0 13px; }
.md-preview blockquote { border-left: 3px solid var(--yellow); margin: 18px 0; padding-left: 18px; font-family: var(--quote); font-weight: 300; font-size: 18px; line-height: 1.5; color: var(--nr-quote, #cfc8b6); }
.md-preview blockquote.np-pull { border-left: 0; padding: 12px 0; border-top: 1.5px solid var(--yellow); border-bottom: 1.5px solid var(--yellow); font-size: 25px; line-height: 1.28; text-align: center; }
.md-preview aside.np-callout { border-left: 4px solid var(--yellow); background: rgba(255,236,1,.08); margin: 18px 0; padding: 13px 15px; font-family: var(--serif); font-size: 16px; line-height: 1.55; }
.newsroom.nr-light .md-preview aside.np-callout { background: rgba(255,236,1,.16); }
.md-preview aside.np-callout p:last-child { margin-bottom: 0; }
.md-preview ul { margin: 0 0 13px; padding-left: 20px; }
.md-preview li { margin: 0 0 5px; }
.md-preview a { color: var(--nr-link, #9cc6e8); text-decoration: underline; text-underline-offset: 2px; }
.md-preview code { font-family: var(--mono); font-size: .88em; background: var(--nr-code-bg, rgba(255,255,255,.08)); padding: 1px 5px; }
.md-preview hr { border: 0; border-top: 1px solid var(--nr-line-strong, rgba(255,255,255,.18)); margin: 20px 0; }
.md-cite { font-family: var(--mono); font-size: 9.5px; color: var(--yellow); background: rgba(255,236,1,.14); border: 1px solid rgba(255,236,1,.4); padding: 0 4px; border-radius: 2px; vertical-align: super; line-height: 0; margin-left: 2px; }
/* on paper, yellow-on-paper citation chips would vanish — flip to ink on yellow */
.newsroom.nr-light .md-cite { color: var(--ink); background: rgba(255,236,1,.55); border-color: rgba(22,20,13,.4); }
.md-preview .claim-src { background: rgba(255,236,1,.2); border-bottom: 1.5px solid var(--yellow); padding: 0 1px; cursor: pointer; }
.newsroom.nr-light .md-preview .claim-src { background: rgba(255,236,1,.4); border-bottom-color: var(--yellow-deep); }
/* a span that's bound to a source but has no pinned source-span yet — the
   citation is incomplete until you point at the exact words in the source */
.md-preview .claim-src.needs-quote { background: rgba(230,123,60,.16); border-bottom: 1.5px dashed var(--reject, #e67b3c); }
.newsroom.nr-light .md-preview .claim-src.needs-quote { background: rgba(230,123,60,.22); }
.md-preview .claim-src.needs-quote::after { content: "⚑"; font-size: 9px; color: var(--reject, #e67b3c); vertical-align: super; margin-left: 1px; }
/* an OWNED claim — the author stands behind it (analysis / account / voice),
   grounded by honest declaration rather than a citation. Citey records the
   stance; it publishes as the author's prose. */
.md-preview .claim-src[data-stance] { background: rgba(124,116,222,.16); border-bottom: 1.5px solid #7c74de; }
.newsroom.nr-light .md-preview .claim-src[data-stance] { background: rgba(124,116,222,.2); }
.md-preview .claim-src[data-stance]::after { content: "⊢"; font-size: 9px; color: #7c74de; vertical-align: super; margin-left: 1px; }
.md-preview .claim-src[data-stance="testimony"]::after { content: "⊨"; }
.md-preview .claim-src[data-stance="voice"]::after { content: "⊩"; }
/* a CONTEXT claim — continuing coverage. The article substantiates it; it is set
   against prior reporting. Distinct teal so it never reads as a proof citation. */
.md-preview .claim-src[data-stance="context"] { background: rgba(46,139,134,.16); border-bottom: 1.5px solid #2e8b86; }
.newsroom.nr-light .md-preview .claim-src[data-stance="context"] { background: rgba(46,139,134,.22); }
.md-preview .claim-src[data-stance="context"]::after { content: "⊪"; color: #2e8b86; }
/* a sentence that is grounded another way but also carries context links
   (prior coverage) — a faint trailing ⊪ marks "set in context, too" */
.md-preview .claim-src[data-context]:not([data-stance]):not(.needs-quote)::after { content: "⊪"; font-size: 9px; color: #2e8b86; vertical-align: super; margin-left: 1px; opacity: .8; }
/* Inline citation number chips are NEVER shown in the editor — a cited claim is
   marked by its highlight alone, not a superscript number. (Footnote markers,
   .md-cite[data-fn], are a separate apparatus and stay.) The span itself remains
   clickable to open/pin its source; only the number is suppressed. */
.md-preview .md-cite:not([data-fn]) { display: none !important; }
/* clean-read toggle (the toolbar's Citations eye): strip every citation overlay
   so the draft reads like a finished page — the claim tints/underlines, the
   stance + context glyphs, the footnote marker chips, and the whole Footnotes
   apparatus at the foot of the page. Nothing is deleted: every span, source,
   marker and note stays in the DOM and is still renumbered in the background —
   clean-read only suppresses the rendering, so toggling it back shows everything
   again, untouched. !important is needed because the light-mode (.nr-light) and
   stance/context highlight rules out-specify this one — without it, turning
   Citations off would clear the numbers but leave the highlights on paper. */
.md-preview.nr-no-cites .claim-src { background: transparent !important; border-bottom-color: transparent !important; cursor: text; }
.md-preview.nr-no-cites .claim-src::after { display: none !important; }
.md-preview.nr-no-cites .md-cite { display: none; }
.md-preview.nr-no-cites ol.nr-fnotes { display: none; }
/* A manual highlight (the highlighter pen — an inline background-color baked onto
   the span) shares the citation yellow, so on a clean read it reads as a citation
   that failed to turn off. Treat it as part of the annotation layer and suppress it
   too. Inline styles only yield to !important; match the highlighter's yellow in
   either serialization. Toggling citations back shows it again, untouched. */
.md-preview.nr-no-cites [style*="255, 236, 1"],
.md-preview.nr-no-cites [style*="255,236,1"] { background-color: transparent !important; }
/* "armed" — a source is picked and waiting for you to select the words it
   backs; the page invites a text selection */
.md-preview.nr-arming { cursor: text; box-shadow: inset 0 0 0 2px var(--yellow); }
.md-preview.nr-arming ::selection { background: rgba(255,236,1,.6); color: var(--ink); }
.md-preview pre { background: var(--nr-code-bg, rgba(255,255,255,.06)); padding: 10px 13px; font-family: var(--mono); font-size: 13px; line-height: 1.5; overflow-x: auto; margin: 0 0 13px; }
.md-preview ol { margin: 0 0 13px; padding-left: 22px; }
.md-preview strong { font-weight: 700; }
.md-preview:focus { outline: none; }

/* poetry / verse block — serif italic, line breaks preserved */
.md-preview pre.verse { background: transparent; font-family: var(--serif); font-style: italic; font-size: 16.5px; line-height: 1.7; padding: 4px 0 4px 18px; border-left: 3px solid var(--yellow); white-space: pre-wrap; }

/* Footnotes — the Substack-style notes list at the foot of the editor page.
   The "Footnotes" label is a CSS ::before so it can't be typed over or deleted;
   each note is one editable <li>, numbered natively by the <ol> (kept in marker
   order by renumberFootnotes). The inline reference is a .md-cite[data-fn] sup. */
.md-preview ol.nr-fnotes {
  list-style: decimal inside;
  margin: 32px 0 0; padding: 15px 0 2px;
  border-top: 2px solid var(--nr-line-strong, rgba(255,255,255,.22));
}
.md-preview ol.nr-fnotes::before {
  content: "Footnotes"; display: block;
  font-family: var(--cond); text-transform: uppercase; letter-spacing: .08em;
  font-size: 12px; font-weight: 700; color: var(--nr-muted, #8c8676); margin: 0 0 10px;
}
.md-preview li.nr-fnote {
  font-family: var(--serif); font-size: 15px; line-height: 1.55;
  margin: 0 0 7px; padding-left: 4px; color: var(--nr-soft, #b3ad9c);
}
.md-preview li.nr-fnote::marker { font-family: var(--mono); font-size: 12px; color: var(--nr-muted, #8c8676); }
.md-preview li.nr-fnote:empty::before,
.md-preview li.nr-fnote:has(> br:only-child)::before {
  content: attr(data-ph); color: var(--nr-muted, #8c8676); pointer-events: none;
}
.newsroom.nr-light .md-preview ol.nr-fnotes { border-top-color: var(--rule, rgba(22,20,13,.18)); }

/* composer widgets dropped into the newsroom editor */
.md-preview .cmp-widget { border-color: var(--nr-line-strong, rgba(255,255,255,.3)); background: var(--nr-code-bg, rgba(255,255,255,.04)); margin: 0 0 13px; }
.md-preview .cmp-widget-h .np-mono { color: var(--yellow); }
.newsroom.nr-light .md-preview .cmp-widget-h .np-mono { color: var(--data); }
.md-preview .cmp-widget-b span { color: var(--nr-soft, #b3ad9c); }
.md-preview .cmp-widget-f { color: var(--nr-muted, #8c8676); }
.md-preview .cmp-tag { color: var(--nr-soft, #b3ad9c); border-color: var(--nr-line-strong, rgba(255,255,255,.25)); }

/* The dark section-nav's column strip contains its own overflow at EVERY width,
   not just on phones: when the bar gets tight the tabs scroll inside this box
   instead of spilling out and overlapping the search field beside them (the
   classic flex min-content leak that made "Sources Archive" sit under the search
   placeholder on a narrow window). The mobile layer below adds the swipe mask +
   lets the strip grow to fill the bar. */
.npj-nav-cols { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.npj-nav-cols::-webkit-scrollbar { display: none; }
/* On desktop the tabs are load-bearing labels, so they keep their full width and
   the search field (which can shrink to nothing) yields the space instead — no
   clipped "Sources Archive". On phones the mobile layer's `flex: 1 1 auto` below
   wins, turning the strip back into a swipeable scroller. */
@media (min-width: 761px) { .npj-nav-cols { flex-shrink: 0; } }

/* ============================================================
   Front page: cover (2/3) + rail (1/3) + overflow grid
   The lineup is a two-column grid — cover story left, a rail of
   compact briefs right. align-items:start lets the rail size to
   its own contents; align-self:stretch on the rail makes its
   left rule run the full height of the cover, so the space below
   the last brief reads as deliberate headroom for more stories.
   Collapses to a single stacked column on phones (below).
   ============================================================ */
.npj-front-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 44px;
  align-items: start;
}
.npj-front-main { min-width: 0; }
.npj-front-side {
  min-width: 0;
  align-self: stretch;
  border-left: 1.5px solid var(--ink);
  padding-left: 28px;
}
.npj-side-head {
  padding-bottom: 12px;
  margin-bottom: 2px;
  border-bottom: 2.5px solid var(--ink);
}
.npj-side-item { padding: 20px 0; border-top: 1px solid var(--rule); }
.npj-side-item:first-of-type { border-top: none; padding-top: 18px; }
.npj-feed-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px 36px;
}
.npj-feed-item { min-width: 0; }
/* Mid-width desktops/tablets: two overflow columns read better than three. */
@media (max-width: 1080px) and (min-width: 761px) {
  .npj-feed-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ============================================================
   Responsive layer
   The prototype shipped desktop-only (no media queries at all).
   These make the public site AND the newsroom usable on phones.
   Much of the app is inline-styled, so a handful of class hooks
   (npj-*) are overridden with !important where unavoidable; the
   newsroom's 3-column editor collapses to a tabbed single column
   via the useIsMobile() hook in JS rather than CSS.
   ============================================================ */
@media (max-width: 760px) {
  :root { --measure: 92vw; }

  .npj-hide-sm { display: none !important; }

  /* masthead */
  .npj-masthead { padding: 12px 16px 16px !important; }
  .npj-logo { height: 76px !important; }
  .npj-nav-inner { padding: 0 8px !important; }
  .npj-nav-inner button { padding: 0 14px !important; font-size: 14px !important; }
  .npj-search { display: none !important; }
  /* The section nav is too wide for a phone (4 columns + the Submit CTA). Instead
     of letting it shove the whole page wider than the viewport — which forced a
     sideways scroll on every route and pushed "Submit a story" off-screen — the
     columns become a self-contained swipeable strip and the CTA stays pinned. */
  .npj-nav-cols { flex: 1 1 auto; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    /* a soft right-edge fade signals "swipe for more columns" and keeps the last
       partly-shown column from butting hard against the pinned Submit button */
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent); }
  .npj-nav-cols::-webkit-scrollbar { display: none; }
  .npj-nav-spacer { display: none !important; }
  .npj-footer-inner { padding: 18px 16px !important; }

  /* front page — the cover + rail + overflow grid all collapse to one column */
  .npj-cover { gap: 24px !important; }
  .npj-cover-h { font-size: 40px !important; }
  .npj-front-grid { grid-template-columns: 1fr !important; gap: 0 !important; }
  .npj-front-side {
    border-left: none !important;
    padding-left: 0 !important;
    margin-top: 30px !important;
    border-top: 2.5px solid var(--ink) !important;
  }
  .npj-side-head { border-bottom: none !important; padding-bottom: 0 !important; margin: 14px 0 2px !important; }
  .npj-feed-grid { grid-template-columns: 1fr !important; gap: 0 !important; }
  .npj-feed-item { padding: 22px 0 !important; border-top: 1px solid var(--rule); }
  .npj-feed-item:first-child { border-top: none !important; }
  .npj-more-row { grid-template-columns: 100px 1fr !important; }
  .npj-more-row > span:last-child { display: none !important; }
  .npj-empty-h { font-size: 40px !important; }

  /* submit */
  .npj-two { grid-template-columns: 1fr !important; }
  .npj-feature-row { flex-direction: column !important; gap: 18px !important; }
  .npj-submit-h { font-size: 40px !important; }

  /* keep wide centered columns inside the viewport */
  main { padding-left: 16px !important; padding-right: 16px !important; }
}

@media (max-width: 460px) {
  .npj-cover-h, .npj-empty-h, .npj-submit-h { font-size: 32px !important; }
  /* article reader hero — keeps the headline inside the column on small phones */
  .npj-article-h { font-size: 36px !important; }
  .npj-logo { height: 62px !important; }
}

/* ============ grounding workspace (GroundingWorkspace.jsx) ============ */
/* Citey, drawn: the bent-wire operator's idle motion + the table's row flash */
@keyframes rowflash { 0%, 28% { background-color: rgba(255,236,1,.22); } 100% { background-color: rgba(255,236,1,0); } }

/* DataTable: a clickable row (e.g. a source you can open) gets a hover cue so
   "click a row to open it below" reads as interactive on a pointer device. */
.npj-rrow { transition: background-color .12s ease; }
.npj-rrow:hover { background-color: rgba(255,236,1,.08) !important; }
@keyframes citey-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes citey-blink { 0%, 93%, 100% { transform: scaleY(1); } 96% { transform: scaleY(.12); } }
@keyframes citey-pop { 0% { transform: scale(.55) rotate(-7deg); } 60% { transform: scale(1.12) rotate(2.5deg); } 100% { transform: scale(1) rotate(0); } }
@keyframes citey-hop { 0%, 100% { transform: translateY(0) scaleY(1); } 28% { transform: translateY(-13px) scaleY(1.06); } 52% { transform: translateY(0) scaleY(.9); } 74% { transform: translateY(-5px) scaleY(1.02); } }
@keyframes citey-wander { 0%, 100% { transform: translate(0, 0); } 18% { transform: translate(2.5px, -1px); } 38% { transform: translate(-2px, 1px); } 58% { transform: translate(1px, 2px); } 78% { transform: translate(-1.5px, -1.5px); } }
@keyframes citey-wave { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(-16deg); } }
@keyframes star-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* ---- the corner-sprite (Citey.jsx) idle + interstitial motion ----
   "boil": the hand-drawn wobble — bent wire never sits perfectly still.
   "morph": the interstitial Citey plays when his shape CHANGES state, so the
   reshape reads as a deliberate transformation instead of a hard glyph swap.
   "breathe": a barely-there scale so an idle, quiet Citey still feels alive. */
@keyframes citey-boil {
  0%   { transform: translate(0,0) rotate(0deg); }
  25%  { transform: translate(.5px,-.4px) rotate(.45deg); }
  50%  { transform: translate(-.4px,.5px) rotate(-.4deg); }
  75%  { transform: translate(.4px,.3px) rotate(.35deg); }
  100% { transform: translate(0,0) rotate(0deg); }
}
@keyframes citey-morph {
  0%   { transform: scale(.6) rotate(-9deg);  opacity: .25; }
  45%  { transform: scale(1.14) rotate(4.5deg); opacity: 1; }
  72%  { transform: scale(.95) rotate(-1.6deg); }
  100% { transform: scale(1) rotate(0);        opacity: 1; }
}
@keyframes citey-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.035); } }
@keyframes citey-eye-wander { 0%, 100% { transform: translate(0,0); } 22% { transform: translate(1.4px,-.8px); } 50% { transform: translate(-1.2px,.7px); } 76% { transform: translate(.8px,1px); } }

/* Motion-sensitive readers: Citey holds still (state still reads, just calm). */
@media (prefers-reduced-motion: reduce) {
  [data-citey-anim] *, [data-citey-anim] { animation: none !important; transition: none !important; }
}

/* ════════════════════════════════════════════════════════════════════════
   Image carousel + fullscreen viewer (Splide + GLightbox, vendored).
   The reader renders a {type:'gallery'} block as <Carousel> (app/shared.jsx);
   any single photo opens the same zoomable viewer via <ZoomImg>. We ship
   splide-core (no theme), so the arrows/dots are themed here to ink/paper.
   ════════════════════════════════════════════════════════════════════════ */

/* single photo: a click opens the fullscreen viewer — reset the button chrome
   so the image still sits flush, and hint the zoom affordance */
.npj-zoom { display: block; width: 100%; margin: 0; padding: 0; border: 0; background: none; cursor: zoom-in; font: inherit; color: inherit; }
.npj-zoom:focus-visible { outline: 2px solid var(--yellow, #ffec01); outline-offset: 3px; }

/* ── reader carousel ─────────────────────────────────────────────────────── */
.npj-carousel-fig { margin: 26px 0; }
.npj-carousel { position: relative; }
.npj-carousel .splide__slide { display: flex; flex-direction: column; }
/* a uniform stage: the WHOLE photo shows (object-fit: contain in the slide's
   own <img>), wide panorama or tall portrait, on a quiet ground — never the
   fixed-height letterbox crop the user was fighting. Click = full size. */
.npj-carousel-slide {
  display: block; width: 100%; height: 58vh; max-height: 560px; min-height: 260px;
  margin: 0; padding: 0; border: 1.5px solid var(--ink, #16140d);
  background: var(--paper-2, #efe9d8); cursor: zoom-in; overflow: hidden;
}
.npj-carousel-slide:focus-visible { outline: 2px solid var(--yellow, #ffec01); outline-offset: 3px; }
.npj-carousel-slidecap { font-family: var(--mono); font-size: 11px; color: var(--ink-soft, #3a362b); margin-top: 6px; text-align: center; }
.npj-carousel-cap { font-family: var(--mono); font-size: 11.5px; font-style: italic; color: var(--ink-soft, #3a362b); margin-top: 12px; text-align: center; }
/* a quiet count chip so it reads as a gallery at a glance */
.npj-carousel-count {
  position: absolute; top: 8px; right: 8px; z-index: 2; pointer-events: none;
  background: rgba(22,20,13,.72); color: #fff; font-family: var(--mono); font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase; padding: 3px 7px; border-radius: 3px;
}
/* arrows + dots — splide-core ships NO arrow rules and no theme colors, so we
   position AND skin them here (ink on paper, square to match the masthead). */
.npj-carousel .splide__arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  display: flex; align-items: center; justify-content: center; padding: 0; border: 0;
  width: 2.4em; height: 2.4em; cursor: pointer; border-radius: 0;
  background: var(--ink, #16140d); opacity: .9; transition: opacity .12s, background .12s;
}
.npj-carousel .splide__arrow--prev { left: 8px; }
.npj-carousel .splide__arrow--next { right: 8px; }
.npj-carousel .splide__arrow svg { fill: var(--paper, #f6f1e4); width: 1.1em; height: 1.1em; }
.npj-carousel .splide__arrow--prev svg { transform: scaleX(-1); }
.npj-carousel .splide__arrow:hover:not(:disabled) { background: var(--yellow-deep, #b9a800); opacity: 1; }
.npj-carousel .splide__arrow:disabled { opacity: .28; cursor: default; }
.npj-carousel .splide__pagination { position: static; margin-top: 12px; }
.npj-carousel .splide__pagination__page { width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%; background: var(--rule, rgba(22,20,13,.28)); margin: 0 4px; transition: transform .12s, background .12s; }
.npj-carousel .splide__pagination__page.is-active { background: var(--ink, #16140d); transform: scale(1.3); }
.npj-carousel .splide__track:focus-visible { outline: 2px solid var(--yellow, #ffec01); outline-offset: 2px; }
/* no-Splide fallback: if the library is blocked, reveal the slides (core hides
   .splide until JS inits) as a plain horizontal scroll-strip */
.npj-carousel--raw { visibility: visible !important; }
.npj-carousel--raw .splide__track { overflow-x: auto; }
.npj-carousel--raw .splide__list { gap: 12px; }
.npj-carousel--raw .splide__slide { flex: 0 0 86%; }

/* ── editor carousel (Newsroom canvas) ───────────────────────────────────── */
/* the slides lay out as a horizontal, scrollable strip of drop targets so the
   author sees every photo at once; the figure itself rides the same 115% bleed
   a single inline image does (styles above). */
.cmp-carousel .cmp-carousel-track {
  display: flex; gap: 12px; overflow-x: auto; padding: 2px 2px 8px;
  scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
}
.cmp-carousel .cmp-slide { position: relative; flex: 0 0 clamp(180px, 42%, 280px); scroll-snap-align: start; }
.cmp-carousel .cmp-slide image-slot { width: 100% !important; }
/* the per-slide remove ✕ and the add chip — quiet until you hover the figure */
.cmp-carousel .cmp-slide-rm {
  position: absolute; top: 6px; right: 6px; z-index: 3; width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  border-radius: 50%; background: rgba(22,20,13,.72); color: #fff; font-size: 12px;
  line-height: 1; opacity: 0; transition: opacity .12s, background .12s; user-select: none;
}
.cmp-carousel:hover .cmp-slide-rm, .cmp-carousel .cmp-slide:hover .cmp-slide-rm { opacity: 1; }
.cmp-carousel .cmp-slide-rm:hover { background: var(--reject, #b3261e); }
.cmp-carousel .cmp-carousel-add {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer; margin-top: 6px;
  padding: 5px 10px; border: 1px dashed var(--nr-line-strong, var(--ink)); border-radius: 5px;
  color: var(--nr-muted, var(--ink-soft)); font-size: 11px; user-select: none; transition: color .12s, border-color .12s;
}
.cmp-carousel .cmp-carousel-add:hover { color: var(--ink); border-color: var(--ink); }
.cmp-carousel .cmp-slide .cmp-cap, .cmp-carousel .cmp-slide .cmp-credit, .cmp-carousel .cmp-slide .cmp-desc { font-size: 10.5px; }

/* ── fullscreen viewer (GLightbox) brand touches ─────────────────────────── */
.glightbox-clean .gslide-description { background: var(--paper, #f6f1e4); }
.glightbox-clean .gslide-desc { font-family: var(--mono); font-size: 12.5px; color: var(--ink, #16140d); }
.gbtn.gclose, .gbtn.gnext, .gbtn.gprev { background-color: rgba(22,20,13,.55); }

@media (max-width: 760px) {
  .npj-carousel-slide { height: 46vh; min-height: 200px; }
  .cmp-carousel .cmp-slide { flex-basis: 78%; }
}
