/* What positioning buys you — /resources/niching-down/
   ────────────────────────────────────────────────────
   Built on golden.css tokens and the shared .res-* chrome in resources.css.

   One card, two blocks: the console (question, slider, and the pitch the slider
   rewrites), then the chart. The slider is the whole tool — everything below it
   is its output — so it leads, and the chart is a display rather than a second
   control.

   Series colours follow the res-titles precedent (honey / clay / brick), with
   one change: honey is structural and never text, and each line here is named
   by a label in its own colour, so the three series use text-safe values only.
   Clay carries the payoff — it's the interactive accent and the one curve to
   look at. Honey still does the structural work as the moving column band. */

/* Tokens scope to the page wrapper, matching .tf / .lv / .ms / .ti. */
.nd {
  /* data palette by role (SYSTEM.md): hero clay over a honey-tint area fill,
     context ink, supporting honey dashed */
  --nd-jobs: var(--ink);
  --nd-rate: var(--honey);
  --nd-got: var(--clay);
  --nd-got-fill: var(--honey);
  /* Room for the end dots to hang past the plot without being clipped. */
  --nd-bleed: 12px;
}


/* No bottom padding: the coda's own margin (--space-block) is the whole
   instrument-to-coda gap, as on the other tools. */
.nd-section {
  border-top: 0;
  padding-top: 32px;
  padding-bottom: 0;
}
.res-intro + .nd-section { padding-top: 0; }

/* ─── The card ────────────────────────────────────────────────────────────── */

.nd-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: clamp(18px, 3vw, 32px);
  overflow: hidden;
}
@media (max-width: 680px) {
  .nd-card { border-radius: var(--r-lg); padding: 16px; }
}
/* Above 980 the chart is the card's last row (no zoom tray), and the SVG's own
   bottom pad (PAD_B in the script, about 32px rendered) already clears the end
   dots. Card padding on top of it left 65px under the baseline against 33px
   above the first label; without it the two insets match. The no-JS card,
   whose last row is text, takes its padding back under scripting: none at
   the end of the file; this rule doesn't match on scripting: enabled, which
   an engine without the feature never matches. */
@media (min-width: 981px) {
  .nd-card { padding-bottom: 0; }
}

/* ─── Console ─────────────────────────────────────────────────────────────── */

.nd-console { padding: 0; }

/* Mono uppercase, matching the label register the tool pages use elsewhere.
   These used to borrow .res-eyebrow; upstream stripped eyebrows from tool pages,
   so they carry their own type now. */
.nd-question,
.nd-said {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--fs-label-ls);
  text-transform: uppercase;
  color: var(--clay);
}

.nd-question { margin-bottom: 16px; }

/* Broad and Niche sit under the ends of the track rather than flanking it, so
   the track keeps the card's full content width — which is what lets the thumb
   line up with the chart's x-axis below. */
.nd-range-wrap { margin-bottom: 30px; }

.nd-range-ends {
  display: flex;
  justify-content: space-between;
  margin-top: 9px;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--fs-label-ls);
  text-transform: uppercase;
  color: var(--muted);
}

/* Thumb and track are the shared .slider.slider--unipolar.slider--clay.
   --fill is that track's stop. It follows the thumb centre, not the raw
   percentage, because a native range insets the travel by half a thumb.
   The input is one thumb wider and pulled back by half so that centre
   lands on the chart axis below. The ring matches the card it sits on. */
.nd-range {
  --slider-surface: var(--card);
  --nd-thumb: 22px;
  --fill: calc(var(--nd-f, 0) * (100% - var(--nd-thumb)) + var(--nd-thumb) / 2);
  display: block;
  width: calc(100% + var(--nd-thumb));
  margin: 0 calc(var(--nd-thumb) / -2);
  padding: 0;
  touch-action: none;
}
.nd-range:active { cursor: grabbing; }
.nd-range:focus { outline: none; }

/* ─── What the slider produces ────────────────────────────────────────────── */

.nd-said {
  margin-bottom: 8px;
  color: var(--muted);
}

/* Every stop's pitch and detail share one grid cell. The visible one is the
   current stop; the rest (.nd-say-size) are hidden copies, so the box is as
   tall as the tallest stop at every width from the first frame and the chart
   never moves while you drag, with no script measuring it. */
