/* ============================================================
   Navisurg Vision — writing

   Loaded after tokens / components / site.css, and depends on all three:
   the header, footer and .wrap are the landing page's, so a post sits in the
   same frame as the front page rather than looking like a second website.

   Only two things here are new rather than reused. Both are deliberate
   departures from the landing page and are marked where they occur:

     1. Body copy runs at 16px, not the design system's 14px --text-base.
        14px is sized for cards that are scanned. An 1,800-word argument is
        read, and read at that size it is tiring. The measure is capped at
        --measure-prose so the line length stays in range.

     2. There is one chart. The design system assigns mist blue to "data,
        charts" and signal orange to emphasis, so the two series take those
        two tokens and no new colour is invented. The pair was checked for
        colour-vision separation against the operating dark — worst adjacent
        deltaE 18.6 under deuteranopia, 21.1 normal vision, both well clear
        of the 8 threshold — and each curve is also labelled in place, so
        identity never rests on hue alone.
   ============================================================ */

:root {
  --prose-size: 16px;
  --prose-leading: 1.62;

  /* Deliberately px, not the 64ch of --measure-prose. `ch` resolves against
     the element's own font, so one token spent on a 16px paragraph, a 24px
     pull quote and a 32px display heading yields three different measures —
     702px, 951px and no cap at all. Each block below states its own. */
  --measure-body: 660px;
  --measure-wide: 820px;
}

/* An index with one post on it does not fill a screen, and a footer floating
   halfway up with nothing under it reads as a page that failed to load. Only
   the blog pages get this — the landing page is long enough not to need it. */
body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body > main { flex: 1 0 auto; }

/* ============================================================
   Index
   ============================================================ */
.writing { padding-top: var(--pad-band); padding-bottom: var(--pad-band); }

.writing__head { margin-bottom: var(--space-14); }
.writing__head .nsv-eyebrow { margin-bottom: var(--space-4); }
.writing__head p {
  margin-top: var(--space-4);
  font: var(--type-body-lg);
  color: var(--text-body);
  max-width: 46ch;
}

/* em, not ch, and on the heading itself — see the note on --measure-body. */
.writing__title {
  max-width: 11em;
  font-family: var(--font-display);
  font-weight: var(--weight-extralight);
  font-size: clamp(36px, 5vw, 60px);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  margin: 0;
}

/* Same ruled-row grammar as .reason and .lineage__row on the front page:
   a mono key in a fixed column, the substance beside it. */
.post-list { list-style: none; margin: 0; padding: 0; }

.post-row {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: var(--space-8);
  padding: var(--space-8) 0;
  border-top: var(--border-hairline-rule);
}
.post-list > li:last-child .post-row { border-bottom: var(--border-hairline-rule); }

.post-row__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-top: 6px;
}
.post-row__date { font: var(--type-mono); color: var(--text-heading); }
.post-row__read { font: var(--type-mono); color: var(--ink-500); }

.post-row__title {
  font-family: var(--font-display);
  font-weight: var(--weight-light);
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-normal);
  margin: 0 0 var(--space-3);
  max-width: 20em;
}
/* The whole title is the target, so it carries no underline of its own —
   the base `a` rule would draw an orange rule under a two-line heading. */
.post-row__title a { border-bottom: 0; color: var(--text-strong); }
.post-row__title a:hover { color: var(--accent); }

.post-row__standfirst {
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: 1.55;
  color: var(--text-body);
  margin: 0;
  max-width: 62ch;
}

.post-row__more {
  display: inline-block;
  margin-top: var(--space-4);
  font: var(--type-caps);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}
.post-row:hover .post-row__more { color: var(--accent); }

/* ============================================================
   Reading progress

   A 2px rule sitting on the header's bottom border, filling left to right
   as the article is read. It replaces that border rather than sitting under
   it, so the header keeps its exact height and nothing below it shifts.

   scaleX rather than width: a transform is composited, and this updates on
   every scroll frame.
   ============================================================ */
.read-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  pointer-events: none;
}
.read-progress__bar {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
}

/* ============================================================
   Post
   ============================================================ */
.post { padding-top: var(--space-12); padding-bottom: var(--pad-band); }

