/* ImpromptBrü — the NOTE PAGE. Loaded after ./site.css, which owns
   the property shell (header, footer, stream row, brew marker, chips, backlinks).
   This file owns one thing: what a note looks like once you are reading it.

   MEASURED AGAINST THE REAL CORPUS, not against a guess. 126 notes: 34 musings, 16
   borrowed, 60 topic hubs. Bodies run 23 words to 1,800+. 475 wikilinks of which 142
   point at nothing published. Zero notes carry a date. One of 16 borrowed notes has a
   structured source; none have a structured author. Every decision below is sized for
   those numbers, which is why several of them look pessimistic.

   RULES, same as site.css:
   1. Every value is a design-system custom property, or a color-mix of them. No hex.
   2. Every rule is a class on markup a template will emit, so each block ports to an
      .astro partial with the same class names and no CSS rewrite.
   3. Zero layout depends on JavaScript. Nothing here needs hydration, and the two
      interactive touches (footnote :target, heading anchors) are CSS and fragments.
   4. Every block is allowed to be ABSENT. A note with no date, no source, no topics,
      no backlinks and 23 words of body must still look finished. There are no
      placeholders, no empty boxes and no "unknown" labels anywhere in this file. */

/* ---------- 1. the article frame ----------
   --measure is 70ch. cwagner.co runs about 72; this content is shorter and denser, so
   it comes in two characters. It is a custom property rather than a literal because
   the hub pages and the kit sheet both need to read it.
   --prose-size is a deliberate half-step between --text-base (16) and --text-lg (20).
   The scale is a major third, which has no 17px step, and 16px at 70ch reads thin over
   1,800 words. It is declared here, once, and never used as a bare literal.
   Everything in the article is capped to the measure; .wide opts out, which is how a
   figure or a table gets the full 920 wrap. */
.note{--measure:70ch;--prose-size:1.0625rem;--prose-leading:1.65;padding-top:var(--space-6);padding-bottom:var(--space-6)}
.ntitle{margin:0;max-width:var(--measure);font-family:var(--font-display);font-weight:var(--weight-extrabold);font-size:var(--text-3xl);letter-spacing:var(--tracking-tight);line-height:var(--leading-tight);color:var(--color-heading);text-wrap:balance}
/* The date lives at the END of the eyebrow line, pushed right, because that line is
   already the metadata line. When there is no date the flex row simply has one fewer
   child: no reserved gap, no "undated", nothing to explain. Today that is all 126. */
.nwhen{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);white-space:nowrap}
/* ---------- the two dates, split by job ----------
   EDITED IS RECENCY and recency qualifies the brew stage, so it sits at the right end of
   the eyebrow beside the pill: pencil plus a written-out age, the notebook's own format.
   Past fifteen days it does not render at all — same rule as the stream — so a settled
   note's eyebrow is two words and the head does not grow a fourth thing on every page.
   CREATED IS PROVENANCE, so it goes to the foot as a colophon line (.ncolo) where the
   source block and the footnotes already live. Either may be absent on its own. */
.nedit{display:inline-flex;align-items:center;gap:5px;margin-left:auto;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);white-space:nowrap}
.nedit svg{opacity:.75;position:relative;top:-.5px}
.ncolo{display:flex;flex-wrap:wrap;align-items:center;margin:var(--space-6) 0 0;padding-top:var(--space-4);border-top:var(--border-hair) solid var(--color-border);max-width:var(--measure);font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.dpair{display:inline-flex;align-items:center;gap:5px}
.dpair svg{opacity:.7;position:relative;top:-.5px}
.ncolo .sep{opacity:.5;padding:0 7px}
/* THE RESTATEMENT DISCLOSURE, and the most important sentence on a borrowed note.
   10 of 16 borrowed notes are Chris compressing someone else's idea until he owns it,
   in his own words. A reader who lands mid-site cannot tell that from a badge reading
   "Borrowed", and the honest reading of an unlabelled restatement is plagiarism. So
   the page says whose words these are, in a sentence, before the body starts. Quiet,
   muted, one line, and it renders only on the borrowed type. */
.nrestate{margin:var(--space-3) 0 0;max-width:var(--measure);font-size:var(--text-sm);color:var(--color-text-muted);text-wrap:pretty}

/* ---------- 2. the steeping notice ----------
   The ONE stage that gets more than a pill. A steeping note is the thin, half-formed,
   dead-linked one, so it is the only stage where the reader needs an explanation of
   what they have walked into. Brewing and poured carry the pill and say nothing
   further. This is also the only place on a note page that uses the jade WASH, which
   is what keeps it the loudest thing above the prose. */
.steep{display:flex;align-items:flex-start;gap:var(--space-3);margin:var(--space-5) 0 0;max-width:var(--measure);padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);background:var(--color-stage-wash);color:var(--color-text-accent-on-peach)}
.steep .mk{margin-top:4px;border-color:currentColor;background-image:linear-gradient(to top,currentColor var(--lvl,0%),transparent var(--lvl,0%))}
.steep p{margin:0;font-size:var(--text-sm);text-wrap:pretty}

/* ---------- 3. the rail: on this page, and the connected count ----------
   MORE THAN TWO HEADINGS or there is no contents list — a two-line list is a list of the
   page you can already see. 13 of 126 notes clear the bar. On those notes it is a STICKY
   list that tracks the reading position, sitting under the connected count in the rail
   column: 70ch of prose plus a 190px rail plus the gap still fits the 920 wrap, so
   nothing about the page width changes and wide figures keep the whole wrap.
   Below 860px the slack is gone, so the same markup falls back to stacked blocks above
   the prose, borders and all. Anchors work with no JavaScript; toc.js only adds the
   current-section highlight and measures the chrome. */
/* --toc-w is the fallback. toc.js overwrites it with the exact distance from the
   right edge of the wrap to the header's .navutil divider, so the rail's rule is the
   SAME vertical line as the one before Feed in the nav, and the rail's text starts on
   the same left edge as "Feed" (both are padded by --space-4).
   --ctx-h is the height of the context strip (section 3b), published by toc.js and 0 on
   a note that has none. Every offset that has to clear the page chrome — the anchor
   scroll padding, the rail's sticky top — reads header plus strip, so neither of them
   has to know whether the strip is there. */