.nd-says { display: grid; }
.nd-say { grid-area: 1 / 1; min-width: 0; }
.nd-say-size { visibility: hidden; }

/* The headline of the block, and the whole answer to the slider. Serif italic
   because it's speech. Balanced, so a short pitch breaks between its sentences
   rather than leaving "I" hanging at the end of the first line.
   The measure sets the empty space under the short stops: .nd-says reserves
   the tallest stop's height, and at 44ch the longest pitch takes two lines,
   not three. At <=680 the column is narrower than that anyway; there it
   stays 28ch. */
.nd-pitch {
  font-family: var(--font-serif);
  font-size: var(--fs-lead);
  font-style: italic;
  line-height: var(--fs-lead-lh);
  letter-spacing: -0.01em;
  color: var(--ink);
  max-width: 44ch;
  margin: 0 0 10px;
  text-wrap: balance;
  /* Hang the opening quote so the first letter lines up with the label above.
     0.426em is the quote's advance in Plex Serif Italic (0.436em) less the
     -0.01em tracking. hanging-punctuation would do it, but only in WebKit. */
  text-indent: -0.426em;
}

.nd-detail {
  font-family: var(--font-sans, var(--font-display));
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--ink);
  max-width: 62ch;
  margin: 0;
}

/* ─── Chart ───────────────────────────────────────────────────────────────── */

.nd-plot { padding: 0; margin-top: var(--space-24); }

/* Zoom sits in the figure's footer row, right-aligned, and only at <=980
   (SYSTEM.md); above that the chart fits without it. The row is laid out
   from the first frame when scripts run, so the tray appearing doesn't
   shift the card.
   Only scripting: none takes the row out, as for the tray (resources.css),
   so an engine without the `scripting` feature keeps the row. */
.nd-plot-bar {
  display: none;
  justify-content: flex-end;
  min-height: 40px;
  margin-top: 10px;
}
@media (max-width: 980px) { .nd-plot-bar { display: flex; } }
@media (max-width: 680px) { .nd-plot-bar { min-height: 52px; } }
@media (scripting: none) { .nd-plot-bar { display: none; } }

/* The plot runs edge to edge of the card's content box, so the dots at either
   end hang half their width past it. The negative margin goes on the wrap and
   the padding on the scroll box inside it, so the wrap's edge fades cover the
   scrollport exactly: zoomed in, a line fades out at the clip edge instead of
   reappearing for 12px past the fade. */
.nd-scrollwrap { margin-inline: calc(var(--nd-bleed) * -1); }
/* The trailing bleed is the canvas's margin, not the scroll box's padding:
   WebKit leaves trailing padding out of the scroll extent and Chromium
   counts it, while both count a margin. res-zoom.js takes the margin out
   of the Fit width. */
.nd-scroll { padding-left: var(--nd-bleed); }

.nd-scroll-inner { margin-right: var(--nd-bleed); }

/* ── Column headers ──
   Columns are nearest-stop bands, so the outer two are half-width — hence the
   1-2-2-2-1 track list, whose boundaries land exactly where the SVG draws its
   dividers. minmax(0, …) keeps a label's width from widening its track, so the
   boundaries hold at every size; a label too long for its band wraps instead.
   The row is ruled top and bottom and carries its own cell rules, so it reads
   as the header row inside the chart's grid rather than a caption floating
   above it. */
.nd-cols {
  display: grid;
  grid-template-columns:
    minmax(0, 1fr) minmax(0, 2fr) minmax(0, 2fr) minmax(0, 2fr) minmax(0, 1fr);
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}

/* Flex so a label sits on the row's bottom rule, next to the selected marker,
   whether it took one line or three. The label is sticky on both sides
   inside its cell, so zoomed in it stays in the visible part of its column:
   it holds the left edge while the column runs off the left and the right
   edge while it runs off the right, over the edge fade.
   The inline inset is the label's measure (--nd-in-l / --nd-in-r), not
   the cell's padding: the cell's content box is the box a sticky label
   can move in, so with no padding a label can hold the visible part of
   its column until that part is narrower than the label. */
.nd-col-btn {
  --nd-in-l: 9px;
  --nd-in-r: 9px;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  min-width: 0;
  padding: 10px 0 11px;
  background: none;
  border: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--fs-label-ls);
  line-height: 1.25;
  text-wrap: balance;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s ease;
}

