/*
 * The manuscript surface: measure, rows, gutter, findings, status bar.
 *
 * Everything is expressed in `tokens.css` variables, plus two that the surface
 * writes from TypeScript because a number in this file would be a second
 * statement of something that already has an owner:
 *
 *   --gutter-ch   set from GUTTER_WIDTH_CH in src/editor/gutter.ts. The gutter
 *                 is a fixed column, and this file must not be able to disagree
 *                 with the module that guarantees it.
 *   --measure     set from src/editor/measure.ts, and always a reference to a
 *                 --measure-* token: a fixed stop, never a percentage and never
 *                 a function of the pane. That is the mechanical reason the
 *                 text does not reflow to fill space the companion gives back.
 *
 * Nothing here is `position: fixed` over the text, and nothing animates: a
 * finding arriving must be incapable of moving anything the author is looking
 * at.
 */

.novely-manuscript {
  /* The finding dot. The design's 7px, with no token of its own to read. */
  --dot-finding: 7px;

  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  flex: 1;
  background: var(--panel);
  color: var(--prose);
}

/* --- the measure ------------------------------------------------------- */

.novely-measure {
  flex: 1;
  min-height: 0;
  display: flex;
  justify-content: center;
  /* 24 rather than 36 at the sides: on a narrow window this padding is the
     whole of the margin, and it is the one number here that was chosen for
     looks rather than for the line length. The top stays -- the first line of a
     chapter wants air above it. */
  padding: 56px 24px 0;
  overflow: auto;
  /* The card and the channel its leader runs in, as one length. Declared here
     because this is the element both of the rules that use it live under. */
  --card-track: calc(var(--card-w) + var(--tether-channel));
}

/*
 * When cards are drawn, the prose moves over to make room for them *as a pair*.
 *
 * `justify-content: center` centres a flex item's margin box, so a right margin
 * of the card track puts the column's left edge at `(available - measure -
 * track) / 2` -- which leaves the same gap on both sides of the measure-plus-
 * cards pair. The column's own width is untouched: the prose is never resized
 * to make room, which is the rule `GUTTER_WIDTH_CH` exists to keep.
 *
 * Driven by whether cards are actually *drawn*, not by the mode. Margin mode
 * declines to draw them on a narrow window or past forty notes (`tether.ts`),
 * and shifting the prose aside for cards that never arrive would be the dead
 * space this rule is here to remove, moved to the other side.
 */
.novely-measure[data-cards='on'] .novely-column {
  margin-inline-end: var(--card-track);
}

.novely-column {
  width: 100%;
  /*
   * Capped, never stretched: extra room in the pane stays as margin.
   *
   * `--measure` is what the surface sets from `measure.ts`; `--shell-measure`
   * is the same stop published by the shell for whatever it has mounted in the
   * centre column. Both are references to a `--measure-*` token, so the two
   * can be missing or present in any combination and still cannot disagree
   * about where the stops are.
   */
  max-width: var(--measure, var(--shell-measure, var(--measure-wide)));
  min-width: 0;
}

/* --- type -------------------------------------------------------------- */

.novely-column .cm-editor {
  background: transparent;
}

.novely-column .cm-editor.cm-focused {
  outline: none;
}

.novely-column .cm-scroller {
  font-family: var(--manuscript-font, var(--font-prose));
  line-height: var(--leading-prose);
  overflow: visible;
}

.novely-column .cm-content {
  font-family: var(--manuscript-font, var(--font-prose));
  font-size: var(--size-prose);
  line-height: var(--leading-prose);
  color: var(--prose);
  text-wrap: pretty;
  padding: 0;
  caret-color: var(--text);
}

.novely-column .cm-line {
  padding: 0;
}

.novely-column .cm-cursor,
.novely-column .cm-cursor-primary {
  border-left-color: var(--text);
}

/* Selection: the author's, and the only wash that is not about a finding. */
.novely-column .cm-selectionBackground,
.novely-column .cm-content ::selection,
.novely-column .cm-line::selection {
  background: var(--selection);
}

.novely-column .cm-editor.cm-focused .cm-selectionBackground {
  background: var(--selection);
}

/* --- rows -------------------------------------------------------------- */

.novely-row-title {
  font-size: var(--size-title);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
}

.novely-row-heading {
  font-size: var(--size-14);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--secondary);
}

/*
 * Author comment. Prose, at a smaller size, with a rule down its left --
 * plainly the author's aside rather than part of the chapter, and visible in
 * every state including review, because it is the author's own text.
 */
.novely-row-comment {
  font-size: var(--size-comment);
  color: var(--muted-3);
  border-left: 2px solid var(--border);
  padding-left: 14px;
}

/* Unless the comment on it has been drawn as the note it is: that note is set
   between rules across the measure and brings its own, and the aside's rule
   stood behind it as a stray vertical line running past its bottom edge. */
.novely-row-rendered.novely-row-comment {
  border-left: 0;
  padding-left: 0;
}

/*
 * The line that points at a picture.
 *
 * It stays -- it is the author's bytes, and it is what a reader opening the
 * file in Obsidian sees -- but it is furniture rather than prose, so it is
 * small, mono and quiet, sitting above its own figure like a caption that got
 * there first.
 */