:root{--toc-w:190px;--hdr-h:72px;--ctx-h:0px}
html{scroll-padding-top:calc(var(--hdr-h) + var(--ctx-h) + var(--space-4))}
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
.ntoc{margin:var(--space-6) 0 0;max-width:var(--measure);padding:var(--space-4) 0;border-top:var(--border-hair) solid var(--color-border);border-bottom:var(--border-hair) solid var(--color-border)}
.ntoch{margin:0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.ntoc ol{list-style:none;margin:var(--space-3) 0 0;padding:0;display:grid;gap:7px}
.ntoc ol ol{margin:7px 0 0;padding-left:var(--space-4);gap:5px}
.ntoc li{position:relative}
/* DEPTH IS AN INDENT, on a flat list. The markup is one <ol> carrying
   `data-depth` per row rather than nested lists, because a real hierarchy would
   have to invent parents for a note that opens on an h3. Levels below the
   second also step down in size and weight, so a deep note reads as an outline
   rather than as a wall of equal links. h1 and h2 share the top rank: a body h1
   is rare and is a peer of the sections around it, not a level above them. */
.ntoc li[data-depth="3"]{padding-left:var(--space-4)}
.ntoc li[data-depth="4"]{padding-left:var(--space-5)}
.ntoc li[data-depth="5"]{padding-left:var(--space-6)}
.ntoc li[data-depth="6"]{padding-left:var(--space-7,2.5rem)}
.page .ntoc li:not([data-depth="1"]):not([data-depth="2"]) a{
  font-size:var(--text-xs);color:var(--color-text-muted)}
.page .ntoc a{display:block;position:relative;font-size:var(--text-sm);color:var(--color-text);text-decoration:none}
.page .ntoc a:hover{color:var(--color-text-accent);text-decoration:underline;text-underline-offset:3px}
.page .ntoc ol ol a{font-size:var(--text-xs);color:var(--color-text-muted)}
/* WHERE YOU ARE. One link at a time carries aria-current, set by toc.js against the
   heading that has just passed under the header. The state is a colour change and a
   two-pixel tick on the rule; no background, no box, nothing that would turn a quiet
   index into a menu. In the stacked fallback the tick has no rule to sit on, so it is
   simply not drawn and the colour does the whole job. */
.page .ntoc a[aria-current]{color:var(--color-text-accent);font-weight:var(--weight-semibold)}
/* THE RAIL IS THE COLUMN, not the sticky box. It holds the connected count at the top
   (section 3b) and the contents list under it, so the column is static and full height
   and only .ntoc inside it sticks — which is what lets the count scroll away with the
   title while the contents list stays. align-self:stretch is load-bearing: without it
   the item is content-sized inside the rows it spans and the sticky child has no travel
   room. A note may carry either piece alone; the rail is absent when it has neither. */
@media (min-width:861px){
  .note:has(.nrail){display:grid;grid-template-columns:minmax(0,1fr) var(--toc-w);column-gap:var(--space-7);row-gap:0;align-items:start}
  .note:has(.nrail)>*{grid-column:1;min-width:0}
  .note>.nrail{grid-column:2;grid-row:1/span 60;align-self:stretch;height:100%;padding-left:var(--space-4);border-left:var(--border-hair) solid var(--color-border)}
  .note>.nrail>.ntoc{position:sticky;top:calc(var(--hdr-h) + var(--ctx-h) + var(--space-5));max-width:none;margin:0;padding:0;border:0;max-height:calc(100vh - var(--hdr-h) - var(--ctx-h) - var(--space-7));overflow-y:auto;overflow-x:clip;overflow-clip-margin:8px;scrollbar-width:thin;overscroll-behavior:contain}
  /* SEPARATION. A hairline under the count, tee-jointed into the rail's vertical rule
     (negative inset, then the padding back), so the two blocks read as two parts of one
     column rather than one long list. It hangs on .nconn, which is static: put it on the
     sticky list instead and the rule travels down the page with it. */
  .note>.nrail>.nconn{padding-bottom:var(--space-4);margin-left:calc(-1 * var(--space-4));padding-left:var(--space-4);border-bottom:var(--border-hair) solid var(--color-border)}
  .note>.nrail>.nconn+.ntoc{margin-top:var(--space-5)}
  .note>.nrail .ntoc a[aria-current]::before{content:"";position:absolute;left:calc(-1 * var(--space-4) - 1px);top:2px;bottom:2px;width:2px;border-radius:1px;background:currentColor}
}
/* Below the slack the rail is just two stacked blocks above the prose, in the same
   order, and the contents list takes its ruled fallback back. */
@media (max-width:860px){.nrail{display:flex;flex-direction:column}}

/* ---------- 3b. the connected count ----------
   It used to be a link in the eyebrow, on a line already carrying type, stage and a
   jump arrow — four things of which only one was a control. It is now TWO states of one
   thing: a block at the head of the rail while you are at the top of the note, and a
   pill in the context strip for the rest of the page. Both point at #connected, which
   is still the only home for the six rows and the filter.
   The numeral is at display size because the count is the evidence for the brew stage
   on a topic, and the three relationships are printed under it — a bare 6 raises the
   question "6 of what" that the section at the foot answers 1,400 words later. No
   field: the rail has only ever been type on a hairline, and a card in it would be the
   loudest object on the page. */
/* ONE BLOCK THE HEIGHT OF THE HEAD. Numeral and label share a baseline row and the
   breakdown sits straight under it with no rule and no field, so the whole thing occupies
   the same vertical band as type/title/chips on the left. That is what makes the hand-off
   read as a move rather than a swap: you scroll past the title line and the count is
   already in the strip. */
.page a.nconn{display:block;text-decoration:none;color:var(--color-text)}
.nch{display:flex;align-items:baseline;gap:8px}
.ncn{font-family:var(--font-display);font-weight:var(--weight-extrabold);font-size:var(--text-2xl);line-height:1;letter-spacing:var(--tracking-tight);color:var(--color-heading)}
.ncl{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.page a.nconn:hover .ncn,.page a.nconn:hover .ncl{color:var(--color-text-accent)}
.ncb{display:grid;gap:3px;margin-top:var(--space-2)}
.ncr{display:flex;align-items:baseline;gap:7px;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-muted)}
.ncrn{color:var(--color-text-accent);font-weight:var(--weight-semibold)}
/* A relationship with nothing in it is not printed, and a note whose links are all one
   relationship gets no breakdown at all — three lines that all say the same thing as
   the numeral above them. The build drops .ncb in that case. */
@media (max-width:860px){
  .nconn{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2) var(--space-4);margin-top:var(--space-5)}
  .ncb{grid-auto-flow:column;gap:var(--space-3);margin:0}
}

/* ---------- 3c. the context strip ----------
   Fixed under the header, and it fades in once the count block has scrolled past. It
   carries the note title, the stage and the count, so a reader 1,400 words down knows
   what they are in and can still reach the connected list — today they lose all three.
   FIXED, not sticky: a sticky strip holds a band of empty layout at the top of the note
   while it is invisible, and shifts the page the moment it arrives. */
.ctxbar{position:fixed;left:0;right:0;top:var(--hdr-h);z-index:15;background:var(--color-bg-page);border-bottom:var(--border-hair) solid var(--color-border);opacity:0;transform:translateY(-6px);pointer-events:none;transition:opacity .16s ease,transform .16s ease}
.ctxbar.is-on{opacity:1;transform:none;pointer-events:auto}
@media (prefers-reduced-motion:reduce){.ctxbar{transition:none}}
.ctxin{display:flex;align-items:center;gap:var(--space-3);padding-top:9px;padding-bottom:9px}
.ctxt{font-family:var(--font-display);font-weight:var(--weight-extrabold);font-size:var(--text-sm);letter-spacing:-.015em;color:var(--color-heading);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* THE COUNT IN THE STRIP IS THE SAME TYPE AS THE COUNT IN THE RAIL, not a button. A
   filled pill in a fixed strip is the loudest thing on the page every time you scroll,
   which is the opposite of what the block at the top does: display numeral, mono label,
   no field. Substantial because of the cut, understated because there is nothing behind
   it. The jade only arrives on hover, and only as ink. */
.page a.ctxc{margin-left:auto;display:inline-flex;align-items:baseline;gap:7px;padding:2px 0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);text-decoration:none;white-space:nowrap}
.page a.ctxc:hover{color:var(--color-text-accent)}
.page a.ctxc:hover .ctxn{color:var(--color-text-accent)}
.page a.ctxc:hover .njump{transform:translateY(2px)}
.page a.ctxc .ncjt{margin-left:5px}
.ctxn{font-family:var(--font-display);font-weight:var(--weight-extrabold);font-size:var(--text-lg);line-height:1;letter-spacing:var(--tracking-tight);color:var(--color-heading)}
/* Left-aligned with the rail's text, not flush right: the count is the same object in
   both places, so it should not move sideways when it is handed over. The box is the rail
   column minus its padding, which puts its left edge on the rail's own text edge — the
   same x as "Feed" in the nav above it. */
@media (min-width:861px){.page a.ctxc{flex:none;width:calc(var(--toc-w) - var(--space-4));justify-content:flex-start}}
/* At 480 the title is the first thing to go: the count is the control and the stage is
   the fact, and neither can be truncated to nothing. */
@media (max-width:480px){.ctxt{display:none}}

/* ---------- 4. THE PROSE ----------
   The stylesheet the property did not have. Every element below was counted in the
   vault before it was designed; nothing here is speculative. Task lists appear in two
   notes and are deliberately not styled.
   The owl selector sets one rhythm and the headings override it, so a body of any
   length spaces itself without the template knowing what is in it. */
.prose{margin-top:var(--space-6);font-size:var(--prose-size);line-height:var(--prose-leading);color:var(--color-text)}
.prose>*{max-width:var(--measure)}
.prose>.wide{max-width:none}
/* .prose.prose, not .prose, and deliberately: the element resets below (.prose p,
   .prose :is(ul,ol), .prose pre, .prose figure) are all one specificity point heavier
   than a plain .prose>*+* and were silently eating the whole flow rhythm. Doubling the
   class outruns them without touching a single reset. Every rule that sets flow margin
   in this section is doubled the same way so they keep beating each other in the order
   written. */
.prose.prose>*+*{margin-top:var(--space-4)}
/* STOP BLOCKS. A callout, a pull quote or a figure is a block the eye is meant to stop at, so
   both get a wider berth than the paragraph rhythm on either side. Only top margins
   are ever set in this file, so two stop blocks back to back share one gap rather than
   stacking two. Headings are the exception on the trailing side: they already open
   with more air than this and keep it. */