/* A name holds the scrollport's edges, where the plot clips: the scroll
   box pads its leading bleed, which sticky insets by, so left: takes the
   bleed back; there is no trailing padding, so right: is 0. At rest the
   names sit inside the card's content box as before. min-width keeps a
   word wider than the measure from being cut to it. */
.nd-col-name {
  position: sticky;
  left: calc(var(--nd-bleed) * -1);
  right: 0;
  z-index: 4;
  min-width: min-content;
  max-width: calc(100% - var(--nd-in-l) - var(--nd-in-r));
  background: var(--card);
}
/* Below about 380px a long word outgrows its column: "design" (about 40px)
   in the 30-35px end column, "discipline" in a 63px middle one. At 320
   "design" ran 8.6px into "+ a discipline". Hyphenation breaks only a word
   that can't fit (de-sign, disci-pline), and nothing changes from 390 up. */
@media (max-width: 380px) {
  .nd-col-name { -webkit-hyphens: auto; hyphens: auto; }
}

/* Continue the column grid up through the header. Drawn on the cell rather than
   inset, so each rule lands on its cell's true edge — the buttons stretch for
   that, which also gives them a full-cell hit area. */
.nd-col-btn + .nd-col-btn { border-left: 1px solid var(--hair); }

/* No outside inset on the end two — their stop is the plot edge itself. */
.nd-col-btn:first-child { justify-content: flex-start; text-align: left; --nd-in-l: 0px; }
.nd-col-btn:last-child { justify-content: flex-end; text-align: right; --nd-in-r: 0px; }

/* "+ stage & city" is about 102px of mono in a half-width band of 106-108px
   above 980. With the usual 9px inset it wrapped to two right-aligned lines;
   on one line it clears the band's left rule by 4px or more. */
@media (min-width: 981px) {
  .nd-col-btn:last-child { white-space: nowrap; --nd-in-l: 4px; }
}

.nd-col-btn:hover { color: var(--ink); }

.nd-col-btn[data-current="true"] {
  color: var(--clay);
  box-shadow: inset 0 -2px 0 var(--clay);
}

.nd-col-btn:focus-visible {
  outline: 2px solid var(--clay);
  outline-offset: -3px;
}

.nd-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* ── Grid ── */

.nd-col-div { stroke: var(--hair); stroke-width: 1; }

/* The band under the nearest stop. Honey doing structural work — faint enough
   to sit behind the curves, but it moves with the slider, which is most of what
   ties the control to the chart. */
.nd-col-band { fill: color-mix(in srgb, var(--honey) 10%, transparent); }

.nd-base { stroke: var(--border); stroke-width: 1.5; stroke-linecap: round; }

/* ── Lines ── */

.nd-line { fill: none; stroke-linecap: round; stroke-linejoin: round; }

/* The believed trade-off: lighter than the payoff, but both are real claims and
   drawn as such. The dash keeps the two separable without relying on colour. */
.nd-line-jobs { stroke: var(--nd-jobs); stroke-width: 2; }
.nd-line-rate { stroke: var(--nd-rate); stroke-width: 2; stroke-dasharray: 9 5; }

/* The payoff: the one saturated mark on the page. */
.nd-line-got { stroke: var(--nd-got); stroke-width: 3.5; }

/* Draw the payoff curve in on load so the rise-and-fall is a motion, not just a
   shape. */
@media (prefers-reduced-motion: no-preference) {
  .nd-line-got {
    stroke-dasharray: 101;
    animation: nd-draw 1100ms cubic-bezier(0.25, 0.8, 0.3, 1) forwards;
  }
  .nd-area { animation: nd-fade 700ms 500ms ease both; }
}

/* The line has pathLength="100", so 101 covers it at any zoom width. */
@keyframes nd-draw {
  from { stroke-dashoffset: 101; }
  to { stroke-dashoffset: 0; }
}

@keyframes nd-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.nd-peak-dot {
  fill: var(--card);
  stroke: var(--nd-got);
  stroke-width: 2;
  opacity: 0.75;
}

/* ── In-chart labels, so there's no legend to decode ── */

.nd-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.02em;
}

.nd-label-jobs { fill: var(--nd-jobs); }
.nd-label-rate { fill: var(--clay); }
.nd-label-got { fill: var(--nd-got); font-size: 12px; }