.novely-row-image {
  font-family: var(--font-mono);
  font-size: var(--size-10);
  letter-spacing: 0.01em;
  color: var(--muted-5);
}

/* --- figures ------------------------------------------------------------ */

/*
 * Padding, not margin, and this is load-bearing.
 *
 * A figure is a CodeMirror **block widget**, so its height goes into the height
 * map that positions every line and every gutter marker below it -- and
 * CodeMirror measures that height from the element's box, which does not
 * include margins. Margin on a block widget is height the editor cannot see.
 *
 * `margin: 0` is doing the real work here, and it is not tidiness: `figure` is
 * one of the handful of elements the browser's own stylesheet gives a margin
 * to, `1em 40px`, and at prose size that is 36px of invisible height and a
 * 40px indent nobody asked for. Every `you` under a picture sat exactly 36px
 * above the note it belonged to, pointing at the wrong paragraph, and no rule
 * in this file mentioned a margin at all.
 *
 * Any spacing on a block widget has to be padding.
 */
.novely-figure {
  margin: 0;
  padding: 10px 0 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  user-select: none;
}

.novely-figure-img,
.novely-figure-slot {
  display: none;
}

.novely-figure[data-state='picture'] .novely-figure-img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: 2px;
  border: 1px solid var(--hairline);
}

/*
 * The empty frame.
 *
 * Dashed, the width of the measure, and roughly the height a picture would
 * have been -- so a chapter whose pictures did not come across reads as a
 * chapter with gaps in it, which is the truth, rather than as prose with
 * stray filenames in the middle of it.
 */
.novely-figure[data-state='slot'] .novely-figure-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 116px;
  padding: 18px;
  border: 1px dashed var(--border);
  border-radius: 3px;
  background: var(--row-hover);
  text-align: center;
}

.novely-figure-slot.is-over {
  border-color: var(--accent);
  background: var(--selection);
}

.novely-figure-name {
  font: 400 var(--size-11) / 1.3 var(--font-mono);
  color: var(--secondary);
  overflow-wrap: anywhere;
}

.novely-figure-hint {
  font: 400 var(--size-11) / 1.4 var(--font-ui);
  color: var(--muted-4);
}

.novely-figure-choose {
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 3px 9px;
  background: transparent;
  color: var(--secondary);
  font: 400 var(--size-11) / 1 var(--font-ui);
  cursor: pointer;
}

.novely-figure-choose:hover {
  border-color: var(--border-hover);
  color: var(--text);
}

/* The picker itself is never the control; the button in front of it is. */
.novely-figure-file {
  display: none;
}

.novely-figure-caption {
  font: 400 var(--size-11) / 1.4 var(--font-ui);
  color: var(--muted-4);
}

.novely-figure-caption:empty {
  display: none;
}

/*
 * Scene break. The `***` stays in the buffer and stays on screen -- these are
 * the author's bytes -- but reads as the mark it is, between two rules drawn
 * without taking part in layout.
 */
.novely-row-scene {
  position: relative;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--size-10);
  letter-spacing: 0.2em;
  color: var(--muted-6);
}

.novely-row-scene::before,
.novely-row-scene::after {
  content: '';
  position: absolute;
  top: 50%;
  width: calc(50% - 34px);
  height: 1px;
  background: var(--border);
}

.novely-row-scene::before {
  left: 0;
}

.novely-row-scene::after {
  right: 0;
}

/* --- findings, and only in review -------------------------------------- */

/*
 * One ink, three weights. Trust is `proved` > `violated` > `observed`, and that
 * ordering is `kindWeight()`'s 1 / 0.7 / 0.4 -- which used to be laundered into
 * a hue's saturation where nothing could read it back. Now it is a border
 * style, which is a channel a reader can actually decode without a legend.
 */
.novely-row-found {
  background: var(--finding-wash);
  padding-left: 12px;
  border-left: 2px solid var(--border-strong);
}

.novely-found-strong {
  border-left-style: solid;
}

.novely-found-medium {
  border-left-style: dashed;
}

.novely-found-faint {
  border-left-style: dotted;
  border-left-color: var(--border);
}

.novely-finding-word {
  background: var(--finding-word);
  border-bottom: 1px solid var(--border-strong);
}

.novely-finding-word.novely-found-medium {
  border-bottom-style: dashed;
}

.novely-finding-word.novely-found-faint {
  border-bottom-style: dotted;
  border-bottom-color: var(--border);
}

/* --- the gutter -------------------------------------------------------- */

/*
 * Fixed width, in both states. Dots land in a column that was already here,
 * so nothing reflows under the cursor when a finding arrives.
 */
.novely-column .cm-gutters {
  background: transparent;
  border: 0;
  color: var(--muted-5);
}