.prose.prose>:is(.cal,blockquote,figure){margin-top:var(--space-6)}
.prose.prose>:is(.cal,blockquote,figure)+:not(h2,h3){margin-top:var(--space-6)}
.prose p{margin:0;text-wrap:pretty}
.prose strong{font-weight:var(--weight-semibold);color:var(--color-heading)}

/* Headings. H2 is the display cut; H3 and H4 step down in size and weight rather than
   changing family, because a hub nests three levels and four display weights in one
   column reads as four competing voices. scroll-margin clears the sticky header when
   a TOC link or a footnote fragment lands on one. */
.prose :is(h2,h3,h4){margin:0;color:var(--color-heading);letter-spacing:var(--tracking-tight);line-height:var(--leading-tight);scroll-margin-top:var(--space-8);text-wrap:balance}
.prose h2{font-family:var(--font-display);font-size:var(--text-xl);font-weight:var(--weight-extrabold)}
.prose h3{font-size:var(--text-lg);font-weight:var(--weight-extrabold);letter-spacing:-.01em}
.prose h4{font-size:var(--prose-size);font-weight:var(--weight-semibold);letter-spacing:0}
.prose.prose>h2{margin-top:var(--space-7)}
.prose.prose>h3{margin-top:var(--space-6)}
.prose.prose>h4{margin-top:var(--space-5)}
/* A heading is its own anchor. The link is the heading, so there is no hover-only
   paragraph mark to discover and nothing to tap on a phone. */
.page .prose :is(h2,h3,h4) a{color:inherit;text-decoration:none}
.page .prose :is(h2,h3,h4) a:hover{text-decoration:underline;text-decoration-color:var(--color-accent);text-decoration-thickness:2px;text-underline-offset:4px}

/* Lists. 33 notes use bullets, 7 use numbers. The marker takes the accent; on the
   numbered list it also takes the mono, because a numeral is data. */
.prose :is(ul,ol){margin:0;padding-left:var(--space-5)}
.prose li+li{margin-top:7px}
.prose li>:is(ul,ol){margin-top:7px}
.prose li::marker{color:var(--color-accent)}
.prose ol>li::marker{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--color-text-accent)}

/* Sub and superscript, which the generator writes for CO₂ and a². The browser
   default raises the baseline WITHOUT taking the line out of the flow, so one
   subscript opens the leading of the paragraph it sits in. `line-height:0` plus
   relative positioning is the standard fix: the glyph shifts, the line does not. */
.prose :is(sub,sup){font-size:.72em;line-height:0;position:relative;vertical-align:baseline}
.prose sup{top:-.5em}
.prose sub{bottom:-.25em}

/* Task lists. The checkbox IS the marker, so the bullet has to go or every row
   carries two. The list keeps its indent rather than pulling flush left, so a
   note that mixes plain bullets and tasks stays on one left edge. The box is
   the accent and always disabled: this is a rendered note, not an app. */
.prose ul.contains-task-list{list-style:none;padding-left:var(--space-5)}
.prose li.task-list-item{list-style:none}
.prose li.task-list-item>input[type=checkbox]{
  accent-color:var(--color-accent);margin:0 .5em 0 calc(-1 * var(--space-5));
  width:1em;height:1em;vertical-align:-.1em;flex:none}

/* Blockquote — 15 notes. A hairline rule and an indent, no fill and no italics: the
   serif italic in this system is reserved for the pull-quote, and a fill would put it
   in competition with the callout. 2px because at 1px the rule is decorative rather
   than structural, and it is non-text so the 3:1 bar applies, not 4.5:1. */
.prose blockquote{margin:0;padding-left:var(--space-4);border-left:2px solid var(--color-stage-rim);color:var(--color-text-lead)}
.prose blockquote>*+*{margin-top:var(--space-3)}
.prose blockquote cite{display:block;margin-top:var(--space-2);font-size:var(--text-sm);font-style:normal;color:var(--color-text-muted)}

/* OBSIDIAN CALLOUTS — 11 notes, four types: note, quote, advice, hint.
   All four share one field and differ ONLY by their label. That is not laziness, it
   is the same constraint the brew stage lives under: this property has ONE accent, so
   four types cannot differ by hue, and inventing three more colours would break the
   rule that makes the sub-brand a sibling instead of a second brand. The label is a
   word, which is the one differentiator that survives being colour-blind, greyscale
   printed, and read aloud.
   The field is RAISED PAPER with a hairline, not the jade wash: the wash is spoken by
   the site (the steeping notice, a highlight), and a callout is spoken by Chris. */
.cal{margin:0;padding:var(--space-4);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-surface-raised)}
.cal>*+*{margin-top:var(--space-3)}
.callab{display:flex;align-items:center;gap:6px;margin:0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-accent)}
.callab::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;flex:none}
/* `:not(.callab)` because the label is a <p> in a plain callout, and `.cal p`
   outranks `.callab` on specificity — so the label quietly took the BODY size
   and lost its own. It shipped that way and nobody saw it until the foldable
   variant put a <summary> label, which this rule cannot reach, beside a <p>
   one and the two were visibly different sizes. */
.cal p:not(.callab){margin:0;font-size:var(--text-sm);line-height:var(--leading-relaxed);text-wrap:pretty}

/* Foldable callout. The summary IS the label, so it inherits .callab and only
   gains the affordance: a pointer, and the native triangle swapped for a
   chevron that turns, so the closed and open states differ by more than the
   presence of a body. Nothing here is scripted; <details> does the work. */
summary.callab{cursor:pointer;list-style:none}
summary.callab::-webkit-details-marker{display:none}
summary.callab::after{content:"";width:5px;height:5px;flex:none;margin-left:auto;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translate(-1px,-1px);transition:transform var(--dur-fast,.15s) ease}
.calfold[open]>summary.callab::after{transform:rotate(-135deg) translate(-2px,-2px)}

/* HIGHLIGHTS — 4 notes, and load-bearing every time: this is how Chris marks the one
   clinically important phrase in a technical note. It gets the jade wash, which is the
   site's own "pay attention" field, and keeps the body ink so it reads as emphasis
   rather than as a link. box-decoration-break keeps the field intact across a line
   break, which is where an unstyled mark falls apart. */
/* box-shadow, not horizontal padding: padding pushes the following punctuation away
   from the highlighted word and reads as a stray space. A spread shadow in the same
   colour extends the field without taking any layout space. */
.prose mark{background:var(--color-stage-wash);color:var(--color-text);padding:.06em 0;box-shadow:.14em 0 0 var(--color-stage-wash),-.14em 0 0 var(--color-stage-wash);-webkit-box-decoration-break:clone;box-decoration-break:clone}

/* CODE — 7 notes with blocks, inline spans throughout. The field is a mix of the
   heading ink into the surface, so it darkens on light paper and lightens on night
   without a second token or a theme override. */
.prose code{font-family:var(--font-mono);font-size:.875em;padding:.12em .34em;border-radius:var(--radius-sm);background:color-mix(in oklab,var(--color-heading) 9%,transparent)}
.prose pre{margin:0;padding:var(--space-4);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-md);background:color-mix(in oklab,var(--color-heading) 5%,var(--color-bg-surface-raised));overflow-x:auto;font-family:var(--font-mono);font-size:var(--text-sm);line-height:var(--leading-normal);color:var(--color-text)}
.prose pre code{padding:0;background:none;font-size:inherit;border-radius:0}

/* TABLES — 8 notes. Ruled, not boxed and not striped: this is paper, and a zebra
   pattern would be the loudest thing on the page. The header row is the mono eyebrow
   the rest of the property uses. .tblwrap is required around every table, because a
   wide table on a 320px screen is the one thing that can break the no-horizontal-
   scroll promise, and the scroll has to belong to the table rather than the page. */
.tblwrap{overflow-x:auto}
.prose table{width:100%;border-collapse:collapse;font-size:var(--text-sm)}
.prose thead th{text-align:left;padding:0 var(--space-3) var(--space-2) 0;border-bottom:var(--border-hair) solid var(--color-border);font-family:var(--font-mono);font-size:var(--text-2xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);white-space:nowrap}
.prose td{padding:var(--space-2) var(--space-3) var(--space-2) 0;border-bottom:var(--border-hair) solid var(--color-rule);vertical-align:top}
.prose :is(td,th):last-child{padding-right:0}
.prose .num{text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}

