/* semgit — the notes: cards in the margin (6c), note density, the notes put
   away, the sentence marks and change colours, the provenance pills in the
   left channel, the Changes compare layout and the slide (#70, #74).
   Linked after sheet.css. */

/* A sentence, wrapped by notes.js so an anchor has something to point at. No
   style at all: it must not cost the text a pixel. */
.sent {}

/* The notes' own tokens, scoped here because tokens.css is closed to this
   lane (#74). The product's semantics as colour (UI-BRIEF 2.1): a discussion
   is --focus, a meaning change --meaning, wording and an addition --wording,
   a block --blocked and nothing else; a removal is --danger, since it is not
   blocked; needs-attention keeps --warn. The washes are mixed from the
   tokens, since the map carries none. */
.paper {
  --blue:         var(--focus);
  --blue-wash:    color-mix(in srgb, var(--focus) 11%, transparent);
  --meaning-wash: color-mix(in srgb, var(--meaning) 12%, transparent);
  --wording-wash: color-mix(in srgb, var(--wording) 13%, transparent);
  --note-w: 268px;
  --note-r: 10px;
  /* The channel pill straddles the sheet's edge: this much of it is over the
     paper, the rest hangs in the channel. */
  --pill-in: 26px;
}

/* ------------------------------------------- 6c. annotations in the margin ---

   Not a panel. Git has no such thing as a comment on a line — `git notes`
   attaches text to a whole commit and that is the extent of it — so anchored
   discussion is semgit's own layer, kept in SQLite while git keeps the content
   (SPEC A.4). The anchor is {para, sent, hash} per G.2, and the hash is how a
   note finds out its sentence changed underneath it.

   Because these are *about a sentence*, they are drawn beside that sentence.
   notes.js sets each card's `top` from its anchor's position and pushes
   overlapping cards down the stack; CSS owns everything else. */

/* The gutter is reserved, not hoped for. Earlier this waited for the paper to
   happen to have space beside it, which on a 1440px window it does not — the
   cards were simply never drawn. Now a document that has annotations keeps a
   column for them and the sheet centres in what is left. */
.paper.has-notes { padding-right: calc(var(--note-w) + var(--channel)); }

.margin {
  position: absolute;
  top: 0;
  /* Fallback for no-JS; notes.js sets this from the sheet's real right edge. */
  left: calc(50% + var(--sheet-w) / 2 + var(--channel));
  width: var(--note-w);
  /* Cards take the pointer; the space between them is still the desk. */
  pointer-events: none;
}

/* The card, as the mockup draws it: white, soft corners, floating, level with
   the first line of the sentence it is about. A coloured dot and a bold head
   say what it is; a badge says its state; the text; then who and how much
   discussion. No number — the card is beside its sentence, which is the
   reference. */
.note {
  position: absolute;
  left: 0;
  width: var(--note-w);
  pointer-events: auto;
  display: grid;
  row-gap: 5px;
  padding: 12px 14px 12px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--note-r);
  box-shadow: var(--e3);
  color: inherit;
}
/* No motion on a card (UI-BRIEF 3): the linked state is a change of border,
   at once. */
.note:hover, .note.is-active {
  border-color: var(--line);
  text-decoration: none;
}

.note-head {
  display: flex; align-items: center; gap: 7px;
  min-width: 0;
  font-size: 12px; font-weight: 650; line-height: 1.3;
  color: var(--ink);
}
.note-dot {
  flex: none;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--blue);
}
.note-kind { white-space: nowrap; }
.note-badge {
  flex: none;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--blue-wash);
  color: var(--blue);
  font-size: 10px; font-weight: 500; line-height: 1.5;
  letter-spacing: .01em;
  white-space: nowrap;
}
/* What the dot and the badge say, by kind: a question is --focus, a flag
   needing attention amber, a block --blocked, an addition and a rewording
   --wording, a meaning change --meaning, a removal red, a noted flag grey. */