/*
 * The gutter column, and the axis its alignment was on.
 *
 * This rule said `display: flex; justify-content: flex-end`, meaning "put the
 * marker at the right-hand edge of the column". Both halves were a mistake,
 * and the second one was the bug.
 *
 * CodeMirror's own theme already makes `.cm-gutter` a flex **column** with
 * `min-height: 100%`. On a column, `justify-content` is the *vertical* axis --
 * so `flex-end` pushed every marker to the bottom of a column as tall as the
 * document. Each `you` sat some 90px below the note it belonged to, beside the
 * paragraph after it, which is worse than no marker at all.
 *
 * Right-alignment is the cross axis here, and it belongs on the rows, where it
 * cannot move anything vertically.
 */
.novely-column .novely-gutter {
  width: var(--gutter-ch);
  min-width: var(--gutter-ch);
}

.novely-column .novely-gutter .cm-gutterElement {
  display: flex;
  justify-content: flex-end;
}

.novely-pills {
  display: flex;
  align-items: center;
  gap: 3px;
  height: calc(var(--size-prose) * var(--leading-prose));
}

/*
 * The pill is three characters, not a dot.
 *
 * `GUTTER_WIDTH_CH` is 3 and always was, which is exactly a three-letter code
 * -- the column did not have to move to stop being a row of coloured dots. The
 * code says what the note is about; the weight says how much to trust it; the
 * author's own mark is handled separately and is never hidden.
 */
/*
 * A tag code, wherever a tag code appears.
 *
 * *"Tag is a code. Finding kind is weight. Neither is a colour."* The code half
 * of that has one form -- uppercase monospace, tracked, 9-10px -- and it was
 * being written out four times in four different ways: the gutter pill had it
 * right, the tether card was half a pixel smaller for no reason, and the inline
 * note's was sans-serif and *lowercase*, so the same note read `PLT` in the
 * margin and `plt` in the prose. The author's word for that was
 * "inconsistent", and there is no argument for it -- one fact, one rule.
 */
.novely-pill,
.novely-inline-kind,
.novely-tether-code,
.novely-source-mark {
  font-family: var(--font-mono);
  font-size: var(--size-10);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.novely-pill {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  line-height: 1;
  color: var(--muted-5);
  border-bottom: 1px solid transparent;
}

.novely-pill-strong {
  color: var(--text);
  border-bottom-color: var(--border-strong);
}

.novely-pill-medium {
  border-bottom-style: dashed;
  border-bottom-color: var(--border-strong);
}

.novely-pill-faint {
  border-bottom-style: dotted;
  border-bottom-color: var(--border);
}

.novely-pill-closed {
  opacity: 0.45;
  text-decoration: line-through;
}

/*
 * "and more behind this one."
 *
 * Absolutely positioned so it costs no width: `GUTTER_WIDTH_CH` is the promise
 * that a note arriving never moves prose, and a count that took a character
 * would break it the first time a line collected a second note.
 */
.novely-pill-stacked {
  position: relative;
}

.novely-pill-stacked::after {
  /* "and more behind this one" -- the number is in the tooltip. */
  content: '\00b7';
  position: absolute;
  top: -0.15em;
  right: -0.55em;
  font-size: 1.1em;
  line-height: 1;
  color: var(--muted-5);
}

/*
 * Precision lost, not the note (§3.3).
 *
 * A hatched ground rather than a ring: the hatch reads as "this ground is not
 * solid", which is what a demoted anchor is. A ring reads as emphasis, and the
 * one thing a demoted note is not is more important than its neighbours.
 */
.novely-pill-demoted {
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0 2px,
    var(--border) 2px 3px
  );
}

/* The author's own mark, beside their own comment. Never hidden. */
.novely-gutter-you {
  font-family: var(--font-mono);
  font-size: var(--size-10);
  color: var(--muted-5);
  line-height: calc(var(--size-prose) * var(--leading-prose));
}

/* --- work-level notes -------------------------------------------------- */

.novely-work-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 36px;
}

.novely-work-rail:empty {
  display: none;
}

.novely-rail-pill {
  font-family: var(--font-ui);
  font-size: var(--size-11);
  color: var(--muted-2);
  background: transparent;
  border: 0;
  border-left: 2px solid var(--border-strong);
  border-radius: 0;
  padding: 2px 8px;
  cursor: pointer;
  text-align: left;
  /* One line, however long the finding's label is: the work rail is a column
     of markers beside the prose, not somewhere to read a sentence. */
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.novely-rail-pill:hover {
  color: var(--text);
}

/* --- status bar -------------------------------------------------------- */

.novely-statusbar {
  height: var(--statusbar-h);
  flex: none;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0 20px;
  font-family: var(--font-mono);
  font-size: var(--size-10-5);
  line-height: 1;
  color: var(--muted-5);
}

.novely-status-gap {
  flex: 1;
}

/*
 * `ch1 · scene 2 · 470w`, and the control that switches the count between words
 * and manuscript pages. It is a button so it can be pressed and reached from a
 * keyboard, and it inherits the bar's type so that nothing on screen says it is
 * one -- the status bar is a statement, not a dashboard.
 */
.novely-status-position {
  padding: 0;
  font-family: inherit;
  font-size: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  cursor: pointer;
}

.novely-status-position:hover {
  color: var(--text);
}

.novely-status-font {
  font-family: var(--font-mono);
  font-size: var(--size-10-5);
  color: var(--muted-5);
  background: transparent;
  border: 0;
  border-radius: var(--r-button);
  padding: 4px 6px;
  cursor: pointer;
}

.novely-status-font:hover {
  color: var(--text);
  background: var(--row-hover-strong);
}

.novely-status-state {
  color: var(--muted-6);
}

.novely-status-review {
  color: var(--muted-2);
}

/* --- the paragraph-style popover (5b) ---------------------------------- */

.novely-style-menu {
  width: 250px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-popover);
  box-shadow: var(--shadow-popover);
  padding: 6px;
  display: flex;
  flex-direction: column;
}