/* FIGURES — 18 notes. No border on the image itself; the caption carries it. .ph is
   the placeholder that stands in until the real asset is migrated, and it is dashed
   on purpose so nobody mistakes it for a design element. */
.prose figure{margin:0}
.prose figure img{display:block;width:100%;height:auto;border-radius:var(--radius-md)}
.prose figcaption{margin-top:var(--space-2);font-size:var(--text-xs);color:var(--color-text-faint);text-wrap:pretty}
.ph{display:flex;align-items:center;justify-content:center;aspect-ratio:16/9;border:var(--border-hair) dashed var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-surface-raised);color:var(--color-text-faint);font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase}

/* HORIZONTAL RULES — 6 notes. A short left-aligned bar, not a full-width line: it is
   a beat between sections, and a rule that spans the measure reads as the end of the
   article. Left-aligned because everything else on this property is. */
.prose hr{width:76px;margin:var(--space-6) 0;border:0;border-top:2px solid var(--color-stage-rim);opacity:.75}

/* TRANSCLUDED NOTES — 6 notes. Another of Chris's notes quoted inside this one, which
   is the one place the graph shows up mid-sentence. It is NOT a callout: it carries
   the note's own gutter marker and a linked title, so it reads as a piece of the
   notebook rather than as an aside, and its body drops to --text-sm to sit as a
   nested voice under the surrounding prose. */