/* The prose keeps its own measure inside the first column; the rail sits at
   the right edge of the container, where the page already has margin going
   spare. The head, the closing card and the sources stay outside this grid
   and run the full width. */
.post__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 200px;
  gap: var(--space-16);
  align-items: start;
}

/* ---------- Section rail ---------- */
/* Hidden until post.js fills it, so a blocked script leaves a clean column
   rather than an empty box with a heading over it. */
.post__toc[hidden] { display: none; }

.post__toc {
  position: sticky;
  top: calc(var(--head-height) + var(--space-8));
}
.post__toc > .nsv-eyebrow { margin-bottom: var(--space-4); }

.post__toc-list {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
}

/* The left border is the track. Each item owns its segment of it, so the
   current section is marked by the rule beside it turning accent — no
   second element to keep in sync with the list. */
.post__toc-link {
  display: block;
  border: 0;
  border-left: var(--border-width-strong) solid var(--border-hairline);
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.4;
  color: var(--text-muted);
  transition: var(--transition-control);
}
.post__toc-link:hover { color: var(--text-strong); border-left-color: var(--border-strong); }
.post__toc-link.is-current {
  color: var(--text-strong);
  border-left-color: var(--accent);
}

.post__toc-top {
  display: inline-block;
  border: 0;
  font: var(--type-caps);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}
.post__toc-top:hover { color: var(--accent); }

.post__back {
  display: inline-block;
  border: 0;
  font: var(--type-caps);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--space-12);
}
.post__back:hover { color: var(--accent); }

.post__head { margin-bottom: var(--space-12); }
.post__head .nsv-eyebrow { margin-bottom: var(--space-5); }

/* The cap is in em, not ch, and it lives on the title rather than on the
   block around it. A ch or a percentage on .post__head would resolve against
   that element's own font — 14px DM Sans — and clamp a 64px display heading
   to roughly a quarter of the width it was drawn at. */
.post__title {
  max-width: 14em;
  font-family: var(--font-display);
  font-weight: var(--weight-extralight);
  font-size: clamp(36px, 5.2vw, 64px);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  margin: 0;
}

.post__standfirst {
  max-width: 56ch;
  margin: var(--space-6) 0 0;
  font-family: var(--font-sans);
  font-size: 18px;
  font-weight: var(--weight-light);
  line-height: 1.55;
  color: var(--text-heading);
}

.post__byline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: var(--border-hairline-rule);
  max-width: 52ch;
  font: var(--type-mono);
  color: var(--text-muted);
}
.post__byline span[aria-hidden] { color: var(--ink-500); }

/* ---------- Opening photograph ---------- */
/* Full-bleed, using the landing page's .scene band so it dissolves into the
   operating dark at both edges rather than sitting on it as a rectangle.
   --sink because a dark section runs above and below it.
   .post's own top padding sits above, so this only needs a bottom gap. */
.post__scene {
  margin: var(--space-4) 0 var(--space-14);
}

/* The monitor sits right of centre and the faces just above the middle.
   As the band crops on a narrow screen, this keeps both in frame. */
.post__scene .scene__img { object-position: 55% 50%; }

/* Deliberately captioned, where the landing page's two bands are not. The
   image is generated, and it shows a screen with a camera view on it — on a
   page about what software should do with camera views, an uncaptioned one
   could be taken for ours. One quiet line covers both.

   Quiet by size, not by colour. --text-muted on the operating dark is already
   3.8:1, under the 4.5:1 floor for text this size; the next step down the ramp
   is 1.7:1 and unreadable. Dimming further would make it decoration. */
.post__scene-credit {
  margin-top: var(--space-4);
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 72ch;
}

/* ---------- Prose ---------- */
/* The measure is capped on the children, not on .prose itself, so that the
   two blocks that need more room than a reading measure — the chart and the
   figure strips — can take it without overflowing a narrower parent. */
.prose {
  font-family: var(--font-sans);
  font-size: var(--prose-size);
  line-height: var(--prose-leading);
  color: var(--text-body);
}

.prose > * { max-width: var(--measure-body); }
.prose > * + * { margin-top: var(--space-6); }

.prose h2 {
  max-width: 20em;
  margin-top: var(--space-16);
  font-family: var(--font-display);
  font-weight: var(--weight-light);
  font-size: var(--text-2xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
}

.prose h3 {
  margin-top: var(--space-10);
  font-family: var(--font-display);
  font-weight: var(--weight-regular);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-normal);
  color: var(--text-strong);
}