.novely-style-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border: 0;
  border-radius: var(--r-button);
  background: transparent;
  cursor: pointer;
  text-align: left;
}

.novely-style-item:hover {
  background: var(--row-hover-strong);
}

.novely-style-on {
  background: var(--row-active-strong);
}

.novely-style-label {
  flex: 1;
  font-family: var(--font-prose);
  font-size: var(--size-14);
  color: var(--text);
}

/* Each row is set in the type it names: the menu is a specimen sheet. */
.novely-style-chapter-title .novely-style-label {
  font-size: 16px;
  font-weight: 500;
}

.novely-style-section-heading .novely-style-label {
  font-weight: 500;
}

.novely-style-scene-break .novely-style-label {
  font-family: var(--font-mono);
  font-size: var(--size-11);
  letter-spacing: 0.2em;
  color: var(--muted-1);
  text-transform: uppercase;
}

.novely-style-block-quote .novely-style-label {
  font-style: italic;
  color: var(--muted-1);
}

.novely-style-verse .novely-style-label {
  font-size: var(--size-13);
  color: var(--muted-1);
}

.novely-style-key {
  font-family: var(--font-mono);
  font-size: var(--size-9-5);
  color: var(--muted-5);
}

.novely-style-rule {
  height: 1px;
  background: var(--hairline);
  margin: 5px 8px;
}

.novely-style-footnote {
  margin: 0;
  padding: 6px 10px;
  font-family: var(--font-mono);
  font-size: var(--size-10);
  line-height: 1.5;
  color: var(--muted-6);
}

/*
 * Inline mode (§3.5): the note, whole, after the paragraph it is about.
 *
 * Margin's job is to stay out of the way; this one's is the opposite, so the
 * constraints are different. It still has to read as *not the manuscript* --
 * an editorial note that looks like prose is a note the author will try to
 * revise -- which is what the rule, the indent and the UI font are doing here.
 *
 * The measure is deliberately the prose measure rather than the full width. A
 * note that runs wider than the paragraph it follows reads as a new section
 * rather than as an annotation of the one above it.
 */
.novely-inline-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 10px 0 14px;
  max-width: var(--measure);
}

/*
 * The long inline note, as the designer drew it (`1d`).
 *
 * *"Inline block -- no box. Hanging code, rules across the measure,
 * galley-proof feel."* Three specifics, and the first version had none of
 * them: it was a 2px rule down the left with the code stacked above the body,
 * which is a card with one side missing.
 *
 * - **Rules across the measure, not a box.** A line above and a fainter one
 *   below, the full width of the prose. It reads as apparatus set into the
 *   galley rather than as an object dropped on top of it, which is the whole
 *   difference between a note in a manuscript and a comment in a code review.
 * - **The code hangs.** Out in the margin beside the note, right-aligned
 *   against it, where it can be skimmed down the page without reading a word
 *   of the notes. Only where there is margin to hang in -- see below.
 * - **Weight is the rule.** `kindWeightClass` has emitted `-strong`,
 *   `-medium` and `-faint` on every one of these since it was written and
 *   nothing styled any of them, so the handoff's one decision that matters --
 *   *"tag is a code, finding kind is weight, neither is a colour"* -- was half
 *   implemented: the code was there and the weight was nowhere.
 */
.novely-inline-note {
  position: relative;
  border-top: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--hairline);
  padding: 8px 0 12px;
  margin: 0 0 24px;
  font-family: var(--font-ui);
  font-size: var(--size-14);
  line-height: 1.62;
  color: var(--text);
}

/* Trust as weight: solid, dashed, faint. The three steps `kindWeight()` has
   returned as 1 / 0.7 / 0.4 since before there was anything to draw. */
.novely-inline-strong {
  border-top-width: 2px;
  border-top-color: var(--secondary);
}

.novely-inline-medium {
  border-top-style: dashed;
  border-top-color: var(--border-strong);
}

/* The faint step is still the *top* rule, so it stays heavier than the hairline
   under it. At `--border` (12%) against `--hairline` (7%) the two read as one
   weight and the note came out looking like a table row, which is what the
   designer's review found. */
.novely-inline-faint {
  border-top-color: var(--border-strong);
}

.novely-inline-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
}

/*
 * The code hangs in the margin -- where there is a margin to hang it in.
 *
 * At this width the capped measure is centred with room either side, which is
 * the same room the tethered cards use on the other edge. Narrower than this
 * there is none, and a code positioned out there would sit on the navigator or
 * be clipped away, so it stays above the note instead. One layout that is
 * sometimes wrong is worse than two that are each right where they apply.
 */