.embed{margin:0;padding:var(--space-3) var(--space-4) var(--space-4);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-surface-raised)}
.embedh{display:flex;align-items:center;gap:var(--space-2);margin-bottom:var(--space-2)}
.page .embedt{font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-heading);text-decoration:none}
.page .embedt:hover{text-decoration:underline;text-decoration-color:var(--color-accent);text-underline-offset:3px}
.embedl{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.embed>p{margin:0;font-size:var(--text-sm);line-height:var(--leading-relaxed);color:var(--color-text-lead);text-wrap:pretty}
.embed>p+p{margin-top:var(--space-3)}

/* FOOTNOTES — 9 notes, and every hub. Fragment links both ways, no JS. :target puts
   the wash behind the footnote you jumped to, which is the whole reason a reader can
   find their place again after tapping a 10px superscript. */
.page .fnref{font-family:var(--font-mono);font-size:var(--text-2xs);font-weight:var(--weight-semibold);color:var(--color-text-accent);text-decoration:none;padding:0 1px;vertical-align:super;line-height:0}
.page .fnref:hover{text-decoration:underline}
.fnotes{margin-top:var(--space-7);padding-top:var(--space-4);border-top:var(--border-hair) solid var(--color-border);max-width:var(--measure)}
.fnotes ol{margin:var(--space-3) 0 0;padding-left:var(--space-5);font-size:var(--text-sm);line-height:var(--leading-relaxed);color:var(--color-text-muted)}
.fnotes li{margin-top:var(--space-2);padding:2px 6px;margin-left:-6px;border-radius:var(--radius-sm)}
.fnotes li::marker{font-family:var(--font-mono);color:var(--color-text-accent)}
.fnotes li:target{background:var(--color-stage-wash);color:var(--color-text)}
.page .fnback{margin-left:6px;color:var(--color-text-faint);text-decoration:none}
.page .fnback:hover{color:var(--color-text-accent)}

/* ---------- 5. WHERE THIS CAME FROM ----------
   The source block, and the hardest constraint in the brief: it must DEGRADE TO
   NOTHING. Right now 1 of 16 borrowed notes has a structured URL and 0 have a
   structured author, so the ABSENT state is the common state and the block cannot be
   a box — an empty box, or a box reading "source unknown", would put a hole on
   fifteen pages out of sixteen. It is a hairline-topped section instead, the same
   idiom as the footnotes and the backlinks, so its absence closes up invisibly.
   Four optional fields: title, author, url, and how it reached me. Any of the four
   may be missing; if all four are, the template omits the section entirely.
   It sits at the FOOT of the page, after the body, because the body is Chris's
   restatement. Attribution above a paragraph of his own words would imply the
   paragraph is theirs. The disclosure that does belong at the top is .nrestate. */
.src{margin-top:var(--space-7);padding-top:var(--space-4);border-top:var(--border-hair) solid var(--color-border);max-width:var(--measure)}
.srch{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.srct{display:block;margin:var(--space-2) 0 0;font-size:var(--text-lg);font-weight:var(--weight-semibold);line-height:var(--leading-snug);color:var(--color-heading);letter-spacing:-.01em;text-decoration:none;text-wrap:pretty}
.page a.srct:hover{color:var(--color-text-accent);text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:4px}
.srcby{margin:6px 0 0;font-size:var(--text-sm);color:var(--color-text-muted)}
.srcurl{margin:var(--space-2) 0 0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);color:var(--color-text-faint);overflow-wrap:anywhere}
.srcvia{margin:var(--space-3) 0 0;font-size:var(--text-sm);color:var(--color-text-muted);text-wrap:pretty}

/* ---------- 6. related by topic ----------
   The GENERATED replacement for the "## Related to:" lists Chris maintains by hand in
   the vault. Driven by the same topics as the header chips, so the relationship is
   designed once and the hand-written lists get deleted.
   Deliberately NOT a second stream: backlinks directly above it are already a list of
   ruled rows, and two identical lists back to back read as one list that repeats. So
   this is a compact run of links per topic — "also filed under", not "read these
   next". */
.rel{margin-top:var(--space-6);max-width:var(--measure)}
.relh{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.relg{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--space-2) var(--space-4);align-items:baseline;margin-top:var(--space-3)}
.rell{display:flex;flex-wrap:wrap;gap:6px var(--space-3);margin:0;padding:0;list-style:none}
.page .rell a{font-size:var(--text-sm);color:var(--color-text);text-decoration:none}
.page .rell a:hover{color:var(--color-text-accent);text-decoration:underline;text-underline-offset:3px}

/* A heading with nothing under it. Four of On Coffee's eleven sections are in this
   state, and it is the reason a 700-word hub reads brewing while a 5-word definition
   reads poured. The heading is a promise the note has made, so dropping it hides a gap
   the author wants visible; leaving it silent reads as a rendering bug. Four faint
   words, no box, no icon, and no invitation to come back later. */
.prose .unwritten{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}

/* ============================================================================
   THE TOPIC PAGE
   61 of 126 pages are topics, and they are CROSSROADS rather than writing. Two
   things follow, and both of them are why a topic is not just a note with a
   different badge.

   1. WORD COUNT IS THE WRONG MATURITY SIGNAL. A topic is staged by INBOUND LINKS:
      1-2 is steeping, 3+ is brewing, poured is manual. So the count is not
      trivia, it is the evidence for the stage, and it is printed at the head of
      the rail (.nconn, section 3b) rather than buried at the foot of the page.
   2. FOR A THIRD OF THEM, THE BACKLINKS ARE THE PAGE. 11 of the 31 brewing
      topics have zero body text; On Laboratory has 8 notes pointing at it and
      says nothing at all. That is a working crossroads, not a broken page, so it
      gets the confident treatment below and NO apology anywhere: no "nothing here
      yet", no dimmed placeholder, no invitation to come back later.

   What makes the no-prose state read as finished is that the collected list stops
   being a footer and becomes the body: its own top rule, ruled rows, room to
   breathe, and one line of the note's own words per row. */
/* The relationship word in a connected row: points here, linked above, or related.
   It used to lead the meta line and was muted rather than faint, because on an
   always-on line it was the one piece the reader was scanning for. It rides beside
   the title on hover now (site.css), next to the read time, so it takes that hint's
   faint instead: two hints appearing together in two different weights read as an
   accident rather than as a pair. No override here. */
/* Kind and stage on a connected row live in the marker, and the hover tooltip that
   says them is in site.css now (.srowa[data-tip]) — every stream row on the property
   uses it, not just these. Nothing to add here. */
/* The connected filter. A line of type, not a widget: mono micro, the same separator
   the meta lines use, the count sitting against its own word. Selected takes the jade
   ink and a rule under it — the one place on a note page where a control is allowed to
   look like the running text it sits in. */
.conn .blh{align-items:center;margin-bottom:var(--space-3)}
/* The hover target is padded well past the type: at 10px tall, a horizontal drag from
   the eye to "also related" falls out of a tight box on the smallest wobble. The
   negative margin gives the padding back to the layout, so the row does not move. */
.connfilter{display:flex;flex-wrap:wrap;align-items:center;gap:0 6px;margin:-12px -14px;padding:12px 14px;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;line-height:1.6}
/* "Show" and the separators carry the same padding and transparent rule as a filter
   label, so every box in this centred row is the same height and the words all sit on
   one line. Without it the labels ride 2px high on their own underline. */
.cflab,.connfilter .sep{padding-bottom:2px;border-bottom:1.5px solid transparent}
.cflab{color:var(--color-text-faint);padding-right:2px}
.connfilter .sep{color:var(--color-text-faint);opacity:.5}
.cfin{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.cf{cursor:pointer;color:var(--color-text-muted);padding-bottom:2px;border-bottom:1.5px solid transparent}
.cf:hover{color:var(--color-heading)}
.cfn{padding-left:5px;color:var(--color-text-faint)}
.cfin:checked+.cf{color:var(--color-text-accent);border-bottom-color:var(--color-accent)}
.cfin:focus-visible+.cf{outline:2px solid var(--color-accent);outline-offset:3px;border-radius:2px}
/* Collapsed to the eye until you ask. Three ways to open it — hover, keyboard focus,
   or tapping the eye, which is a label for a checkbox so touch has a real toggle. The
   running filter stays visible while collapsed, because a list that is hiding rows
   must not look like a list that is complete. */
/* THE EYE IS THE ONLY WAY IN. The strip itself is not a hit target while closed, so
   the selected label sitting beside the eye cannot reopen the menu the instant a pick
   collapses it. Hovering the eye makes the whole strip live again, which is what keeps
   it open while the pointer travels across the options. The eye's own padding swallows
   the container's, so the hot zone is a comfortable target rather than a 15px glyph. */
.connfilter{pointer-events:none}
.cfeye{pointer-events:auto}
.connfilter:hover,.connfilter:focus-within,.connfilter:has(#cf-open:checked){pointer-events:auto}
.cfeye{display:inline-flex;align-items:center;padding:12px 10px 12px 14px;margin:-12px -10px -12px -14px;color:var(--color-text-faint);cursor:pointer}
.cfeye svg{display:block;width:15px;height:15px}
.cfeye:hover,.connfilter:has(#cf-open:checked) .cfeye{color:var(--color-text-accent)}
.cfin:focus-visible+.cfeye{outline:2px solid var(--color-accent);outline-offset:2px;border-radius:3px}
.cfof{padding-left:4px;color:var(--color-text-faint);display:none}
.connfilter.is-dismissed,.connfilter.is-dismissed .cfeye{pointer-events:none}
/* Dismissed is its own collapsed state, not just a way to drop :hover. Chrome does not
   re-run the hit test until the pointer next moves, so pointer-events alone leaves the
   strip open under a stationary cursor — which is the whole moment this exists for. */
.connfilter.is-dismissed :is(.cflab,.sep,.cf){display:none}
.connfilter.is-dismissed .cfin:checked+.cf{display:inline}
.connfilter.is-dismissed:not(:has(input[name="cf"]:checked)) label[for="cf-all"]{display:inline}
.connfilter.is-dismissed .cfin:checked+.cf .cfof{display:inline}
.connfilter:not(:hover):not(:focus-within):not(:has(#cf-open:checked)) :is(.cflab,.sep,.cf){display:none}
.connfilter:not(:hover):not(:focus-within):not(:has(#cf-open:checked)) .cfin:checked+.cf{display:inline}
/* NOTHING CHECKED IS "ALL". The static build ships checked on the all radio; the DC
   preview's React pass drops it, and a re-render can reset the property again. Rather
   than fight that from script, the empty group is treated as the default state here —
   which is also the honest reading of the markup, since no filter checked means no
   rows hidden. */
.connfilter:not(:has(input[name="cf"]:checked)) label[for="cf-all"]{color:var(--color-text-accent);border-bottom-color:var(--color-accent)}
.connfilter:not(:hover):not(:focus-within):not(:has(#cf-open:checked)):not(:has(input[name="cf"]:checked)) label[for="cf-all"]{display:inline}
.connfilter:not(:hover):not(:focus-within):not(:has(#cf-open:checked)) .cfin:checked+.cf .cfof{display:inline}
.conn:has(#cf-in:checked) .srow:not([data-rel="in"]),
.conn:has(#cf-text:checked) .srow:not([data-rel="text"]),
.conn:has(#cf-rel:checked) .srow:not([data-rel="rel"]){display:none}
.collected{margin-top:var(--space-6)}
.collh{display:flex;align-items:baseline;gap:var(--space-3);margin-bottom:var(--space-3);flex-wrap:wrap}
.collcount{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
/* Rows breathe here, because on a bare topic these eight rows ARE the article.
   The home stream packs them tight on purpose; this is the opposite job. */
.collected .srowa{padding-top:var(--space-3);padding-bottom:var(--space-3)}
/* The excerpt is the single move that turns a list into a page. It is the note's
   own opening sentence, which VaultCMS already has, so nothing is invented and
   nothing is written twice. Clamped to two lines so one long opener cannot turn a
   scannable index back into prose. On a five-word note the excerpt IS the note,
   which is correct: the row has told you everything and you need not click. */
.sexcerpt{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;max-width:78ch;margin-top:5px;font-size:var(--text-sm);line-height:var(--leading-normal);color:var(--color-text-muted);text-wrap:pretty}
/* Filed under: its own row under the eyebrow. Categories run first and drop the hash,
   because a category is a place the note lives and a hashtag is a word thrown at it. */
.nfiled{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);margin:var(--space-3) 0 0}
.page .chip.cat{color:var(--color-heading);border-color:var(--color-stage-rim)}
.chip.cat::before{content:none}
/* The count came OFF the eyebrow. That line is about the note — what it is and how done
   it is — and the count was the only control on it. It is the rail block and the context
   strip now (sections 3b and 3c); the eyebrow is two words again.
   The arrow says the link goes DOWN this page rather than off it, which nothing else in
   the row gives a reason to assume. It nudges on hover so the direction is stated twice,
   once in the glyph and once in the movement. */
.njump{display:inline-block;padding-left:6px;font-size:var(--text-base);line-height:1;color:currentColor;transition:transform .12s ease}
.page a.nconn:hover .njump{transform:translateY(2px)}
/* NAMING THE JUMP, ON DEMAND. Parenthesised and mono, revealed on hover or keyboard
   focus of either count — the rail block and the context strip. It is held in the layout
   at rest (opacity, not display) so revealing it shifts nothing, and it is aria-hidden's
   opposite: real text, so a screen reader gets it from the link either way. */
.ncjt{display:inline-flex;align-items:baseline;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-accent);opacity:0;transition:opacity .14s ease}
.ncjt .njump{padding-left:3px;font-size:var(--text-sm)}
.page a.nconn:hover .ncjt,.page a.nconn:focus-visible .ncjt,.page a.ctxc:hover .ncjt,.page a.ctxc:focus-visible .ncjt{opacity:1}
@media (prefers-reduced-motion:reduce){.ncjt{transition:none}}

/* ---------- the way back from the jump ----------
   Revealed by [data-connback] on <html> (toc.js), the same handoff pattern as the corner
   line and the context strip: the script owns the state, the stylesheet owns the drawing.
   A CIRCLE, not a line of type. The reader has just been moved 1,400 words and the undo
   has to be findable at a glance, so it is the one round object on the page — an arrow in
   a ring, parked at the RIGHT END of the section's own header row, opposite the eyebrow:
   the corner the eye lands on when the jump finishes. The words stay out of the layout
   until the pointer or the keyboard arrives, and then they name the place it goes back to,
   which is the part that makes the offer trustworthy. */
.connback{display:none;align-items:center;gap:var(--space-2);margin:0 0 0 auto;padding:0;border:0;background:none;cursor:pointer;color:var(--color-text-muted);opacity:0;transition:opacity .2s ease}
[data-connback] .connback{display:inline-flex;opacity:1}
.cbc{display:grid;place-items:center;width:34px;height:34px;flex:none;border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-pill);font-size:var(--text-base);line-height:1;color:var(--color-text-muted);transition:border-color .12s ease,color .12s ease,background-color .12s ease,transform .12s ease}
.connback:hover .cbc,.connback:focus-visible .cbc{border-color:var(--color-text-accent);color:var(--color-text-accent);background:var(--color-bg-surface-raised);transform:translateY(-2px)}
.connback:focus-visible{outline:none}
.connback:focus-visible .cbc{outline:2px solid var(--color-ring);outline-offset:3px}
/* The label is held out of the flow at rest, so the circle sits alone in the corner and
   nothing shifts when the words arrive. It opens leftward, away from the page edge. */
.cbt{white-space:nowrap;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);opacity:0;transform:translateX(6px);transition:opacity .14s ease,transform .14s ease}
.connback:hover .cbt,.connback:focus-visible .cbt{opacity:1;transform:none;color:var(--color-text-accent)}
/* The heading it left from, so the offer can be trusted. Sentence case against the
   line's uppercase, because it is quoted page content and not label text. */
.cbw{text-transform:none;font-style:italic;color:var(--color-text-muted)}
.connback:hover .cbw,.connback:focus-visible .cbw{color:var(--color-text-accent)}
@media (prefers-reduced-motion:reduce){.connback,.cbc,.cbt{transition:none}}
@media print{.connback{display:none}}

/* ---------- a body short enough to be a definition ----------
   Poured is a manual flag and it overrides length in both directions, so a
   FIVE-WORD POURED NOTE is a legitimate, finished page. SMART Goals is 5 words,
   Serendipity is 14, NICE Goals is 38, and all three are done and will never grow.
   Set at the body step in a 70-character column, five words look like a fragment
   of something missing. Set at the lead step in a short column they look like a
   statement, which is what a definition is.
   The rule is length-driven, not stage-driven: any rendered body under the
   short-body threshold takes it, poured or not. It reuses the word count that the
   stage rule already computes, so it costs the build nothing. */
.prose.short{font-size:var(--text-lg);line-height:var(--leading-relaxed);color:var(--color-text-lead)}
.prose.short>*{max-width:46ch}

/* ============================================================================
   THE NOW PAGE
   Not a note and deliberately not built like one: no brew stage, no backlinks, no
   topic chips, no source. It is replaced wholesale rather than grown, so the one
   thing it owes the reader is HOW STALE IT IS, and that is the only place on this
   property where a date is promoted instead of whispered.
   The body is FOUR QUADRANTS, not a column of headings: the question I am chewing
   on, what I am building, what I am reading and why, what I changed my mind about.
   The first of the four is the one a stranger keeps the tab open for, so it takes
   the jade wash — the only differentiation in the grid, and enough. */
.now{--measure:70ch;--prose-size:1.0625rem;--prose-leading:1.65;padding-top:var(--space-6);padding-bottom:var(--space-6)}
.nowmeta{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2) var(--space-3);margin:var(--space-3) 0 0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.nowmeta .sep{opacity:.55}
/* The age is computed at build time and it is allowed to be unflattering. Past the
   staleness threshold the build swaps in .old, which is the one honest way a Now
   page can admit it is not now. */
.nowmeta .age{color:var(--color-text-accent)}
.nowmeta .age.old{color:var(--color-text-faint)}
.nowgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--space-4) var(--space-5);margin:var(--space-5) 0 0;padding:var(--space-4) 0;border-top:var(--border-hair) solid var(--color-border);border-bottom:var(--border-hair) solid var(--color-border)}
.nowk{margin:0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.nowv{margin:5px 0 0;font-size:var(--text-sm);line-height:var(--leading-normal);color:var(--color-text);text-wrap:pretty}

/* ---------- the four quadrants ----------
   The page is four questions, so it is four boxes: the question I am chewing on,
   what I am building, what I am reading and why, what I changed my mind about.
   A 2x2 grid rather than four stacked sections, because the point of the page is
   that these four things are true AT THE SAME TIME — stacked, they read as a
   chronology. The boxes are the same paper and the same hairline as every other
   card on the property; only the first one takes the jade wash, which is how the
   page says which of the four is load-bearing without making it bigger.
   The body inside a quadrant is ordinary prose. No sub-grids, no status dots, no
   two-column tables: the grid IS the visual system, and anything added inside it
   competes with it. */
.quads{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4);margin-top:var(--space-6)}
.quad{display:flex;flex-direction:column;padding:var(--space-5);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-surface-raised)}
.quad.lead{border-color:var(--color-stage-rim);background:var(--color-stage-wash)}
/* The label carries a glyph at 15px — one step above the 10px mono it sits beside,
   because a glyph matched to cap height reads as a bug. It inherits the label's
   colour, so it takes the on-peach accent inside the lead quadrant for free. */
.quadh{display:flex;align-items:center;gap:7px;margin:0 0 var(--space-3);font-family:var(--font-mono);font-size:var(--text-2xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-accent)}
.quadh svg{flex:none}
.quad.lead .quadh{color:var(--color-text-accent-on-peach)}
/* The lead line of a quadrant is the display cut, so each box has one thing the eye
   lands on before it decides whether to read the rest. In the question box that is
   the question itself; in the others it is the sentence that carries the point. */
.quadl{margin:0;font-family:var(--font-display);font-weight:var(--weight-extrabold);font-size:var(--text-lg);letter-spacing:var(--tracking-tight);line-height:var(--leading-tight);color:var(--color-heading);text-wrap:balance}
.quad.lead .quadl{font-size:var(--text-xl)}
.quadb{margin:0;font-size:var(--text-sm);line-height:var(--leading-relaxed);color:var(--color-text-lead);text-wrap:pretty}
.quadl+.quadb,.quadb+.quadb{margin-top:var(--space-3)}
.quad em{font-style:normal;font-weight:var(--weight-semibold);color:var(--color-heading)}
/* The stamp sits directly under the body, NOT pushed to the card floor: the grid
   equalises row heights, so an auto margin turns the shorter quadrant into a half
   empty box with a caption stranded at the bottom. On the jade wash the faint ink
   is a page-background token and drops under 4.5:1, so the lead quadrant steps it
   up to muted. */
.quadf{margin:var(--space-4) 0 0;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
.quad.lead .quadf{color:var(--color-text-muted)}
.nowfoot{margin-top:var(--space-7);padding-top:var(--space-4);border-top:var(--border-hair) solid var(--color-border);max-width:var(--measure);font-size:var(--text-sm);color:var(--color-text-muted);text-wrap:pretty}

/* ---------- 7. 320px ----------
   The prose drops to the base step and the measure stops mattering, because at 320 the
   viewport is the measure. The title drops two steps: --text-3xl at 320 is six
   characters to a line. */
@media (max-width:760px){
  .note{--prose-size:var(--text-base);--prose-leading:var(--leading-relaxed)}
  .ntitle{font-size:var(--text-2xl)}
  .relg{grid-template-columns:1fr;gap:var(--space-1) 0}
  .relg .chip{justify-self:start}
  .now{--prose-size:var(--text-base);--prose-leading:var(--leading-relaxed)}
  .quads{grid-template-columns:1fr;gap:var(--space-3)}
  .quad{padding:var(--space-4)}
  .quad.lead .quadl{font-size:var(--text-lg)}
  .prose.short{font-size:var(--text-base)}
}
@media (max-width:420px){
  .ntitle{font-size:var(--text-xl)}
  .prose h2{font-size:var(--text-lg)}
  .prose h3{font-size:var(--text-base)}
  .srct{font-size:var(--text-base)}
}

/* ---------- 9. the corner invite ----------
   The second, quieter way out: one line of the notebook's own mono, pinned to the bottom
   of the page ON THE RAIL'S COLUMN — same left edge as the contents list and as "Feed" in
   the nav, so it is aligned to the wayfinding column rather than floating in a corner.
   --jright is published by jump.js (measured off .hdrin, because 100vw includes the
   scrollbar); the token's fallback keeps it in the right place before the script runs.

   IT IS ALWAYS THERE. A 45%-read threshold was tried and it reads as the control being
   missing rather than as tact — the line is quiet enough that it costs the top of the page
   nothing. The only thing that takes it away is the palette being open, since it would
   otherwise sit under the scrim naming a control that is already up. Absent below 861px —
   the width where the rail's column stops existing — and in print. jump.js publishes
   [data-cshow] on <html> for the reveal, so this needs no wrapper of its own.

   No button, no field, no shadow: the whole point of choosing this over a floating
   control is that the page gains type and nothing else. */
.jcorner{position:fixed;right:var(--jright,var(--space-6));bottom:var(--space-5);z-index:16;display:none;align-items:center;gap:8px;width:calc(var(--toc-w) - var(--space-4));font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);text-decoration:none;opacity:0;transform:translateY(6px);pointer-events:none;transition:opacity .2s ease,transform .2s ease}
@media (min-width:861px){.jcorner{display:inline-flex}}
[data-cshow] .jcorner{opacity:1;transform:translateY(calc(-1 * var(--jlift, 0px)));pointer-events:auto}
/* --jlift is how far the line has been pushed back up off the viewport floor so it comes
   to rest on the last rule of the article instead of drifting across the footer. jump.js
   measures it; at rest it is 0 and this is the plain fixed line. Transition it and the
   park stutters against the scroll, so only opacity animates once the line is up. */
[data-cshow] .jcorner{transition:opacity .2s ease}
@media (prefers-reduced-motion:reduce){.jcorner{transition:none}}
.page a.jcorner:hover{color:var(--color-text-accent);text-decoration:none}
.page a.jcorner:hover .jkey{border-color:var(--color-stage-rim);color:var(--color-text-accent)}
.jcorner:focus-visible{outline:2px solid var(--color-ring);outline-offset:3px;border-radius:var(--radius-sm)}
@media print{.jcorner{display:none}}

/* ---------- 10. the two words in the header, and what they mean ----------
   THE HEADER KEY is one mechanic serving two vocabularies. Both words on a note header —
   the KIND (Topic, Musing, Borrowed, Post) and the STAGE (steeping, brewing, poured) — are
   ImpromptBrü's own, and a reader meets both before anything explains either. Each becomes
   a disclosure: the word stays exactly the word it was, and a key opens under it.
   .hkey carries the mechanics and is deliberately id-free, so a consolidated post/note/
   topic view can put two, three or none of these on one header without renaming a class.
   Everything below .hkey is shared: one panel, one row, one ladder, two vocabularies.

   HOVER RE-POURS THE CUP. The gesture, the @property registration and the keyframes now
   live in site.css beside .mk, so every page pours: stream rows, the jump palette (hover
   and arrow-key selection), the connections panel, the colophon's three cards. What stays
   here is what only this page has — the pair acting as one control, and the ladder. That
   one gesture is the whole system — the mark is a vessel, the stage is how full it is — and it
   says it without a tooltip. Every marker in the key does it too, so you can pour any of
   the three by running the pointer down the ladder.
   CLICK OPENS THE KEY: three stages in order with their levels and a line each, this
   note's row marked, and the door to the Colophon for the long version. */
/* @property --lvl, the [data-stage] --lv targets and @keyframes skpour are in site.css. */
/* Hovering EITHER word lights BOTH and pours the cup: that is what tells the reader the
   pair is a single control before they click it. One caret, at the end of the pair. */
.skpill:hover .mk,.hkey:hover>.hkl .mk,.skr:hover .mk{animation:skpour .85s cubic-bezier(.22,.61,.36,1)}
@media (prefers-reduced-motion:reduce){.skpill:hover .mk,.hkey:hover>.hkl .mk,.skr:hover .mk{animation:none}}
.hkey{position:relative;display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-2) var(--space-3)}
/* The separator is INSIDE the control now, so the two words read as one object with a
   punctuation mark in it rather than as two controls that happen to be adjacent. */
.hksep{pointer-events:none}
/* The word is a label now, so it says so: a pointer, and the caret that turns over when
   the key is open. Nothing else about either word changes — the pill is the same pill the
   search results and the context strip use, and the badge is still a word and not a chip. */
.hkl{cursor:pointer}
.skcar{padding-left:5px;font-size:9px;line-height:1;opacity:.6;transition:transform .16s ease,opacity .16s ease}
/* The badge is already a 6px flex gap, so the caret's own padding would double the space
   between the word and the mark of the control. */
.tkpill .skcar{padding-left:0}
.hkl:hover .skcar{opacity:1}
.hkey:hover .skcar{opacity:1}
.hkey:hover .tkpill{color:var(--color-text-accent)}
.hkey:hover .skpill{box-shadow:inset 0 0 0 var(--border-hair) var(--color-stage-rim)}
.hkey:has(.hkeyin:checked) .skcar{transform:rotate(180deg);opacity:1}
.hkeyin:focus-visible+.hkl{outline:2px solid var(--color-ring);outline-offset:3px;border-radius:var(--radius-sm)}
/* THE KEY. A card, not a strip: it carries three rows of running text, which is more than
   a hairline can hold. Hidden with visibility rather than display so it stays out of the
   tab order while closed and can still fade. */
.skpop{position:absolute;top:calc(100% + var(--space-2));left:0;z-index:20;display:grid;gap:var(--space-3);width:352px;max-width:calc(100vw - var(--space-5) * 2);max-height:var(--skmax, 60vh);overflow:auto;overscroll-behavior:contain;padding:var(--space-4);border:var(--border-hair) solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-surface-raised);box-shadow:var(--shadow-card);opacity:0;visibility:hidden;transform:translateY(-4px);transition:opacity .16s ease,transform .16s ease,visibility .16s}
.hkey:has(.hkeyin:checked) .skpop{opacity:1;visibility:visible;transform:none}
.skeb{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint)}
/* ONE PANEL, TWO AXES. The intro is the only running sentence in it: it names the two
   questions, and each section below answers one. Section heads carry a rule so the panel
   reads as a short document rather than as one long list of eight things. */
.skintro{font-size:var(--text-xs);line-height:var(--leading-snug);color:var(--color-text-muted);text-wrap:pretty}
.skeb+.skintro{margin-top:calc(var(--space-3) * -1 + 2px)}
.sksec{padding-top:var(--space-3);border-top:var(--border-hair) solid var(--color-border);font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-heading)}
.sksec+.skl{margin-top:calc(var(--space-3) * -1 + var(--space-2))}
.skl{display:grid;gap:var(--space-3)}
/* TWO AXES, TWO COLUMNS. On a phone the panel is one column and the sections stack; from
   861px — the width where the note grows its rail and the page has room to spare — it opens
   to 640px and puts one axis beside the other. That is the shape of the idea: two questions
   asked at once, not one after the other. The intro and the door span both. */
.sksecw{display:grid;gap:var(--space-3);align-content:start}
@media (min-width:861px){
  .skpop{width:640px;grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:var(--space-5)}
  .skpop>.skeb,.skpop>.skintro,.skpop>.skmore{grid-column:1/-1}
}
/* The marks are the panel's illustrations, so they are drawn bigger here than in a dense
   stream row: a 16px cup shows its level at a glance, a 12px one has to be studied. */
.skpop .mk{width:16px;height:16px;margin-top:0}
.skpop .mk[data-kind="post"]{width:13px;height:13px}
/* THE LADDER POURS ON OPEN. Three cups fill in order, 120ms apart, the moment the panel
   appears — the notebook's one piece of theatre, and it is the explanation rather than
   decoration: you watch the scale fill. `both` holds each cup empty through its delay, so
   the ladder starts blank and rises left to right instead of three cups already full. */
.hkey:has(.hkeyin:checked) .stages .skr:nth-child(1):not(:hover) .mk{animation:skpour .8s cubic-bezier(.22,.61,.36,1) both}
.hkey:has(.hkeyin:checked) .stages .skr:nth-child(2):not(:hover) .mk{animation:skpour .8s cubic-bezier(.22,.61,.36,1) .12s both}
.hkey:has(.hkeyin:checked) .stages .skr:nth-child(3):not(:hover) .mk{animation:skpour .8s cubic-bezier(.22,.61,.36,1) .24s both}
@media (prefers-reduced-motion:reduce){.hkey:has(.hkeyin:checked) .stages .skr:not(:hover) .mk{animation:none}}
/* :not(:hover) is what keeps the ladder hoverable after it has poured itself. The open-state
   rules outrank .skr:hover on specificity, so without it the staggered animation would sit on
   every cup for as long as the panel is open and swallow the hover pour. Dropping the rule
   the moment the pointer arrives hands the cup back to the hover animation, which restarts
   it — so any cup on this side can be poured again, one at a time. */
/* EVERY KIND ROW IS A DOOR. The bench in the notebook is where these two knobs actually
   turn, and the kinds already have an entrance there (?shown=), so the row that explains a
   kind is also the way to see all of them. The arrow is held at zero opacity so the row
   does not shift when it appears. */
.page a.skr{text-decoration:none;color:inherit}
.page a.skr:hover{background:var(--color-bg-surface)}
.page a.skr.is-here:hover{background:var(--color-stage-wash)}
.page a.skr:hover .skn{color:var(--color-text-accent)}
.page a.skr.is-here:hover .skn{color:var(--color-text-accent-on-peach)}
.page a.skr .skn::after{content:"→";display:inline-block;padding-left:6px;opacity:0;transition:opacity .12s ease,transform .12s ease}
.page a.skr:hover .skn::after{opacity:1;transform:translateX(2px)}
/* Hovering a kind row must not pour anything — those cups are empty on purpose, and an
   animation to nothing still reads as a flicker. */
.sksecw:not(.stages) .skr:hover .mk{animation:none}
.page a.skr:focus-visible{outline:2px solid var(--color-ring);outline-offset:2px}
/* Two lines per row: the mark and the name on the first, the sentence under it. The level
   is NOT printed as a number — the cup beside it is the measurement, and a percentage next
   to a drawing that already says the same thing makes the drawing look insufficient. */
.skr{display:grid;grid-template-columns:18px 1fr auto;align-items:center;gap:3px var(--space-2);padding:var(--space-2);margin:calc(var(--space-2) * -1);border-radius:var(--radius-sm)}
.skr .mk{margin-top:0}
.skn{font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-heading)}
.skg{grid-column:2/-1;font-size:var(--text-xs);line-height:var(--leading-snug);color:var(--color-text-muted);text-wrap:pretty}
/* This note's row: the jade wash the pill itself wears, so the connection between the two
   is made by colour and needs no arrow drawn between them. */
.skr.is-here{background:var(--color-stage-wash)}
.skr.is-here .skn{color:var(--color-text-accent-on-peach)}
.skr.is-here .skg{color:var(--color-text-accent-on-peach);opacity:.85}
.skhere{grid-column:3;grid-row:1;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-accent-on-peach);white-space:nowrap}
.page a.skmore{display:inline-flex;align-items:center;gap:5px;padding-top:var(--space-3);border-top:var(--border-hair) solid var(--color-border);font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-faint);text-decoration:none}
.page a.skmore:hover{color:var(--color-text-accent);text-decoration:none}
.skar{transition:transform .12s ease}
.page a.skmore:hover .skar{transform:translateX(3px)}
@media (prefers-reduced-motion:reduce){.skpop,.skcar,.skar{transition:none}}
@media print{.hkey .skpop,.skcar{display:none}}
/* THE KIND KEY reuses the panel wholesale and adds the grouping: the three note subtypes
   sit bracketed under one label with the mark they share, and the post sits in its own.
   A hairline on the left is the bracket — cheaper than a rule under each label and it makes
   "inside Notes" a spatial fact rather than a sentence to remember. */
.skgrp{display:grid;gap:var(--space-3);padding-left:var(--space-3);border-left:var(--border-hair) solid var(--color-border)}
.skgl{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 6px;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-heading)}
/* What the mark says, next to the group it belongs to: the reason the group exists. On a
   solo row it sits beside the name instead, with the space a flex gap would give a label. */
.skgm{text-transform:none;font-style:italic;letter-spacing:0;color:var(--color-text-faint)}
.skn .skgm{padding-left:7px}
/* The outdented row: a kind with no subtypes, so it is its own label. Flush with the group
   labels above it, which is what makes it a peer of the bracket rather than a fourth note. */
.skr.sksolo{padding-left:0;margin-left:0}
.skgrp .skr{margin-left:0;margin-right:calc(var(--space-2) * -1)}
/* The badge's own mark must not inherit the pill's jade-on-wash ink: inside the key it is
   the real gutter mark, drawn in the stage inks like every row of the stream. */
.skr .mk[data-kind="post"]{margin-top:0}

/* ---------- 11. SHARING A NOTE ----------
   ONE control at the right end of the colophon line — the share glyph and the word Share —
   and it lives THERE rather than in a hairline section of its own for one reason: the colophon is the note's record —
   created, edited, and the link — and a share row given its own section would be a fifth
   block at the foot of the page carrying two words. It also keeps the order of departure
   intact: the prose, the record, the references, where it came from, then six ways out.
   ONE GLYPH, NOT A ROW OF LOGOS. The Phosphor share-network mark plus a mono word in the
   micro tier, the same voice as the connected filter. Four network logos at the foot of a
   note is furniture and a claim about which four networks matter; the control that covers
   all of them (the OS share sheet) has no logo to use anyway.
   BOTH ARE display:none UNTIL share.js CONFIRMS THE CAPABILITY and writes the attribute
   on <html>. A control that cannot work must not be drawn; with the file blocked neither
   appears, and the URL is still in the address bar. */
.nshare{display:inline-flex;align-items:center;gap:var(--space-1);margin-left:auto}
/* NO PILL, NO BORDER, NO FIELD. It sits on a line of mono metadata, and a framed control
   there is a button dropped into a caption — it outweighs the dates it stands next to and
   it competes with the stage pill above, which is the one thing on this page that has
   earned a field. This is the same treatment the connected filter uses: accent ink, and an
   underline on hover to say it is pressable. */
.shbtn{display:none;font-family:var(--font-mono);font-size:var(--text-2xs);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--color-text-accent);background:none;border:0;padding:6px 0;margin:-6px 0;cursor:pointer;text-align:left;transition:color 130ms ease}
html[data-canshare] .shbtn{display:inline-flex}
.shbtn{align-items:center;gap:6px}
.shbtn svg{width:13px;height:13px;flex:none;display:block}
/* The three words are stacked in ONE grid cell, so neither confirming nor failing can
   reflow the colophon line — a row that jumps sideways makes the reader check what moved. */