.prose strong { color: var(--text-strong); font-weight: var(--weight-medium); }
.prose em { font-style: italic; }

.prose a { color: var(--text-strong); }

.prose ul, .prose ol { margin-left: 0; padding-left: var(--space-5); }
.prose li + li { margin-top: var(--space-3); }
.prose li::marker { color: var(--text-muted); }

/* A lead paragraph, one step up, used once at the top of the body. */
.prose .lead {
  font-size: 18px;
  line-height: 1.55;
  color: var(--text-heading);
}

/* ---------- The two approvals ---------- */
/* padding-left: 0 overrides the .prose ol rule — these rows are ruled, and an
   indent would push them off the left edge everything else is aligned to. */
.approvals { list-style: none; margin: var(--space-8) 0 0; padding-left: 0; }

.approval {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: var(--space-6);
  padding: var(--space-5) 0;
  border-top: var(--border-hairline-rule);
}
.approval:last-child { border-bottom: var(--border-hairline-rule); }
.approval__date { font: var(--type-mono); color: var(--accent); padding-top: 3px; }
.approval__body { margin: 0; font-size: var(--text-md); line-height: 1.55; }
.approval__body strong { color: var(--text-strong); }

/* ---------- Figure strip ---------- */
/* Numbers pulled out of the paragraphs so the argument can be checked at a
   glance. Same construction as .facts on the front page. */
.keyfacts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-8);
  max-width: var(--measure-wide);
  margin: var(--space-10) 0;
  padding: var(--space-8) 0;
  border-top: var(--border-hairline-rule);
  border-bottom: var(--border-hairline-rule);
}

.keyfact { display: flex; flex-direction: column; gap: var(--space-2); }
.keyfact__value {
  font-family: var(--font-display);
  font-weight: var(--weight-extralight);
  font-size: var(--text-2xl);
  line-height: var(--leading-flat);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
}
.keyfact__label {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--text-body);
}
.keyfact__source { font: var(--type-mono); color: var(--ink-500); }

/* ---------- Pull quote ---------- */
/* One step wider than the body measure, which is the point of it: the eye
   registers the break before it reads the sentence. */
.pull {
  max-width: 760px;
  margin: var(--space-12) 0;
  padding-left: var(--space-6);
  border-left: var(--border-width-strong) solid var(--accent);
  font-family: var(--font-display);
  font-weight: var(--weight-light);
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-normal);
  color: var(--text-strong);
}

/* ---------- Chart ---------- */
/* Breaks the prose measure: the curves need width to separate, and at 64ch
   the two peaks sit almost on top of each other. */
.figure {
  margin: var(--space-12) 0;
  max-width: var(--measure-wide);
}

.figure__frame {
  border: var(--border-width) solid var(--border-hairline);
  border-radius: var(--radius-card);
  padding: var(--space-6) var(--space-5) var(--space-4);
  background: var(--ink-900);
  overflow-x: auto;
}
.figure__frame svg { width: 100%; height: auto; min-width: 460px; }

.figure__title {
  font-family: var(--font-display);
  font-weight: var(--weight-regular);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-normal);
  color: var(--text-strong);
  margin: 0 0 var(--space-2);
}
.figure__sub {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text-muted);
  margin: 0 0 var(--space-6);
  max-width: 60ch;
}
.figure__caption {
  margin-top: var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 68ch;
}

/* The legend is always present for two series; the in-plot labels are the
   secondary encoding that keeps identity off hue alone. */
.figure__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  margin-bottom: var(--space-5);
}
.legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  color: var(--text-body);
}
.legend-item::before {
  content: "";
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: var(--swatch);
}

/* ---------- Closing card ---------- */
.post__cta {
  margin-top: var(--space-16);
  padding: var(--space-8);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-8);
  align-items: center;
}
.post__cta h2 {
  font-family: var(--font-display);
  font-weight: var(--weight-light);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-normal);
  color: var(--text-strong);
  margin: 0 0 var(--space-2);
}
.post__cta p {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.5;
  color: var(--text-body);
  margin: 0;
  max-width: 52ch;
}