/*
 * And the host does not clip what hangs out of it.
 *
 * `styles.css` gives `.novely-editor-host` `overflow: auto`, from before the
 * measure existed and the scroller stopped scrolling. Here it clips rather than
 * scrolls -- `.novely-measure` is what scrolls and `.cm-scroller` is
 * `overflow: visible` -- so its only remaining effect was to cut the first
 * letter off every hanging code. Nothing escapes the pane: the measure is still
 * `overflow: auto` around all of it.
 */
.novely-column .novely-editor-host {
  overflow: visible;
}

@media (min-width: 1100px) {
  .novely-inline-head {
    position: absolute;
    left: -64px;
    top: 6px;
    width: 52px;
    margin: 0;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
  }
}

/* One ink, because a tag is a code and not a colour. It was `--r-primary`,
   which is a *radius* (16px): `color: 16px` is not a declaration, so the one
   element meant to identify a note at a glance inherited the body's grey. */
.novely-inline-kind {
  color: var(--accent);
}

/* Who wrote it. Named `-by` because `-author` is the note's *origin* class on
   the article itself, and the two collided: the label's rule landed on the
   whole note and every author note rendered as 10px monospace. */
.novely-inline-by {
  font-family: var(--font-mono);
  font-size: var(--size-10);
  color: var(--muted-5);
}

.novely-inline-body {
  margin: 0;
  /* The author's own line breaks, kept. A note is often a list. */
  white-space: pre-wrap;
}

/* Settled, not gone. Same rule as the margin's closed pills. */
.novely-inline-closed {
  opacity: 0.55;
}

/* The author's own note reads as theirs, not as something handed to them. */
.novely-inline-you {
  border-left-color: var(--accent);
}

/*
 * A short note, in the run (§3.5).
 *
 * The rule this obeys: a two-word note costs two words of space. It sits in the
 * prose's own flow, so it wraps with the sentence and reflows with the measure
 * like any other run of text -- no rule across the page, no vertical room, no
 * paragraph broken in half to say four words.
 *
 * Brackets rather than a frame. A box mid-paragraph stops the line dead, which
 * is exactly what the long form is for and exactly what this one is not.
 */
.novely-run-note {
  font-family: var(--font-ui);
  font-size: var(--size-comment);
  color: var(--muted-5);
  white-space: normal;
}

.novely-run-note::before {
  content: ' [';
}

.novely-run-note::after {
  content: '] ';
}

.novely-run-code {
  font-family: var(--font-mono);
  font-size: var(--size-10);
  letter-spacing: 0.04em;
  margin-right: 4px;
  /* The same three characters the margin shows. One note, one code. */
  border-bottom: 1px solid transparent;
}

.novely-run-strong .novely-run-code {
  color: var(--text);
  border-bottom-color: var(--border-strong);
}

.novely-run-medium .novely-run-code {
  border-bottom: 1px dashed var(--border-strong);
}

.novely-run-faint .novely-run-code {
  border-bottom: 1px dotted var(--border);
}

/* Settled, not gone -- the same rule the margin and the block form follow. */
.novely-run-closed {
  opacity: 0.55;
}

/* The author's own short note reads as theirs. */
.novely-run-author .novely-run-code {
  color: var(--accent);
}

/* The same hatch, on both inline forms. One state, one texture (§3.3). */
.novely-inline-demoted,
.novely-run-demoted {
  background-image: repeating-linear-gradient(
    -45deg,
    transparent 0 3px,
    var(--border) 3px 4px
  );
}

.novely-inline-demoted {
  padding-left: 10px;
  border-left-style: dashed;
}

/*
 * A long note, folded (§3.5).
 *
 * Measured rather than guessed: the median note in a real manuscript is 69
 * words and there are about fifteen a chapter, so drawing them whole makes the
 * editorial layer longer than the prose it annotates.
 */
/*
 * Folded: one copy of the note, clamped, and a control that prices it.
 *
 * Three lines rather than one. A single clipped sentence with a chevron after
 * it is indistinguishable from a note that has been cut off, and on a real
 * manuscript that is exactly how it was read -- the author reported inline mode
 * as "cutting everything off". Three lines plainly continue; one line plainly
 * ends.
 */
.novely-inline-clamped {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

.novely-inline-more {
  display: block;
  margin-top: 0.35em;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: var(--size-10);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent);
}

.novely-inline-more::after {
  content: ' \00a0\25B8';
}

.novely-inline-more.is-open::after {
  content: ' \00a0\25BE';
}

.novely-inline-more:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/*
 * An empty mark: a caret slot at the text's own size, not a chip.
 *
 * The author has opened a comment and not yet written it -- `commentChange`
 * inserts exactly this and centres the caret in it. Given the full comment
 * decoration it came out as a saturated wash around three characters of
 * punctuation, which on a page of otherwise monochrome apparatus is the
 * register of a parse failure rather than of an empty note.
 *
 * The braces stay in the buffer and go unread: `color: transparent` leaves them
 * selectable, copyable and exactly where the author typed them, and leaves a
 * slot the width they occupy. Nothing is hidden that the author would miss --
 * the state lasts only until they type a character, at which point the comment
 * is no longer empty and takes the ordinary decoration.
 */