/* The narrow layout's stand-in for those labels: same words, same strokes. */
.nd-key { display: none; }
.nd-key li { display: inline-flex; align-items: center; gap: 7px; }
.nd-key line { stroke-linecap: round; }
.nd-key-jobs { color: var(--nd-jobs); }
.nd-key-jobs line { stroke: var(--nd-jobs); stroke-width: 2; }
.nd-key-got { color: var(--nd-got); }
.nd-key-got line { stroke: var(--nd-got); stroke-width: 3.5; }
.nd-key-rate { color: var(--clay); }
.nd-key-rate line { stroke: var(--nd-rate); stroke-width: 2; stroke-dasharray: 5 3; }

/* ── Playhead: where the slider has put you ── */

.nd-playhead { stroke: var(--ink); stroke-width: 1.5; opacity: 0.26; }

.nd-dot { stroke: var(--card); stroke-width: 2; }
.nd-dot-jobs { fill: var(--nd-jobs); }
.nd-dot-rate { fill: var(--nd-rate); }
.nd-dot-got { fill: var(--nd-got); stroke-width: 2.5; }

/* ─── Narrow ──────────────────────────────────────────────────────────────── */

@media (max-width: 980px) {
  /* The in-chart labels can't stay legible here and would cross into
     neighbouring columns. The column headers still name the axis. */
  .nd-label { display: none; }
  .nd-key {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin: 0 0 14px;
    padding: 0;
    list-style: none;
    font-family: var(--font-mono);
    font-size: var(--fs-meta);
    letter-spacing: var(--fs-meta-ls);
  }
}

@media (max-width: 680px) {
  .nd-console { padding: 0; }
  .nd-pitch { max-width: 28ch; }
  .nd-plot { padding: 0; }

  /* At phone width the end bands are under 40px, so every point of padding
     and tracking counts: "design" only clears its band without them. */
  .nd-col-btn { font-size: var(--fs-label); letter-spacing: 0; padding: 9px 0 10px; --nd-in-l: 4px; --nd-in-r: 4px; }
  .nd-col-btn:first-child { --nd-in-l: 0px; --nd-in-r: 2px; }
  .nd-col-btn:last-child { --nd-in-l: 2px; --nd-in-r: 0px; }
  .nd-col-btn:first-child,
  .nd-col-btn:last-child { position: relative; }
  .nd-col-btn:first-child::before,
  .nd-col-btn:last-child::before {
    content: "";
    position: absolute;
    inset: 0;
  }
  .nd-col-btn:first-child::before { left: min(0px, calc(100% - 44px)); }
  .nd-col-btn:last-child::before { right: min(0px, calc(100% - 44px)); }
}

/* The shared thumb stays 22px. The input grows so the touch target is 44px,
   and the 3px track stays centred in that box. */
@media (pointer: coarse) {
  .nd-range {
    height: 44px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 100% 3px;
  }
  .nd-col-btn { min-height: 44px; }
}

/* ─── Without JavaScript ──────────────────────────────────────────────────── */

/* Without JavaScript the card holds stop 0's pitch and detail, which the
   template prints. The slider and the chart only work together with the
   script, so they hide rather than sit there dead; the no-JS line sits
   above the card to say why, and the card is only as tall as stop 0. A
   media query, not a .has-js class: browsers paint before the end-of-body
   script runs, and a class added then would drop the whole chart in after
   first paint. Last in the file so it wins over the display rules above. */
@media (scripting: none) {
  .nd-question,
  .nd-range-wrap,
  .nd-plot,
  .nd-say-size { display: none; }
}
@media (scripting: none) and (min-width: 981px) {
  .nd-card { padding-bottom: clamp(18px, 3vw, 32px); }
}

/* With scripting on, the card is laid out as the script runs it from the
   first frame, and the template has drawn the chart at stop 0. Until
   res-niching-down.js runs (.has-js), or if it never does, the slider and
   the column buttons hold their space hidden, so nothing that only the
   script can work sits there dead, and the script turning them on moves
   nothing. The column names stay, as the chart's heads. */
.nd:not(.has-js) .nd-range-wrap,
.nd:not(.has-js) .nd-cols { visibility: hidden; }
.nd:not(.has-js) .nd-col-name { visibility: visible; }
.nd:not(.has-js) .nd-cols { pointer-events: none; }
.nd-nojs { margin: 0 0 var(--space-16); }