.note[data-kind="flag"] .note-dot,
.note[data-flag="flag"] .note-dot { background: var(--warn); }
.note[data-kind="flag"] .note-badge { background: var(--warn-wash); color: var(--warn); }
.note[data-kind="flag"][data-severity="note"] .note-dot { background: var(--ink-3); }
.note[data-kind="flag"][data-severity="note"] .note-badge { background: var(--sunken); color: var(--ink-2); }
.note[data-severity="block"] .note-dot,
.note[data-flag="block"] .note-dot { background: var(--blocked); }
.note[data-kind="flag"][data-severity="block"] .note-badge { background: var(--danger-wash); color: var(--blocked); }
.note[data-kind="added"] .note-dot,
.note[data-kind="surface"] .note-dot { background: var(--wording); }
.note[data-kind="added"] .note-badge,
.note[data-kind="surface"] .note-badge { background: var(--wording-wash); color: var(--wording); }
.note[data-kind="meaning"] .note-dot { background: var(--meaning); }
.note[data-kind="meaning"] .note-badge { background: var(--meaning-wash); color: var(--meaning); }
.note[data-kind="removed"] .note-dot { background: var(--danger); }
.note[data-kind="removed"] .note-badge { background: var(--danger-wash); color: var(--danger); }
.note[data-state="resolved"] .note-badge { background: var(--sunken); color: var(--ink-3); }

/* A noted flag is collapsed until it is looked at (UI-BRIEF 4.1): the head
   says what it is; the rest comes on hover. */
.note[data-kind="flag"][data-severity="note"] :is(.note-meta, .note-title, .note-body, .note-foot),
.note-flag[data-severity="note"] :is(.note-title, .note-foot) { display: none; }
.note[data-kind="flag"][data-severity="note"]:is(:hover, .is-active) :is(.note-meta, .note-title, .note-foot),
.note:is(:hover, .is-active) .note-flag[data-severity="note"] :is(.note-title, .note-foot) { display: block; }
.note[data-kind="flag"][data-severity="note"]:is(:hover, .is-active) .note-body { display: -webkit-box; }

.note[data-state="resolved"] { opacity: .55; }
.note[data-state="resolved"]:hover { opacity: 1; }
.note[data-state="resolved"] .note-title { text-decoration: line-through; }