.shwords{display:grid}
.shwords>span{grid-area:1/1;white-space:nowrap}
/* THE SAME BUTTON AT THE HEAD, glyph only. It sits at the end of the eyebrow row after the
   edited stamp — which holds margin-left:auto — so it lands on the right edge without a
   second auto margin. Negative vertical margin keeps the 24px hit area from growing the
   row: the eyebrow's height is set by the panel, and a taller row would push the title
   down on all 126 pages for a control that is 12px of drawing.
   WHY BOTH ENDS. A reader who has not read the note yet shares from the head (they know it
   already — a backlink, the palette, a second visit); a reader who has just finished shares
   from the foot. One is not a duplicate of the other, it is the same act at the two moments
   it happens, which is the same argument the note page already makes for putting Connected
   at the foot and the jump palette in the corner. */
.shtop{margin-left:10px;padding:6px;margin-top:-6px;margin-bottom:-6px}
.shstack{display:grid}
.shstack>span{grid-area:1/1;display:flex;align-items:center}
/* The head glyph is drawn at the edited stamp's own weight — .75 opacity, like the pencil
   beside it — so the eyebrow reads as one row of metadata rather than a row with a control
   bolted on. Hover brings it to full and to the accent, which is the whole affordance. */
.shstack svg{opacity:.75}
.shtop{color:var(--color-text-muted)}
.shtop:hover{color:var(--color-text-accent)}
.shtop:hover .shstack svg{opacity:1}
.shdone{visibility:hidden;color:var(--color-text-accent)}
.shbtn[data-copied] .shl{visibility:hidden}
.shbtn[data-copied] .shdone{visibility:visible}
.shbtn:hover{color:var(--color-accent-hover)}
/* The only tell the Obsidian jump has, and it appears three seconds into a
   Cmd-held press, which no reader reaches by accident. It says "let go now":
   the launch happens on the release, because a browser will not hand a custom
   protocol to the OS from a timer. Nothing is drawn or reserved until then. */
.shbtn[data-jump]{color:var(--color-accent)}
.shbtn:hover .shwords{text-decoration:underline;text-underline-offset:3px}
.shfail{visibility:hidden;color:var(--color-text-muted)}
.shbtn[data-failed] .shl{visibility:hidden}
.shbtn[data-failed] .shfail{visibility:visible}
/* Glyph-only has nowhere to put "Press ⌘C", so a failure there simply keeps the share
   glyph rather than showing a tick that would be a lie. */
.shtop[data-failed] .shl{visibility:visible}
.shbtn:focus-visible{outline:2px solid var(--color-ring);outline-offset:3px;border-radius:var(--radius-sm)}
@media print{.shbtn{display:none!important}}