.novely-critic-void {
  background: none;
  border: 0;
  border-bottom: 1px dotted var(--border-strong);
  border-radius: 0;
  color: transparent;
  padding: 0 0.6em;
}

/* Under the pointer, the punctuation comes back -- for the one case the rule
   above does not cover: an author arriving at an old empty comment rather than
   one they are in the middle of writing. */
.novely-critic-void:hover {
  color: var(--muted-5);
  border-bottom-style: solid;
}

/*
 * Margin's marker: where a comment was, in three characters.
 *
 * Margin promises the notes are out of the prose, and the author's own
 * `{>>...<<}` was the one thing that never kept it -- a two-hundred-word remark
 * set at full length in the middle of the paragraph, which is Inline's job. The
 * words are in the tether card; this is the anchor the leader points at, and it
 * carries the same three-letter code the gutter shows for the same note.
 *
 * Sized and set so it reads as apparatus rather than as a word of the book:
 * the interface face, small, raised a little, and never wide enough to change
 * where the line breaks.
 */
.novely-source-mark {
  display: inline-block;
  vertical-align: 0.35em;
  margin: 0 0.15em;
  padding: 0 4px;
  border: 1px solid var(--border);
  border-radius: 2px;
  background: var(--panel);
  line-height: 1.5;
  color: var(--accent);
  cursor: default;
  /* A marker is apparatus: it must not be caught by a selection meant for the
     prose, or copying a paragraph would paste the codes into it. */
  user-select: none;
}

.novely-source-mark:hover {
  border-color: var(--accent);
}

/* Resolved notes keep their place in the prose and stop asking for attention. */
.novely-source-closed {
  color: var(--muted-5);
  border-color: var(--hairline);
  opacity: 0.6;
}

/* Weight: `kindWeightClass` emits strong/medium/faint, and the strong one is
   the only step that earns more than the hairline. */
.novely-source-strong {
  border-color: var(--accent);
}


/*
 * The author's own comment, set as a note where they wrote it (§3.2, 1d).
 *
 * A block in the run of the prose rather than after the paragraph, because it
 * *is* at that point in the prose: it replaces the `{>>...<<}` the author
 * typed. The margin under it keeps it from touching the lines either side, and
 * the left rule is the one every inline note carries, so a comment and a
 * finding read as the same kind of object -- which they now are.
 */
/*
 * A note belongs to the paragraph it is about, which is the one above it.
 *
 * Measured on the running build: 68px of space above and 68px below, so the
 * note floated exactly between two paragraphs and belonged to neither. Most of
 * that is the author's own blank lines either side of the comment and none of
 * it is ours to remove -- but the note's own margin is, and putting all of it
 * underneath is what turns an equidistant float into an attachment.
 */
.novely-inline-in-situ {
  display: block;
  margin: 0 0 1.25em;
}

/*
 * Whose note it is, in the margin.
 *
 * `novely-pill-author`, `-agent` and `-reader` have been emitted since the
 * marker was written and nothing ever styled them, so every note in the margin
 * looked like every other. That did not matter while the margin held an
 * agent's findings alone. It matters now that an author's own thousand
 * comments are the only things in it.
 *
 * The handoff asks for a solid stamp for an agent and an outline for the
 * author, and the obvious reading -- border style -- is already taken: it
 * carries `kind`, solid/dashed/dotted for proved/violated/observed. Using it
 * for origin as well would re-collapse the two axes into one channel, which is
 * the exact mistake the codes replaced the hue wheel to undo.
 *
 * So the free channel is the ground, and the default is inverted from the
 * mockup on purpose: the *author's* note is left plain, and the agent's is
 * stamped. In a book whose every note is the author's, marking them all marks
 * nothing; what earns a mark is a reading that arrived from outside. Drawn with
 * a background rather than a box because `GUTTER_WIDTH_CH` promises that a note
 * arriving never moves prose, and a border would spend a character to break it.
 */
.novely-pill-agent {
  background: color-mix(in srgb, var(--text) 7%, transparent);
  border-radius: 2px;
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--text) 7%, transparent);
}

.novely-pill-reader {
  background: none;
  box-shadow: 0 0 0 1px var(--border);
  border-radius: 2px;
}

/*
 * The tethered margin (design option `1a`).
 *
 * The layer sits inside `.novely-measure`, which is the element that scrolls --
 * `.cm-scroller` is `overflow: visible` here -- so a card positioned in it
 * travels with the prose and nothing has to run on scroll.
 *
 * `pointer-events` is off on the layer and back on for the card, so the leader
 * lines and the empty margin never intercept a click meant for the text. A note
 * layer that swallows selection is worse than no note layer.
 */
.novely-measure {
  position: relative;
}

.novely-tethers {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}

.novely-tether-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  /* Hairline, and the same ink as a rule rather than an accent: the tether is
     punctuation between two things, not a third thing to look at. */
  fill: var(--border-strong);
  stroke: var(--border-strong);
  stroke-width: 1;
  stroke-linejoin: round;
}