/* ---------- References ---------- */
/* Every figure in the piece is checkable, which is the whole basis on which
   it asks to be believed. The list is numbered and the numbers are cited
   inline. */
.refs { margin-top: var(--space-16); }
.refs > .nsv-eyebrow { margin-bottom: var(--space-6); }

.refs__list {
  list-style: none;
  counter-reset: ref;
  margin: 0;
  padding: 0;
  max-width: 78ch;
}
/* Absolutely positioned counter rather than a two-column grid. Grid would
   make every inline child of the <li> — each <em>, each <a> — its own grid
   item, and auto-placement would drop them into the number column one after
   another. The reference has to stay one run of inline text. */
.refs__list li {
  counter-increment: ref;
  position: relative;
  padding: var(--space-4) 0 var(--space-4) 34px;
  border-top: var(--border-hairline-rule);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text-body);
}
.refs__list li:last-child { border-bottom: var(--border-hairline-rule); }
.refs__list li::before {
  content: counter(ref);
  position: absolute;
  left: 0;
  top: calc(var(--space-4) + 3px);
  font: var(--type-mono);
  color: var(--text-muted);
}
.refs__list a { overflow-wrap: break-word; }

/* Inline citation marker. Superscript without the line-height distortion
   <sup> causes in a 1.62 measure. */
.cite {
  font: var(--type-mono);
  font-size: 10px;
  vertical-align: super;
  line-height: 0;
  color: var(--text-muted);
  border: 0;
  padding-left: 2px;
}
a.cite:hover { color: var(--accent); }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1100px) {
  /* Below this the rail would take its 200px out of the reading measure.
     The progress bar carries on alone; it costs no width. */
  .post__body { grid-template-columns: minmax(0, 1fr); }
  .post__toc { display: none; }
}

@media (max-width: 860px) {
  .post-row,
  .approval { grid-template-columns: 1fr; gap: var(--space-3); }
  .post-row { gap: var(--space-4); }
  .post-row__meta { flex-direction: row; gap: var(--space-4); padding-top: 0; }
  .keyfacts { grid-template-columns: 1fr; gap: var(--space-8); }
  .post__cta { grid-template-columns: 1fr; gap: var(--space-6); }
}

@media (max-width: 560px) {
  .prose h2 { margin-top: var(--space-12); }
  .figure__frame { padding: var(--space-4) var(--space-3); }

  /* Let the chart shrink to the column instead of scrolling sideways: the
     whole argument of it is that the two peaks sit at different times, and a
     reader who can only see one peak at a time has been shown nothing. The
     labels are stepped up in viewBox units to survive the scale-down — at a
     ~340px frame the SVG renders at roughly 0.45, so 26 units lands near
     12px on screen. */
  .figure__frame svg { min-width: 0; }
  .fig-label { font-size: 26px; }
  .fig-axis-label { font-size: 19px; }
  .fig-note { font-size: 18px; }
}

/* ============================================================
   Chart marks

   Presentation attributes cannot read a custom property, so the SVG is
   styled from here. Two series, two brand tokens, nothing invented:
   signal orange is the design system's emphasis colour and mist blue is
   the one it nominates for data and charts.

   Text never wears a series colour — the labels are text tokens and the
   coloured dot beside each one carries the identity.
   ============================================================ */
.fig-axis  { stroke: var(--ink-600); stroke-width: 1; }
.fig-drop  { stroke: var(--ink-500); stroke-width: 1; }
.fig-brace { stroke: var(--ink-400); stroke-width: 1; }

.fig-curve {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.fig-curve--art { stroke: var(--orange-500); }
.fig-curve--ven { stroke: var(--blue-400); }
.fig-dot--art  { fill: var(--orange-500); }
.fig-dot--ven  { fill: var(--blue-400); }

.fig-label { fill: var(--text-heading); font-family: var(--font-sans); font-size: 13px; }
.fig-axis-label { fill: var(--text-muted); font-family: var(--font-sans); font-size: 12px; }
.fig-note { fill: var(--text-muted); font-family: var(--font-mono); font-size: 11px; letter-spacing: .04em; }

/* Current page in the header nav. The landing page has no current state
   because every link there is an anchor within it. */
.site-head__link--current { color: var(--text-strong); }