/* After the badge: the scope of a pinned note. */
.note-scope {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; font-weight: 400; font-style: italic;
  color: var(--ink-3);
}
/* A verdict's second line: the intent, and where the label came from. */
.note-meta {
  margin-top: -2px;
  font-size: 10.5px; line-height: 1.3;
  color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.note-title { font-size: 12px; line-height: 1.4; color: var(--ink); }
.note-body {
  font-size: 11.5px; line-height: 1.5; color: var(--ink-2);
  overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
}
/* One line: `author · 4 comments`, or `author · 2d ago` when the row carries
   no count. */
.note-foot {
  margin-top: 3px;
  font-size: 10.5px; line-height: 1.3; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The anchored sentence, marked at rest. A finding you have to go looking for
   is a finding that gets missed, so the mark is always on; hovering only
   deepens what is already there. A hairline in the note's blue, so several
   anchored sentences in one paragraph still read as prose.

   INVARIANT: backgrounds and underlines only. Vertical padding and borders on
   an inline box do not touch the line box, so the text does not move; nothing
   here may become horizontal. Measured by tools/ui_drift.py, not assumed. */
.anchor {
  border-bottom: 1px solid var(--blue);
  cursor: pointer;
}
.anchor[data-severity="block"] { border-bottom-color: var(--blocked); }
.anchor:hover, .anchor.is-active { background: var(--blue-wash); }
.anchor[data-severity="block"]:hover,
.anchor[data-severity="block"].is-active { background: var(--danger-wash); }

/* Changes mode (#58, §G.5 `changes`): a changed sentence is marked in the
   text by what kind of change it is, with a shape as well as a colour (G.4,
   UI-BRIEF 2.1): added is washed and underlined in --wording, a meaning
   change carries a --meaning hairline, a wording-only change a dotted
   --wording one. A removed sentence has no text here; its note says so,
   beside the sentence it followed, which is not itself marked. A flag on the
   change colours the text by what it is: --blocked where it blocks, amber
   where it needs attention. */
.sent[data-change="added"]   { background: var(--wording-wash); border-bottom: 2px solid var(--wording); }
.sent[data-change="meaning"] { border-bottom: 1px solid var(--meaning); }
.sent[data-change="surface"] { border-bottom: 1px dotted var(--wording); }
.sent[data-change="removed"] { border-bottom: 0; }
.sent[data-change="meaning"]:hover, .sent[data-change="meaning"].is-active { background: var(--meaning-wash); }
.sent[data-change="surface"]:hover, .sent[data-change="surface"].is-active,
.sent[data-change="added"]:hover, .sent[data-change="added"].is-active { background: var(--wording-wash); }
.sent[data-flag="flag"] { background: var(--warn-wash); border-bottom: 1px solid var(--warn); }
.sent[data-flag="block"] { background: var(--danger-wash); border-bottom: 1px solid var(--blocked); }
.sent[data-flag="flag"]:hover, .sent[data-flag="flag"].is-active { background: var(--warn-wash); filter: brightness(.96); }
.sent[data-flag="block"]:hover, .sent[data-flag="block"].is-active { background: var(--danger-wash); filter: brightness(.96); }

/* In the editor the sentence cannot carry the underline itself (nothing may
   be written into ProseMirror's DOM), so notes.js draws it into the marks
   layer: one bar per line the sentence covers, styled like the anchor. */
.mark-line {
  position: absolute;
  box-sizing: content-box;
  border-bottom: 1px solid var(--blue);
}
.mark-line[data-severity="block"] { border-bottom-color: var(--blocked); }
.mark-line[data-change="added"] { background: var(--wording-wash); border-bottom: 2px solid var(--wording); }
.mark-line[data-change="meaning"] { border-bottom-color: var(--meaning); }
.mark-line[data-change="surface"] { border-bottom-style: dotted; border-bottom-color: var(--wording); }
.mark-line[data-flag="flag"] { background: var(--warn-wash); border-bottom-color: var(--warn); }
.mark-line[data-flag="block"] { background: var(--danger-wash); border-bottom-color: var(--blocked); }
.mark-line.is-active { background: var(--blue-wash); }
.mark-line[data-severity="block"].is-active { background: var(--danger-wash); }

/* The change note: `Change` and the kind in the head, the intent after it,
   the wording that changed as the body — struck where it went, underlined
   where it came. A flag on the unit hangs under it as its own link. */
.note.change { cursor: default; }
.note-diff {
  font-family: var(--prose);
  font-size: 12px; line-height: 1.45;
  color: var(--ink-2);
}
.note-diff del { color: var(--ink-3); text-decoration: line-through; text-decoration-color: var(--ink-3); }
.note-diff ins { color: var(--ink); text-decoration: underline; text-decoration-color: var(--wording); text-underline-offset: 2px; }
.note-diff del + ins { margin-left: 3px; }
.note[data-change="removed"] .note-diff del { color: var(--ink-2); text-decoration-color: var(--danger); }
.note-flag {
  display: grid; row-gap: 3px;
  margin-top: 4px;
  padding-top: 7px;
  border-top: 1px solid var(--line-2);
  color: inherit;
}
.note-flag:hover { text-decoration: none; }
.note-flag:hover .note-title { text-decoration: underline; }
.note-flag .note-head { font-size: 11px; }
.note-flag .note-kind { color: var(--warn); }
.note-flag .note-badge { background: var(--warn-wash); color: var(--warn); }
.note-flag[data-severity="block"] .note-kind { color: var(--blocked); }
.note-flag[data-severity="block"] .note-badge { background: var(--danger-wash); color: var(--blocked); }
.note-flag[data-severity="note"] .note-kind { color: var(--ink-2); }
.note-flag[data-severity="note"] .note-badge { background: var(--sunken); color: var(--ink-2); }
.note-flag .note-foot { display: flex; gap: var(--s2); margin-top: 0; }

/* ------------------------------------------------- the left channel ---

   Provenance, beside the paragraph: `added in #31`, `changed in #31` — which
   merged submission the paragraph came from. In Changes the same pills say
   the verdicts' kinds beside the branch page. A white pill on a hairline,
   fully rounded, straddling the sheet's left edge with --pill-in of it over
   the paper. notes.js sets the channel's left from the sheet's real edge and
   each pill's top from its paragraph's first line, in the same pass as the
   cards; shell.js's Fit subtracts what hangs outside the sheet. */
.channel {
  position: absolute;
  top: 0;
  width: 0;
  /* Fallback for no-JS; notes.js sets this the same way .margin's is set. */
  left: calc(50% - var(--sheet-w) / 2);
  z-index: 2;
  pointer-events: none;
}
.channel-pill {
  position: absolute;
  right: calc(-1 * var(--pill-in));
  display: inline-flex; align-items: center;
  height: 24px;
  padding: 0 10px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--e3);
  font-family: var(--ui);
  font-size: 11px; font-weight: 450; line-height: 1;
  color: var(--ink-3);
  white-space: nowrap;
  pointer-events: auto;
}
.channel-pill:hover { color: var(--ink); text-decoration: none; border-color: var(--line); }
.channel-pill[data-kind="removed"] { color: var(--danger); }
.channel-pill[data-kind="added"] { color: var(--wording); }

/* Moving between modes (#58): the sheet you were reading slides to where
   the next mode puts it, and what is new fades in. modes.js sets the
   transform for one frame and then lets it go; the transition does the rest. */
.is-sliding { transition: transform 360ms var(--ease), opacity 280ms var(--ease); }
.is-arriving { opacity: 0; }
.margin.is-sliding, .submissions.is-sliding, .channel.is-sliding { transition-delay: 120ms; }

/* Two documents on the desk (Changes, #58): main on the left as it stands,
   the branch on the right as proposed, and the verdicts in the channel
   between them. The channel is the notes column, narrowed, plus a lane for
   the pills that straddle the branch page's left edge; notes.js puts the
   cards off main's right edge. The branch sheet is first in the DOM — it is
   the document — and main is placed left by order. Each sheet is still
   210mm; when two do not fit the window, one Fit zooms them together
   (shell.js), and the channel stays unzoomed like the margin. */
.compare {
  --note-w: 200px;
  /* What hangs outside the branch page: the widest pill, `changed`, less
     --pill-in, and a channel's breathing room. */
  --pill-lane: 44px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  column-gap: calc(var(--note-w) + 2 * var(--channel) + var(--pill-lane));
}
.compare .sheet { margin: 0; }
.compare .sheet.base { order: -1; }
/* Stated with the density's specificity too: zoomed out, the column between
   two pages keeps its width — it is the comparison, not an overview. */
.paper:has(.compare), body[data-density] .paper:has(.compare) { --note-w: 200px; --pill-lane: 44px; }
/* The column is between the pages, so no gutter is reserved to the right. */
.paper.has-notes:has(.compare) { padding-right: 0; }

/* The submissions, always in view: the paper becomes two columns, the list
   and the spread, and the floating history panel steps aside — a review
   surface does not fold its list away. The dock, the pages and the pagefoot
   take the second column; the margin and the channel are positioned, so the
   grid does not touch them. */
.paper:has(.compare) {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  column-gap: var(--s4);
  align-items: start;
}
.paper:has(.compare) > *, .paper:has(.compare) .mode > * { grid-column: 2; }
/* Rows stated, not left to auto-placement: the dock is a child of the
   editor's form (display: contents), so it would otherwise take the first
   free cell — the list's column. */
.paper:has(.compare) .dock { grid-column: 2; grid-row: 1; }
.paper:has(.compare) .compare { grid-row: 2; }
.paper:has(.compare) .compare-foot { grid-row: 3; }
/* Positioned children take their grid area as containing block; these are
   positioned against the whole paper, so they get no area. */
.paper:has(.compare) .margin, .paper:has(.compare) .channel { grid-column: auto; grid-row: auto; }
.submissions {
  grid-column: 1 !important;
  grid-row: 2 / span 3;
  position: sticky;
  top: calc(var(--dock-h) + var(--s4));
  display: flex; flex-direction: column;
  padding: 0 var(--s2) var(--s2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
}
.submissions .rail-head { padding: var(--s2) var(--s2) 0; }
.submissions .commit[aria-current="page"] { background: var(--sunken); }
.desk:has(.compare) .rail { display: none; }
/* The channel is where the dock floats, so the column starts under the dock
   rather than level with it. No head and no fold: the column between two
   pages is the comparison, and the count is in the pagefoot. */
.paper:has(.compare) .margin { top: calc(var(--dock-h) + var(--s4)); }
.compare-foot {
  max-width: none;
  width: calc(2 * var(--sheet-w) * var(--zoom, 1) + var(--note-w) + 2 * var(--channel) + var(--pill-lane));
}

/* On main the change is what is about to go: a removed sentence struck
   through where it still exists, in red, a changed one underlined where it
   was. */
.sheet.base .sent[data-change="removed"] {
  text-decoration: line-through;
  text-decoration-color: var(--danger);
  background: var(--danger-wash);
  border-bottom: 0;
  color: var(--ink-2);
}
.sheet.base .sent[data-change="added"] { background: none; border-bottom: 0; }
.sheet.base .sent[data-change="meaning"],
.sheet.base .sent[data-change="surface"] { border-bottom: 1px dotted var(--ink-3); }

/* ------------------------------------------------------- note density ---

   Zooming out shrinks the page but a 268px card does not shrink with it, so
   at 50% the annotations are two thirds as wide as the manuscript. Scaling
   them with the page is the obvious fix and the wrong one: the text goes to
   6px and stops being readable, which is the only thing a note is for.

   Instead the notes change what they are. Zooming out is asking for an
   overview, so they become one — first losing their body, then everything
   but the dot, until the margin is a density map of where the argument is in
   the document (G.4's "Patchwork minibar for locating changes in long
   documents"). Hovering any of them brings the whole card back.

   shell.js sets data-density from the zoom; the three states live here. */

body[data-density="compact"] .paper { --note-w: 190px; }
body[data-density="marks"] .paper   { --note-w: 26px; }

[data-density="compact"] .note-body,
[data-density="compact"] .note-foot { display: none; }
[data-density="compact"] .note-title { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }

[data-density="marks"] .note {
  padding: 6px 0;
  justify-items: center;
  border-color: transparent;
  box-shadow: none;
  background: none;
}
[data-density="marks"] .note-head { gap: 0; }
[data-density="marks"] .note-head > :not(.note-dot),
[data-density="marks"] .note-meta,
[data-density="marks"] .note-title,
[data-density="marks"] .note-body,
[data-density="marks"] .note-diff,
[data-density="marks"] .note-flag,
[data-density="marks"] .note-foot { display: none; }
[data-density="marks"] .note-dot { width: 8px; height: 8px; }

/* Whatever the density, one note at a time can be read in full. */
[data-density="compact"] .note:hover,
[data-density="compact"] .note.is-active,
[data-density="marks"] .note:hover,
[data-density="marks"] .note.is-active {
  z-index: 2;
  width: 268px;
  padding: 12px 14px 12px;
  justify-items: stretch;
  background: var(--surface);
  border-color: var(--line);
  box-shadow: var(--e3);
}
[data-density="marks"] .note:hover .note-head,
[data-density="marks"] .note.is-active .note-head { gap: 7px; }
[data-density="marks"] .note:hover .note-dot,
[data-density="marks"] .note.is-active .note-dot { width: 6px; height: 6px; }
[data-density] .note:hover .note-head > :not(.note-dot),
[data-density] .note.is-active .note-head > :not(.note-dot) { display: revert; }
[data-density] .note:hover .note-foot,   [data-density] .note.is-active .note-foot { display: block; }
[data-density] .note:hover .note-meta,   [data-density] .note.is-active .note-meta { display: block; }
[data-density] .note:hover .note-title,  [data-density] .note.is-active .note-title { display: block; }
[data-density] .note:hover .note-body,   [data-density] .note.is-active .note-body { display: -webkit-box; }
[data-density] .note:hover .note-diff,   [data-density] .note.is-active .note-diff { display: block; }
[data-density] .note:hover .note-flag,   [data-density] .note.is-active .note-flag { display: grid; }

/* The column's old head — `3 NOTES ›` with its fold — is gone (#74): the
   notes on/off and Clean reading are the View menu's, and the count said
   nothing the cards do not. base.html still renders the element until lane
   1 drops it (#71); until then it is not shown. */
.gutter { display: none; }

/* ------------------------------------------------- the notes, put away ---

   Two questions, not one setting with three values:

     the fold    put the notes away and give the page its width back. A View
                 menu choice ([data-notes-toggle], #71); notes.js sets
                 body[data-notes] and remembers it.
     clean       the manuscript with nothing of semgit on it: no notes, no
                 pills, no underlines. Rare, and a different way of reading
                 rather than a third position of the same switch, so it is
                 the View menu's Clean reading.
*/

/* Folded, the notes go and their marks stay: the underlines are what is left
   saying where the discussion is. They cost the text nothing either way. The
   provenance pills stay too — they are the page's, not the discussion's. */
/* Neither state reaches the Changes spread: there, the notes and the marks
   are the comparison, and two pages with nothing between them say nothing. */
[data-notes="off"] .paper:not(:has(.compare)) .note { display: none; }

[data-clean] .paper:not(:has(.compare)) .anchor {
  border-bottom: 0;
  cursor: auto;
}
[data-clean] .paper:not(:has(.compare)) .anchor:hover { background: none; }
[data-clean] .paper:not(:has(.compare)) .marks { display: none; }
/* Nothing of semgit on the page: the way back is the same menu the reader
   came in by. */
[data-clean] .paper:not(:has(.compare)) .note,
[data-clean] .paper:not(:has(.compare)) .channel { display: none; }

/* When the paper has no room beside it, the notes collapse away and the
   sentences keep their marks. notes.js decides that by measuring, not a
   media query: whether there is room depends on the window AND on whether
   the history panel is open, which a breakpoint cannot see. */
.no-margin .note { display: none; }

/* Select a sentence and the ways to say something about it come to the
   selection, rather than the reader going to find a form. §G.4 requires opening
   an issue from a text selection; this is where that starts. */
.selbar {
  position: absolute;
  z-index: 7;
  display: none;
  align-items: center; gap: var(--s1);
  padding: var(--s1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: calc(var(--r3) + 2px);
  box-shadow: var(--e2);
  /* Sits above the text it is about, and points at nothing while hidden. */
  transform: translate(-50%, calc(-100% - var(--s2)));
}
.selbar.is-open { display: flex; }
.selbar button {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s1) var(--s3);
  border: 0;
  border-radius: var(--r2);
  background: transparent;
  font-size: var(--t-sm);
  color: var(--ink-2);
  white-space: nowrap;
  cursor: pointer;
}
.selbar button:hover { background: var(--sunken); color: var(--ink); }
.selbar .sep { width: 1px; align-self: stretch; margin: 2px 0; background: var(--line); }

/* The marks layer over the page.

   INVARIANT, and the sibling of the one on --sheet-w: the markup never moves a
   word. Turning it on or off changes what is drawn over the manuscript and
   nothing about the manuscript — not a line break, not a line count, not the
   height of the page. The layer is absolutely positioned over the page, so
   nothing in it is part of a line; in Read it is empty, in Edit it holds the
   bars notes.js draws for the anchored sentences (.mark-line). The numbers
   that used to sit in the page's right margin are gone (#74). */
.marks {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* The notes' share of the print rules; the base is in tokens.css. */
@media print {
  .anchor { border-bottom: 0; background: none; }
  .channel { display: none !important; }
}