.novely-tether-lines path {
  fill: none;
}

.novely-tether-cards {
  position: absolute;
  inset: 0;
}

/*
 * Anchored to the right of the measure, not to the viewport: the card column
 * is whatever room the capped measure left over, which is exactly where the
 * designer put it and exactly what disappears first on a narrow window --
 * hence `marginFits`, which stops drawing rather than overlapping the prose.
 */
/*
 * The card column: one fixed width, in a track the prose is centred *with*.
 *
 * `--card-track` is the card and its channel together, and it appears in
 * exactly two rules -- here, and as the margin that shifts the prose column
 * left by half of it. Both are written in terms of the token, so they cannot
 * drift apart numerically.
 *
 * The arithmetic: `.novely-column` is centred in the measure, so its centre is
 * at 50% of the layer. Giving it a right margin of the track moves that centre
 * left by half the track, which is what centres the *pair*. The card then
 * starts at the column's right edge -- 50% - track/2 + measure/2 -- plus the
 * channel.
 */
.novely-tether-card {
  position: absolute;
  left: calc(
    50% - var(--card-track) / 2 + var(--measure, var(--measure-wide)) / 2 +
      var(--tether-channel)
  );
  width: var(--card-w);
  pointer-events: auto;
  /*
   * Two rows, not three columns.
   *
   * The card used to be `auto 1fr auto` -- code, body, open -- on one line, so
   * on a 200px card the note itself got about 120px and wrapped at three words
   * to the line. That was survivable while the whole comment was *also* in the
   * prose. It stopped being survivable when Margin took the comment out: the
   * card became the only place the words are, and it was showing them four to a
   * line with an ellipsis after eighty characters.
   *
   * So the apparatus takes a row of its own and the note gets the full width.
   */
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 2px 6px;
  padding: 5px 8px;
  border-left: 2px solid var(--border-strong);
  background: var(--page);
  font-family: var(--font-ui);
  font-size: var(--size-12-5);
  /* `--leading-ui` is not a token; the card inherited the prose's 1.72 and set
     a note in the margin at book leading, which is why they read as loose. */
  line-height: var(--leading-ui-prose);
  color: var(--muted-1);
}

/* Moved out of another card's way, and saying so quietly: the tether already
   shows where it belongs, so this only has to not look deliberate. */
.novely-tether-card.is-pushed {
  border-left-style: dashed;
}

.novely-tether-code {
  color: var(--muted-5);
}

.novely-tether-body {
  margin: 0;
  grid-column: 1 / -1;
  /* Long enough to be worth reading, short enough that forty of them still
     stack down one margin. `open` carries the rest to the companion. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
  line-clamp: 6;
  overflow: hidden;
}

/*
 * The phrase the card in hand is about.
 *
 * One ink at one strength, and no border: the wash says *these words* and the
 * tether already said *this note*, so a second colour here would be a third
 * channel for a fact two channels have already carried. It sits under the text
 * rather than round it -- a box would read as a selection, and the author has
 * one of those.
 */
.novely-anchor-hover {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border-radius: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .novely-anchor-hover {
    transition: background 120ms ease;
  }
}

/*
 * Triage, in the margin (`1a`).
 *
 * On its own row under the note, left-aligned, with `open` opposite -- the
 * actions are what the author does to the note and `open` is where they go to
 * read it, which are different kinds of thing and read better apart.
 *
 * Quiet until approached. Two controls per card times fifteen cards is thirty
 * things competing with a page of prose, so at rest they are the ground's own
 * grey and only the one under the pointer is ink.
 */
.novely-tether-actions {
  grid-column: 1 / -2;
  display: flex;
  gap: 10px;
  align-items: baseline;
}

.novely-tether-action {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: var(--size-11);
  letter-spacing: var(--shell-track, 0.04em);
  color: var(--muted-5);
  border-bottom: 1px solid transparent;
}

.novely-tether-action:hover {
  color: var(--text);
  border-bottom-color: var(--border-strong);
}

.novely-tether-action:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The card the pointer is on brings its actions up with it, so they are legible
   before they are aimed at rather than after. */
.novely-tether-card:hover .novely-tether-action {
  color: var(--muted-3);
}

.novely-tether-open {
  grid-column: 2 / -1;
  justify-self: end;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--size-9-5);
  color: var(--muted-5);
  text-decoration: underline;
}

.novely-tether-open:hover,
.novely-tether-open:focus-visible {
  color: var(--text);
}

/*
 * Re-anchor, on a note that lost its place (§3.3, design option `2e`).
 *
 * Quiet: it is an offer, not a warning. The hatched ground has already said
 * something is wrong, and a second loud thing beside it would make a note the
 * author may reasonably ignore look like a task they have to clear.
 */
.novely-inline-reanchor {
  margin-left: auto;
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--size-9-5);
  letter-spacing: 0.04em;
  color: var(--muted-5);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.novely-inline-reanchor:hover,
.novely-inline-reanchor:focus-visible {
  color: var(--text);
}

/*
 * Interlinear notes (§3.5, design option `2c`).
 *
 * The scribe's gloss: the note sits between the lines, above the words it
 * doubts, and the prose is never moved sideways to make room for it.
 *
 * The room comes from the line rather than from the chapter. The mockup sets
 * the whole page on 2.5 leading so there is space above every line; that makes
 * an unannotated paragraph pay for an annotated one, and on a manuscript with
 * fifteen notes a chapter most paragraphs are unannotated. `novely-line-glossed`
 * is added only to lines that carry a gloss, so the rest of the chapter is set
 * exactly as it was.
 */
.novely-line-glossed {
  /*
   * Leading, not padding on the top of the block.
   *
   * Padding was the first attempt and it is wrong for a reason worth keeping:
   * a CodeMirror line is a *logical* line -- a whole paragraph -- and padding
   * above it only makes room over the paragraph's first visual row. A phrase
   * three rows down got a gloss printed across the prose above it.
   *
   * Leading gives every wrapped row in the paragraph the same room, which is
   * what the designer's mockup does for the whole chapter. Scoping it to the
   * paragraphs that actually carry a gloss is the only change: on a manuscript
   * with fifteen notes a chapter, most paragraphs have none and are set exactly
   * as they were.
   */
  /*
   * 2.7, and the number is arithmetic rather than taste. At the manuscript's
   * 18px the half-leading above a line is `(2.7 x 18 - 18) / 2` = 15.3px; the
   * gloss is 11.5px and wants ~3px of air under it, so it needs 14.5px. 2.4 --
   * and the mockup's 2.5 -- leave it a couple of pixels short, and the gloss
   * prints into the descenders of the line above, where it reads as belonging
   * to that line instead of to its own.
   */
  line-height: 2.7;
}

/* Zero width: the prose is laid out as though the gloss were not there. */
.novely-inter-slot {
  position: relative;
  display: inline-block;
  width: 0;
  height: 0;
  overflow: visible;
  vertical-align: baseline;
}

.novely-inter {
  position: absolute;
  left: 0;
  /*
   * Measured up from the baseline, not from the top of a box.
   *
   * The slot is zero-height and sits on the baseline, so `bottom: 100%` -- the
   * obvious spelling, and the mockup's -- resolves to zero and prints the gloss
   * straight through the words it is about.
   */
  bottom: 0.92em;
  /* One unbroken line above one unbroken phrase. It cannot wrap -- which is the
     whole reason `INTERLINEAR_WORDS` is four. */
  white-space: nowrap;
  font-family: var(--font-heading);
  font-size: var(--size-11-5);
  line-height: 1;
  letter-spacing: 0.02em;
  /* `--muted-2`, not `--muted-4`. At 11.5px a gloss set at 38% of the ink is a
     watermark: present, and not readable at reading distance. The designer's
     mockup sets it near 65%, which is what this is. */
  color: var(--muted-2);
  pointer-events: auto;
}

.novely-inter-code {
  font-family: var(--font-mono);
  font-size: var(--size-9-5);
  letter-spacing: 0.06em;
  color: var(--muted-4);
  margin-right: 4px;
}

/* The same weights the margin uses, so a kind reads the same in every form. */
.novely-inter-strong { color: var(--text); }
.novely-inter-faint { color: var(--muted-5); }

/* Which words the gloss is about. Without this a remark floats over a sentence
   without saying what in it was doubted. */
.novely-inter-phrase {
  border-bottom: 1px solid var(--border-strong);
}

.novely-inter-demoted .novely-inter-body {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/*
 * The join between two chapters (§3.5).
 *
 * A book is one thing kept in seventy files, and the files are the author's
 * business rather than the reader's: scrolling off the end of a chapter used to
 * stop dead, which is the filing system showing through the book. So the
 * chapter before and the chapter after are drawn at the edges of this one, and
 * reading past the rule opens that chapter with the position kept.
 *
 * Set in the same face, at the same size and leading, on the same measure as
 * the prose it continues -- because it *is* the prose it continues. The only
 * thing that separates it is ink: a preview is quieter than the chapter the
 * author is actually in, so there is never a question about which one that is.
 */
.novely-continue {
  font-family: var(--manuscript-font, var(--font-prose));
  font-size: var(--size-prose);
  line-height: var(--leading-prose);
  color: var(--muted-4);
  /* Never taken by a selection or a find: these words are on the page a second
     time the moment the author crosses into them. */
  user-select: none;
}

/* Set exactly as the manuscript sets a paragraph -- separated by space, never
   indented. A preview that indented where the chapter does not would read as a
   different kind of text, which is the one thing it is not. */
.novely-continue-line {
  margin: 0 0 var(--size-prose);
}

/*
 * The rule that names the chapter on the other side.
 *
 * It is the only thing in the join that is not prose, so it carries the whole
 * job of saying where the author is: a hairline across the measure with the
 * chapter's number and title sitting in it.
 */
.novely-continue-rule {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 2.5em 0;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-heading);
  font-size: var(--size-12-5);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-3);
}

.novely-continue-order {
  font-family: var(--font-mono);
  font-size: var(--size-9-5);
  letter-spacing: 0.06em;
  color: var(--muted-5);
  text-transform: none;
}

.novely-continue-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
