/* The chart itself. This stylesheet is shared by the on-screen view and the
   printed page, so what you proof is what comes out of the PDF. */

/* The page the chart is composed onto. In Build this is a plain wrapper at
   the chart's old page width; in Layout and Read it is given the frame's
   exact pixel size by js/reading.js and scaled to the window.

   It is also the chart's query container, which is the point: the reflow
   rules further down have to answer to the *page's* width, not the browser
   window's. Keyed to the window, composing in a narrow window would quietly
   lay the chart out differently from the iPad that reads it — which is the
   one thing a fixed page is supposed to make impossible. */
.chart-frame {
  container-type: inline-size;
  container-name: chart;
  max-width: 860px;
  margin: 0 auto;
}

/* The dials the Layout panel turns. All neutral at 1, all written straight
   onto this element as inline custom properties by paintLayout in
   js/reading.js — which is what lets a slider move without re-rendering the
   chart, and a re-render means VexFlow redrawing every stave on the page.

   Defaults are declared here rather than only in the JS so a chart still
   looks right in any context that never runs paintLayout: the print path,
   the test harness, a chart rendered into the groove bench. */
.chart {
  --chart-notation: 1;
  --chart-text: 1;
  --chart-markings: 1;
  --chart-title: 1;
  --chart-pad: 1;
  --chart-space: 1;
  /* Wide enough for the section names that actually turn up. The name
     field is this less the 22px the ▾ beside it takes, so 92px left
     PRE-CHORUS, GUITAR SOLO, BREAKDOWN and INTERLUDE all wrapping onto a
     second line while INTRO sat in a third of the room. INSTRUMENTAL still
     wraps; past that the staves start paying more than the names are
     worth. Score keeps its own 92px — its page is finished by eye against
     a real page, and the width that suits the editing view is not the one
     Adam wants printed. See LAYOUT_DEFAULTS in reading.js. */
  --chart-label-w: 128px;

  --chart-ink: #000;
  --chart-line: #b9bec7;
  --chart-faint: #7b818c;
  --chart-warn: #a15c00;
  --chart-lyric: #2456a6;
  background: #fff;
  color: var(--chart-ink);
  width: 100%;
  padding: calc(22px * var(--chart-pad)) calc(26px * var(--chart-pad)) calc(30px * var(--chart-pad));
  border-radius: 3px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.14), 0 8px 24px rgba(0, 0, 0, 0.06);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* --- heading -------------------------------------------------------- */

.chart-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 4px 12px;
  border-bottom: 2px solid var(--chart-ink);
  padding-bottom: 6px;
  margin-bottom: 12px;
}
.chart-title { font-size: calc(24px * var(--chart-title) * var(--tz, 1)); margin: 0; font-weight: 700; letter-spacing: -0.015em; }
.chart-meta { grid-column: 1; font-size: calc(13px * var(--chart-text) * var(--tz, 1)); color: var(--chart-faint); }

/* The notehead in a tempo marking, set in the music font rather than in
   whatever the system has — see TEMPO_NOTE_VALUES in js/model.js for why
   Unicode's own characters will not do. Bravura is already loaded (VexFlow
   engraves the staves with it) and is embedded in an exported page, so this
   comes out right in a PDF too. It sits a little low and runs a little large
   at its natural size, which is what the nudge and the size are for. */
.tempo-note {
  font-family: Bravura, serif;
  font-size: 1.45em;
  line-height: 0;
  vertical-align: -0.06em;
  margin-right: 0.12em;
}
/* The notehead picker: a visible glyph with an invisible native select over
   it. See noteValuePicker in js/chart.js — macOS draws a select's option list
   itself, in the system font, so a music glyph in an <option> is a box. */
.note-pick {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  min-width: 1.4em;
}
/* The face carries .tempo-note when it is a notehead, and that class is
   tuned for sitting inline in a line of text — zero line-height so it does
   not open up the row it is in. Standing on its own in a control it needs
   its height back, or it collapses to nothing and the glyph hangs outside
   its own box. */
.note-pick-face {
  pointer-events: none;
  white-space: nowrap;
  line-height: 1.1;
  display: inline-block;
  min-width: 0.75em;
  text-align: center;
}
.note-pick .tempo-note { line-height: 1.1; vertical-align: baseline; }
.note-pick-select {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  cursor: pointer;
  font: inherit;
  -webkit-appearance: none;
  appearance: none;
}
.note-pick.disabled { opacity: 0.75; }
.note-pick.disabled .note-pick-select { cursor: default; }
.chart-total { grid-column: 2; grid-row: 1 / span 2; font-size: calc(12px * var(--chart-text) * var(--tz, 1)); color: var(--chart-faint); align-self: center; }

/* Build only: the title and meta line are the same text, just editable in
   place — no border or background until you actually reach for one, so an
   idle chart still reads exactly like the printed one. */
input.chart-title, .cm-field {
  border: none; background: none; padding: 1px 3px; margin: -1px -3px;
  font: inherit; color: inherit; letter-spacing: inherit; border-radius: 3px;
}
input.chart-title { width: 100%; }
input.chart-title:hover, .cm-field:hover,
input.chart-title:focus, .cm-field:focus { outline: none; background: rgba(31, 95, 208, 0.08); }
/* The column gap has to clear the -3px each field pulls in on either side
   (see .cm-field above) before it is a gap at all: at 4px the fields'
   hover boxes overlapped and the words themselves were 4px apart, which
   read as "190" and "11/8" jammed together. 10px leaves 4px between the
   boxes and a proper word space between the words. */
.chart-meta-edit { grid-column: 1; display: flex; flex-wrap: wrap; gap: 2px 10px; font-size: calc(13px * var(--tz, 1)); color: var(--chart-faint); }
.cm-field { font-size: calc(13px * var(--tz, 1)); color: var(--chart-faint); }
/* The artist line as tall as its words: an input's 30px minimum, there for
   a finger on a form, made it the height of a button (Adam, 26 Oct 2026:
   "tighten up the artist line"). */
.chart-meta-edit .cm-field { min-height: 0; line-height: 1.3; }
/* A fixed width clipped anything longer than it mid-word — "The Allman
   Brothers Ba" — and the cut ran straight into the tempo marking beside
   it. Sized to what is typed where the browser can do it; the fixed width
   is only the fallback, and wide enough now for an ordinary band name. */
.cm-field.cm-artist { width: 14em; }
@supports (field-sizing: content) {
  .cm-field.cm-artist { width: auto; field-sizing: content; min-width: 5em; max-width: 22em; }
}
.cm-tempo-group { display: inline-flex; align-items: center; gap: 1px; }
.cm-field.cm-tempo-unit { width: auto; padding-right: 0; cursor: pointer; }
.cm-tempo-detect { font-size: calc(11px * var(--tz, 1)); padding: 1px 6px; }
.cm-tempo-eq { margin: 0 2px 0 1px; }
.cm-field.cm-tempo { width: 3em; text-align: center; }
.cm-field.cm-tempo.empty { width: 4.2em; margin: 0 2px; box-shadow: inset 0 0 0 1px rgba(31, 95, 208, 0.45); background: #fff; }
.cm-field.cm-tempo::placeholder { color: rgba(31, 95, 208, 0.7); }

/* Which page this is, on a chart of more than one. In the bottom margin and
   out of the flow, so it takes nothing from the page. */
.chart-frame > .page-no {
  position: absolute;
  right: 14px;
  bottom: 8px;
  font: 600 9px/1 "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #7b818c;
  pointer-events: none;
}

/* What the Apple Pencil drew on the page in Score (js/ink.js): over the whole
   page, in the page's own units, so it sits on the same spot of the paper
   whatever size the page is shown at. Above the chart and the labels laid
   over its bars (z-index 1), and never in the way of a tap. Here rather than
   in app.css because this sheet goes into every exported page. */
.chart-frame > .ink {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  overflow: hidden;
  pointer-events: none;
}

.chart-head-wrap { position: relative; }

/* --- sections ------------------------------------------------------- */

.chart-body {
  display: flex;
  flex-direction: column;
  /* Reading mode may flow the sections into columns: a chart that is tall and
     narrow wastes a landscape screen, and two columns halve its height. */
  column-count: var(--chart-columns, 1);
  column-gap: calc(24px * var(--chart-space));
  column-fill: balance;
}
.chart-body[style*="--chart-columns"] { display: block; }
.chart-body > * { break-inside: avoid; }

.chart-section {
  display: grid;
  grid-template-columns: var(--chart-label-w) minmax(0, 1fr);
  gap: calc(3px * var(--chart-space)) 10px;
  padding: calc(6px * var(--chart-space)) 0;
  border-top: 1px solid var(--chart-line);
  break-inside: avoid;
  transition: transform .12s ease;
}
.chart-section:first-child { border-top: 0; padding-top: 2px; }
/* The bar sits in the margin beside the row rather than inset over its
   first column — the section name starts hard against the section's left
   edge, so an inset bar was drawn straight through the V of VERSE. */
.chart-section.selected { background: #f2f7ff; box-shadow: -3px 0 0 #1f5fd0; }
.chart-section.dragging, .phrase.dragging {
  position: relative;
  z-index: 5;
  transition: none;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
  cursor: grabbing;
}

/* In Build a box, and a section, is taken hold of by itself: a press that
   moves picks it up (pickUpPhrase and pickUpSection in js/chart.js). The
   pointer says so over a box, and over a section once it is selected; over
   a section's blank space before that it is the ordinary arrow (Adam,
   16 Oct: the hand everywhere said nothing about what would move). It goes
   back to a caret over the words that are typed into. */
.phrase.movable, .chart-section.movable.selected { cursor: grab; }
.phrase.movable { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
/* Its fields still take text: Safari on an iPad will not type into a field
   inside something that cannot be selected. */
.phrase.movable input { -webkit-user-select: text; user-select: text; }
.chart-section.movable .phrase:not(.movable) { cursor: auto; }
.chart-section.movable input, .chart-section.movable textarea { cursor: text; }
.chart-section.movable button, .chart-section.movable select { cursor: pointer; }
body.drag-moving, body.drag-moving * { cursor: grabbing !important; -webkit-user-select: none !important; user-select: none !important; }



/* Where a dragged phrase would land: a bar on the side of whichever phrase it
   is closest to, in the gap and over its edge, or a dashed outline around an
   empty section's phrase area when there is nothing there to be closest to.
   The box being dragged is see-through, so the bar shows under it. */
.phrase.drop-before { box-shadow: inset 3px 0 0 #1f5fd0, -3px 0 0 #1f5fd0; }
.phrase.drop-after { box-shadow: inset -3px 0 0 #1f5fd0, 3px 0 0 #1f5fd0; }
.phrase.dragging { opacity: 0.72; }
.chart-section.drop-into-empty .phrases { outline: 2px dashed #1f5fd0; outline-offset: 2px; border-radius: 3px; }

.sec-head { grid-column: 1; }
.sec-name {
  font-size: calc(15px * var(--chart-text) * var(--tz, 1));
  font-weight: 700;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.2;
}
/* A textarea, not an input — a name too long for the column wraps onto a
   second row rather than being clipped where you can't see the rest of it.
   resizeSectionNames (called after every render, see app.js) grows it to
   fit; rows="1" is only ever its starting height. */
textarea.sec-name {
  border: none; background: none; padding: 1px 2px; margin: -1px -2px;
  width: 100%; color: inherit; border-radius: 3px;
  /* A textarea's own default font is a form-control one, not the page's —
     the .sec-name rule above already sets the size/weight/case/spacing that
     matter, so only the family needs pulling back in. */
  font-family: inherit;
  resize: none; overflow: hidden; white-space: pre-wrap; word-break: break-word;
  display: block;
}
textarea.sec-name:hover, textarea.sec-name:focus { outline: none; background: rgba(31, 95, 208, 0.1); }
/* The ▾ beside a name you can edit (sectionlabel.js). Quiet until the name is
   pointed at, but never hidden outright — an iPad has no hover to find it with.
   Aligned to the first line rather than centred, so it stays put when a long
   name wraps to a second one. */
.sec-head .name-field { display: flex; align-items: flex-start; min-width: 0; }
.sec-head .name-field textarea.sec-name { flex: 1 1 auto; min-width: 0; }
.sec-head .name-field .name-field-toggle { margin-top: 1px; }
/* On a touch screen in a narrow chart, where the name sits on a line over the
   boxes (below), its field is as wide as the name, so the ▾ and the count
   come straight after it rather than at the end of an empty column (Adam's
   iPad held sideways, 29 Oct 2026). */
@media (hover: none) and (pointer: coarse) {
  @container chart (max-width: 620px) {
    .sec-head .name-field { flex: 0 1 auto; }
    .sec-head .name-field textarea.sec-name { flex: 0 1 auto; width: auto; field-sizing: content; min-width: 3ch; }
  }
}
.sec-bars {
  font-size: calc(12px * var(--chart-text) * var(--tz, 1));
  color: var(--chart-ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.sec-bars-mismatch { color: var(--chart-warn); }
/* Nothing written yet: the number is the map's, in grey, until the boxes are. */
.sec-bars-unwritten { color: var(--chart-faint); }
/* Build: the count is a field, but reads as the label until it is reached for. */
.sec-bars-edit { white-space: nowrap; }
/* The count is 92px of column wide at most, and "8 bars ⚠ map: 69" is not
   — written on one line it ran on underneath the notation tile, hiding the
   number the warning exists to give you. It gets its own line instead. */
.sec-bars-map { display: block; }
.sec-bars-input {
  width: 2.6em; border: none; background: none; padding: 1px 2px; margin: -1px 0 -1px -2px;
  font: inherit; color: inherit; text-align: right; border-radius: 3px;
}
.sec-bars-input:hover, .sec-bars-input:focus { outline: none; background: rgba(31, 95, 208, 0.1); }
.sec-bars-input::placeholder { color: var(--chart-faint); }

/* A section's own note (sectionNote in js/chart.js): a line or two of the
   drummer's words over the section's boxes, or beside its name where it has
   none. Plain upright text, so it is not taken for a box's label (small and
   faint) or a cue (bold capitals). */
.sec-note {
  margin: 0;
  font-size: calc(14px * var(--chart-text) * var(--tz, 1));
  line-height: 1.3;
  color: var(--chart-ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* Build's field for it: the same words, typed in place, growing a line at a
   time (resizeSectionNames in js/sectionlabel.js). */
textarea.sec-note-field {
  display: block; width: 100%;
  border: none; background: none; padding: 1px 2px; margin: -1px -2px;
  font-family: inherit; resize: none; overflow: hidden; border-radius: 3px;
}
textarea.sec-note-field::placeholder { color: var(--chart-faint); }
textarea.sec-note-field:hover, textarea.sec-note-field:focus { outline: none; background: rgba(31, 95, 208, 0.1); }

/* Score's "Sections" style: the names alone, in order, to learn a song's
   shape by. Each name takes the width it needs rather than the margin
   column's, set large enough to read from the stand, with its note beside
   it. The Size dial and Fill the page scale it as they do any page. */
.chart.sections-only {
  /* How much larger than these sizes the names are drawn to fill the page
     (fitSectionsPage in js/reading.js). */
  --sections-fit: 1;
}
.chart.sections-only .chart-section {
  grid-template-columns: max-content minmax(0, 1fr) auto;
  column-gap: calc(18px * var(--chart-space) * var(--sections-fit));
  align-items: baseline;
  padding: calc(9px * var(--chart-space) * var(--sections-fit)) 0;
}
.chart.sections-only .sec-name {
  font-size: calc(26px * var(--chart-text) * var(--sections-fit) * var(--tz, 1));
  line-height: 1.15;
  white-space: nowrap;
}
.chart.sections-only .sec-note { font-size: calc(15px * var(--chart-text) * var(--sections-fit) * var(--tz, 1)); }
/* Each section's bar count (Adam, 23 Oct 2026): at the right-hand end of its
   line, the numbers one under another down the page, smaller than the name
   and lighter, so the name is still what is read first. The ×N of a
   repeated block is the same size, the two being read together. */
.chart.sections-only .sec-length {
  justify-self: end;
  white-space: nowrap;
  font-size: calc(19px * var(--chart-text) * var(--sections-fit) * var(--tz, 1));
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.chart.sections-only .sec-length-word { font-weight: 400; color: var(--chart-faint); }
.chart.sections-only .rg-times { font-size: calc(19px * var(--chart-text) * var(--sections-fit) * var(--tz, 1)); }

/* A section carried over a page turn (see paginate in reading.js): where it
   is cut it says so, and where it picks up its name is there again with
   "continued" under it. Both are in the margin column, under the name, where
   there is room already — neither costs the page any height. */
.sec-cont-note, .sec-cont-tag {
  font-size: calc(9.5px * var(--chart-text) * var(--tz, 1));
  font-style: italic;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--chart-faint);
  white-space: nowrap;
}
.sec-cont-note { position: absolute; left: 0; bottom: calc(6px * var(--chart-space)); }
.sec-cont-tag { display: block; margin-top: 1px; }

/* What a selected box offers (phraseTools in js/chart.js), and a selected
   section (sectionBlock): a tab joined to the top of its outline at the
   right. Over the box's own top line it would sit on the bar count of a
   one-bar box, and set in that line it made the box wider and taller. Delete
   in red, apart from the rest, as the one that takes something away.

   It shows while the box (or section) is pointed at, or its label is being
   typed, and not otherwise: always up, it sat on the row above for as long as
   the box stayed selected, over the notes there (Adam, 2026-10-07). Moving
   off to read the chart, or to the groove editor, leaves the outline alone.
   A touch screen has no pointer to show it with, so there it stays up. */
.ph-tools {
  position: absolute;
  right: -3px;
  bottom: calc(100% + 3px);
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  background: #1f5fd0;
  border-radius: 6px 6px 6px 0;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.16);
  white-space: nowrap;
  cursor: default;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease, visibility 0s linear 0.12s;
}
/* The few pixels of outline between the tab and its box: crossing them on
   the way up to the tab is not leaving the box. */
.ph-tools::after { content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 5px; }
.phrase:hover > .ph-tools,
.phrase:focus-within > .ph-tools,
.chart-section:hover > .sec-tools,
.chart-section:focus-within > .sec-tools {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.12s ease, visibility 0s;
}
.ph-tools .ph-tool, .ph-tools .ph-align-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  min-height: 0;
  height: 20px;
  padding: 0 7px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: #fff;
  font: 600 11.5px/1 system-ui, -apple-system, sans-serif;
  cursor: pointer;
}
.ph-tools .ph-tool:hover, .ph-tools .ph-align-btn:hover { background: rgba(255, 255, 255, 0.18); }
/* Icons alone, and close set, so the whole tab — the label's three places,
   Duplicate and Delete — is no wider than a one-bar box: with Duplicate a
   word it ran over the box's edge (Adam, 2026-10-07). Delete is set apart by
   its red. */
.ph-tools .ph-dup, .ph-tools .ph-del { width: 20px; padding: 0; }
.ph-tools .ph-del { background: #d93025; }
.ph-tools .ph-del:hover { background: #b3261e; }
.ph-align { display: inline-flex; padding-right: 2px; border-right: 1px solid rgba(255, 255, 255, 0.35); }
.ph-tools .ph-align-btn { width: 17px; padding: 0; opacity: 0.75; }
.ph-tools .ph-align-btn.on { opacity: 1; background: rgba(255, 255, 255, 0.24); }
.ph-align-icon { display: inline-flex; }
/* A section's tab stands on the line along its top. */
.chart-section > .sec-tools { right: 0; bottom: calc(100% - 1px); border-radius: 6px 6px 0 0; }
.chart-section > .sec-tools::after { display: none; }
@media (hover: none) {
  .ph-tools { opacity: 1; visibility: visible; transition: none; }
  .ph-tools .ph-tool, .ph-tools .ph-align-btn { height: 26px; }
  .ph-tools .ph-align-btn { width: 24px; }
  .ph-tools .ph-dup, .ph-tools .ph-del { width: 28px; }
}

/* The way in to a new bar either side of a box (insertHandles in
   js/chart.js): a strip the height of the box, down the gap to the next box
   and a few pixels over each, taking no room. Pointed at anywhere, it shows a
   line down the gap and a "+" on it; a click anywhere on it writes the bar.
   The gap was nobody's before 22 Oct: only an 18px "+" halfway down the
   box's edge could be clicked, shown once the box had been pointed at for a
   moment, so aiming between two boxes put it away (Adam: "only a few
   pixels", "the hover doesn't work perfectly"). The strip after a box lies
   over the strip before the next one, so a gap has one "+", not two a pixel
   apart; the one before a box is reached at the start of a row. The box
   pointed at shows its "+" too, a moment after the pointer settles, so it
   can be found. */
.ph-insert {
  --ins-gap: calc(5px * var(--chart-space));
  --ins-in: 4px;
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 4;
  width: calc(var(--ins-gap) + 2 * var(--ins-in));
  cursor: pointer;
}
/* From the outside of the box's 1px border. */
.ph-insert.after { left: calc(100% + 1px - var(--ins-in)); z-index: 5; }
.ph-insert.before { right: calc(100% + 1px - var(--ins-in)); }
body.drag-moving :is(.ph-insert, .phrase-ghost) { display: none !important; }
/* The line down the gap, and the "+" on it, in the middle of the gap. */
.ph-insert::before {
  content: '';
  position: absolute;
  top: 1px;
  bottom: 1px;
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: #1f5fd0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.1s ease;
}
.ph-insert::before, .ph-insert > .ph-insert-btn { left: calc(var(--ins-in) + var(--ins-gap) / 2); }
.ph-insert:hover::before { opacity: 1; transition-delay: 0.04s; }
.ph-insert-btn, .sec-insert-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 2px solid #fff;
  background: #1f5fd0;
  color: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
  cursor: pointer;
}
.ph-insert-btn {
  position: absolute;
  top: 50%;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -50%) scale(0.6);
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0s linear 0.12s;
}
.phrase:hover > .ph-insert > .ph-insert-btn {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%);
  transition: opacity 0.12s ease 0.15s, transform 0.12s ease 0.15s, visibility 0s linear 0.15s;
}
/* On the strip itself the "+" is up at once and a little bigger: the
   pointer is where the bar would go. */
.phrase > .ph-insert:hover > .ph-insert-btn {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(1.15);
  transition: opacity 0.08s ease 0.04s, transform 0.08s ease 0.04s, visibility 0s linear 0.04s;
}
/* No pointer: they are the selected box's, and there is nothing to point
   at, so only its "+" are there to touch (their touch areas, app.css). */
@media (hover: none) {
  .ph-insert { pointer-events: none; }
  .ph-insert::before { display: none; }
  .phrase.selected > .ph-insert > .ph-insert-btn { pointer-events: auto; opacity: 1; visibility: visible; transform: translate(-50%, -50%); transition: none; }
}

/* After a section's last box, a dotted box (insertHandles): laid over the
   paper after it, the box's height, as wide as the row has room for up to
   44px (--row-room, alignChartStaves), so it never moves anything or takes a
   row of its own; not drawn where a row has less room than a narrow one
   (row-roomy). Shown while the section is pointed at, a moment after the
   pointer settles; on a touch screen, while it or a box in it is the one
   selected. Above the strip after the box, which writes to the same place. */
.phrase-ghost {
  position: absolute;
  top: -1px;
  bottom: -1px;
  left: calc(100% + 1px + 5px * var(--chart-space));
  z-index: 6;
  width: min(44px, var(--row-room, 0px) - 5px * var(--chart-space) - 2px);
  display: none;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border: 1.5px dashed var(--chart-line);
  border-radius: 3px;
  background: #fff;
  color: var(--chart-faint);
  cursor: pointer;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.12s ease, visibility 0s linear 0.12s;
}
.phrase.row-roomy > .phrase-ghost { display: flex; }
.chart-section:hover .phrase-ghost { visibility: visible; opacity: 1; transition: opacity 0.12s ease 0.15s, visibility 0s linear 0.15s; }
:is(.phrase-ghost:hover, .ph-insert.ends-section:hover ~ .phrase-ghost) { border-color: #1f5fd0; border-style: solid; color: #1f5fd0; background: #f2f7ff; }
/* Where it is drawn it is the way in after the box, and the strip there
   lights it rather than showing a "+" of its own beside it. */
.phrase.row-roomy > .ph-insert.ends-section::before,
.phrase.row-roomy > .ph-insert.ends-section > .ph-insert-btn { display: none; }
.phrase-ghost .plus-icon svg { width: 14px; height: 14px; }
@media (hover: none) {
  .chart-section:is(.selected, :has(.phrase.selected)) .phrase-ghost { visibility: visible; opacity: 1; transition: none; }
  /* A finger's width at least, out over the paper after it. */
  .phrase-ghost::after { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: max(100%, calc(44px / var(--chart-zoom, 1))); }
}

/* "+" for a new section (sectionBlock in js/chart.js): in the page's left
   margin, on the line above the section and the line under it, so it covers
   no name. With the section under the pointer, a moment after it settles
   there, as a box's "+" are; not for as long as the section stays selected,
   where they were left standing in the margin (Adam, 2026-10-07). A touch
   screen has no pointer, so there the selected section shows them. Pointed
   at, it says what it does, and the line it would go in on turns blue. */
.sec-insert {
  position: absolute;
  left: calc(-25px * var(--chart-pad));
  z-index: 4;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease, visibility 0s linear 0.12s;
}
.sec-insert.before { top: -10px; }
.sec-insert.after { bottom: -10px; }
.chart-section:hover > .sec-insert {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.12s ease 0.15s, visibility 0s linear 0.15s;
}
@media (hover: none) {
  .chart-section.selected > .sec-insert { opacity: 1; visibility: visible; transition: none; }
}
body.drag-moving .sec-insert { display: none !important; }
/* The blank margin beside a section, part of it for pointing at, so its "+"
   stay up wherever the pointer is out to the left of it, as far as the edge
   of the pane, and in the page's margin on the right (Adam, 2026-10-07).
   Run off to the left, out of the pane, it scrolls nothing; to the right it
   stops at the paper's edge, where it would. Under the "+", over nothing
   else. */
.sec-reach { position: absolute; top: 0; bottom: 0; z-index: 0; cursor: default; }
.sec-reach.left { right: 100%; width: 100vw; }
.sec-reach.right { left: 100%; width: calc(26px * var(--chart-pad)); }
.sec-insert-btn {
  width: max-content;
  gap: 4px;
  height: 20px;
  min-width: 20px;
  padding: 0 4px;
  border-radius: 10px;
  font: 600 11px/1 system-ui, -apple-system, sans-serif;
  white-space: nowrap;
}
.sec-insert-word { display: none; }
.sec-insert-btn:hover { padding: 0 9px 0 6px; }
.sec-insert-btn:hover .sec-insert-word { display: inline; }
.chart-section:has(> .sec-insert.before .sec-insert-btn:hover) { box-shadow: inset 0 2px 0 #1f5fd0; }
.chart-section:has(> .sec-insert.after .sec-insert-btn:hover) { box-shadow: inset 0 -2px 0 #1f5fd0; }

.rg-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  background: var(--accent-soft, #eaf1fd);
  border-radius: 5px;
  padding: 5px 8px;
  margin-bottom: 6px;
  font-size: calc(12px * var(--tz, 1));
  grid-column: 1 / -1;
}
.rg-label { font-weight: 600; }
.rg-times-input { width: 44px; }

.phrases { grid-column: 2; display: flex; flex-direction: column; gap: calc(5px * var(--chart-space)); min-width: 0; }
/* Stretching every box in a row to match the tallest one meant one tall
   phrase pushed blank space into every other box beside it, several times
   over in a busy row. Staves still line up across the row regardless (see
   alignChartStaves in chart.js, which aligns them directly rather than
   relying on the boxes around them being equal height) — only the boxes' own
   bottom edges differ. */
.phrase-run { display: flex; flex-wrap: wrap; gap: calc(5px * var(--chart-space)); align-items: flex-start; }

/* alternate endings */
.phrase-run.ending { display: block; position: relative; padding-left: 8px; width: max-content; max-width: 100%; align-self: flex-start; }
.phrase-run.ending .phrase-run-inner { display: flex; flex-wrap: wrap; gap: 6px; padding-top: 12px; }
.ending-bracket {
  position: absolute;
  inset: 0 0 auto 0;
  border-top: 1.5px solid var(--chart-ink);
  border-left: 1.5px solid var(--chart-ink);
  height: 11px;
}
.ending-bracket span {
  position: absolute;
  top: 1px;
  left: 4px;
  font-size: calc(10px * var(--tz, 1));
  font-weight: 700;
  background: #fff;
  padding: 0 3px;
  line-height: 1;
}

/* --- phrase cells --------------------------------------------------- */

.phrase {
  position: relative;
  border: 1px solid var(--chart-line);
  border-radius: 3px;
  padding: 2px 5px 3px;
  min-width: 48px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: #fff;
  break-inside: avoid;
  transition: transform .12s ease;
}
.phrase.selected { outline: 2px solid #1f5fd0; outline-offset: 1px; }

/* How far through this pass of a multi-bar or repeated phrase playback has
   got — only drawn when there is more than one bar to get through, so a
   phrase that is only ever one bar stays exactly as plain as it always was. */
.phrase.sounding-progress::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  border-radius: 0 0 2px 2px;
  background: #1f5fd0;
  width: calc(var(--play-progress, 0) * 100%);
  transition: width 0.15s linear;
}

.ph-top {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: 4px;
  font-size: calc(10px * var(--chart-text) * var(--tz, 1));
  color: var(--chart-faint);
  line-height: 1;
  min-height: 13px;
}
.ph-side { display: flex; align-items: baseline; gap: 4px; }
.ph-side.right { justify-content: flex-end; }
/* The bar count is the number you scan for, so it sits centred and bold over
   the bar it counts. */
.ph-bars {
  font-weight: 800;
  color: var(--chart-ink);
  font-variant-numeric: tabular-nums;
  font-size: calc(14px * var(--chart-text) * var(--chart-bars, 1) * var(--tz, 1));
  text-align: center;
}
/* Times round, typed on a selected groove or repeat: the × and the number
   together where the bar count stands. */
.ph-bars.ph-times { display: inline-flex; align-items: baseline; justify-content: center; }
.ph-top .ph-times .ph-bars-input { width: 2.8ch; text-align: left; }
/* A pickup is not a numbered bar: said in words, lighter than a bar count. */
.ph-bars.ph-pickup { font-weight: 600; font-style: italic; font-size: calc(11px * var(--chart-text) * var(--tz, 1)); }
/* A marking on the music, same as the groove's label and the feel — it says
   what this bar is in, not how the chart is built, so it does not follow the
   bar numbers it sits beside. */
.ph-sig { font-size: calc(10px * var(--chart-markings) * var(--tz, 1)); font-weight: 700; color: var(--chart-ink); border: 1px solid var(--chart-ink); border-radius: 2px; padding: 0 3px; }
/* Every box's header row as tall as one with a time signature in it: an
   unseen strut of the signature's height, no width, at the start of the
   left side. A box with a time signature had a row taller than the box
   beside it (half a pixel in Safari, the Mac app's engine, at the usual
   sizes; up to 4 px in both browsers at others), so its staff sat lower than
   its neighbour's (Adam: staves side by side line up). Where the signature
   adds no height, as in Chrome at the usual sizes, nothing moves. */
.ph-top > .ph-side:not(.right)::before {
  content: '4';
  visibility: hidden;
  font-size: calc(10px * var(--chart-markings) * var(--tz, 1));
  font-weight: 700;
  border-top: 1px solid transparent;
  border-bottom: 1px solid transparent;
  width: 0;
  overflow: hidden;
  /* The side's gap after it, given back. */
  margin-right: -4px;
}
.ph-times { font-weight: 700; color: var(--chart-ink); }
/* A tempo marking set from the box: bold, in the chart's ink, as a marking
   on the music is. */
.ph-tempo { font-size: calc(11px * var(--chart-markings) * var(--tz, 1)); font-weight: 700; color: var(--chart-ink); white-space: nowrap; }
/* On a line of its own over the top line (tempoLine in chart.js), across the
   whole box and taking no width: a box is as wide as its bars, and a marking
   wider than a one-bar box (every chart's first box carries the chart's
   tempo) would widen it. The line's height is set, and a box beside it on the
   row makes room of the same set height (.tempo-room), so the staves stay
   level whatever the marking's glyph does to the height of its line. */
.ph-top > .ph-tempo-line, .ph-top.tempo-room::before {
  grid-column: 1 / -1;
  height: calc(13px * var(--chart-markings));
}
.ph-top.tempo-room::before { content: ''; }
.ph-tempo-line { display: flex; align-items: flex-end; min-width: 0; }
.ph-tempo { display: inline-block; width: 0; overflow: visible; line-height: 1; }
/* The note's stem stands within the line, under the box's top edge: at the
   size a marking in a sentence has (.tempo-note) it rose through the edge
   (Adam, 26 Oct, Where Is My Husband's first box). */
.ph-tempo .tempo-note { font-size: 1.2em; }

.ph-text-line, .ph-rest, .ph-ref, .ph-repeat {
  font-size: calc(14px * var(--chart-text) * var(--tz, 1));
  line-height: 1.25;
}
.ph-text-line.style-instruction { font-weight: 600; }
.ph-text-line.style-arrangement { font-style: italic; color: #3a3f48; }
.ph-text-line.style-texture { font-style: italic; }
.ph-text-line.style-fill {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: calc(13px * var(--chart-text) * var(--tz, 1));
}
.ph-text-line.style-uncertain { font-weight: 700; }
.ph-rest {
  font-weight: 600;
  letter-spacing: 0.08em;
  color: #5c626d;
  text-transform: uppercase;
  font-size: calc(12px * var(--chart-text) * var(--tz, 1));
}
.ph-repeat { font-size: calc(22px * var(--chart-text) * var(--tz, 1)); font-weight: 600; text-align: center; line-height: 1; padding: 2px 8px; }
.ph-ref { display: flex; align-items: center; gap: 5px; font-weight: 600; }
.ph-ref-arrow { font-size: calc(15px * var(--tz, 1)); }
/* Markings, not notation and not the chart's structural text — they are
   words, but words written on the music rather than around it, so they answer
   to their own dial. (The swing word inside this inherits it.) */
/* One line each for the feel and the label, feel on top. */
/* In the chart's ink, as everything printed is: a label in grey read as a
   hint rather than the player's own words, and faded on a stand (Adam, 23
   Oct 2026: "all black"). The grey sample of a picked groove's name is the
   field's placeholder, still faint. */
.ph-groove-label { font-size: calc(11px * var(--chart-markings) * var(--tz, 1)); color: var(--chart-ink); margin-bottom: 1px; }
/* A label put in the middle of its bar, or at the end of it. */
.ph-label-line.align-center, .ph-label-line.align-center .ph-label-input { text-align: center; }
.ph-label-line.align-right, .ph-label-line.align-right .ph-label-input { text-align: right; }
/* A line kept so a label on one box does not drop the staff beside it, taken
   away again on the finished page in a row where no box writes anything on it
   (see trimSpareLines in chart.js). */
.ph-groove-label.off, .ph-line-spare.off { display: none; }
/* On the finished page a label is put in room its box already has rather than
   on a line of its own (see placeLabels in chart.js): over the top of its own
   staff's frame, where the row has left space to line it up with a taller
   neighbour, or up beside the bar count. */
.ph-groove { position: relative; }
.ph-groove-label.over { position: absolute; top: 0; left: 0; right: 0; z-index: 1; margin: 0; line-height: 1; }
.ph-top .ph-label-up {
  font-size: calc(11px * var(--chart-markings) * var(--tz, 1));
  line-height: 1;
  white-space: nowrap;
}
.ph-top .ph-label-up:not(.ph-swing) { color: var(--chart-ink); }
/* Swing is a performance direction, so it is set like one: italic, in the
   chart's ink rather than the label's grey, and hard to miss. */
.ph-swing {
  font-style: italic;
  font-weight: 600;
  color: var(--chart-ink);
  letter-spacing: 0.01em;
}

.phrase.type-text:has(.style-fill) { border-style: dashed; }
.phrase.type-text:has(.style-uncertain) { border-color: #b3261e; }
.phrase.type-repeat { justify-content: center; }

/* A lyric: words in quotation marks in a label or a line of text, set in
   italic, the marks with them (see lyricRuns in js/chart.js). It is what
   notes and lyrics did, in a label's room: the words are a landmark, found by
   the eye without being read. In black since 23 Oct 2026 (Adam: "all
   black"), no longer the lyric's blue: the italic and the marks say which
   words are sung. */
.lyric { font-style: italic; color: var(--chart-ink); }

/* --- section-group repeats ------------------------------------------ */

/* ×N sits against the bracket, on the left, rather than out at the far
   right edge where it was read as belonging to the last line. */
.repeat-group {
  display: grid;
  grid-template-columns: auto 12px minmax(0, 1fr);
  align-items: stretch;
  gap: 0 6px;
  break-inside: avoid;
}
.rg-bracket {
  /* Three sides, written side by side rather than as `border` with the right
     one taken off afterwards: a shorthand holding a var() that a later line
     overrides cannot be read back out of the stylesheet, which is how the
     export copies these rules — and the bracket was missing from every
     exported page. */
  border-style: solid;
  border-color: var(--chart-ink);
  border-width: 2px 0 2px 2px;
  border-radius: 3px 0 0 3px;
  margin: 6px 0;
}
.rg-body { min-width: 0; }
/* A repeat split by a page turn (paginate in reading.js): the bracket is left
   open at the end that carries on to, or from, the other page. */
.repeat-group.rg-open-below > .rg-bracket { border-bottom: 0; border-bottom-left-radius: 0; margin-bottom: 0; }
.repeat-group.rg-open-above > .rg-bracket { border-top: 0; border-top-left-radius: 0; margin-top: 0; }
.rg-times {
  align-self: center;
  font-size: calc(13px * var(--tz, 1));
  font-weight: 700;
  padding-right: 1px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.rg-times small { font-weight: 400; font-size: calc(10px * var(--tz, 1)); color: var(--chart-faint); }

/* --- groove grids --------------------------------------------------- */

.grid-view { display: flex; flex-direction: column; gap: 1px; }
.grow { display: flex; align-items: center; gap: 4px; }

.glabel {
  flex: 0 0 15px;
  width: 15px;
  font-size: calc(8px * var(--tz, 1));
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--chart-faint, #7b818c);
  text-align: right;
  line-height: 1;
}

.gcells {
  display: grid;
  flex: 1 1 auto;
  border-left: 1.5px solid var(--chart-ink, #000);
  border-right: 1.5px solid var(--chart-ink, #000);
}

.gcell {
  position: relative;
  min-width: 14px;
  height: 19px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-right: 1px solid #f0f1f4;
  font-size: calc(11px * var(--tz, 1));
  line-height: 1;
  color: #000;
  /* room above the note for the accent mark */
  padding-top: 6px;
}
.gcell:last-child { border-right: 0; }
.gcell.beat { border-left: 1px solid #c3c7cf; }
.gcell.barline { border-left: 1.5px solid #000; }

/* ghost notes in brackets, accents under a caret — the way you would write them */
.gcell.w-ghost .glyph { font-size: calc(8px * var(--tz, 1)); color: #5c626d; }
.gcell.w-ghost .glyph::before { content: "("; }
.gcell.w-ghost .glyph::after { content: ")"; }
.gcell.w-accent .glyph { font-weight: 800; }
.gcell.w-accent::after {
  content: ">";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  font-size: calc(9px * var(--tz, 1));
  font-weight: 700;
  line-height: 1;
}

.grid-view.compact .gcell { height: 18px; min-width: 13px; }
.grid-view.editable .gcell { height: 28px; min-width: 20px; font-size: calc(13px * var(--tz, 1)); padding-top: 7px; }
.grid-view.editable .glabel { text-align: left; }
.grid-view.editable .gcells { border-color: #b9bec7; }
.grid-view.editable .gcell.w-accent::after { top: 2px; }

.grid-view.triplet .gcell.beat { border-left-color: #8d939c; }

/* --- narrow screens --------------------------------------------------- */
/* A phone gets a reflow, not a shrink: the section name moves above its
   phrases instead of sitting in a column that would eat a third of the width. */

@container chart (max-width: 620px) {
  .chart { padding: 14px 12px 20px; }
  .chart-title { font-size: calc(21px * var(--chart-title) * var(--tz, 1)); }

  .chart-section {
    grid-template-columns: minmax(0, 1fr);
    gap: 3px;
    padding: 7px 0;
  }
  .sec-head {
    grid-column: 1;
    display: flex;
    align-items: baseline;
    gap: 8px;
  }
  .sec-name { font-size: calc(14px * var(--tz, 1)); }
  .phrases { grid-column: 1; }

  .repeat-group { grid-template-columns: auto 8px minmax(0, 1fr); gap: 0 4px; }

  .phrase { min-width: 48px; }
}

/* Score no longer picks a width of its own: the frame is the width, set in
   device pixels by js/reading.js, and the chart simply fills it. What used
   to be a search across a dozen candidate widths is now a property of the
   page you chose to compose onto. */

/* --- what is on the page at all --------------------------------------- */
/* The artist line and the bar totals are priceless on one chart and clutter
   on the next. Hiding rather than not rendering, so the switch costs a class
   change and never a re-render. */

.chart.hide-meta .chart-meta { display: none; }
.chart.hide-bar-total .chart-total,
.chart.hide-bar-total .sec-bars { display: none; }

/* Where the title goes (titlePlace in js/reading.js). Off, the heading is
   gone, the line under the title and its rule with it, so the chart starts at
   the top of the page. In the margin it is one small line in the page's own
   top margin, out of the flow: the chart starts where it would with no
   heading at all, and the title is still there to say which song this is. */
.chart.title-off .chart-head { display: none; }
.chart.title-margin { position: relative; }
.chart.title-margin .chart-head {
  position: absolute;
  top: calc(5px * var(--chart-pad));
  left: 0;
  right: 0;
  display: flex;
  align-items: baseline;
  gap: 0 10px;
  margin: 0;
  padding: 0;
  padding-left: inherit;
  padding-right: inherit;
  border: 0;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
}
.chart.title-margin .chart-title { font-size: calc(10.5px * var(--chart-title) * var(--tz, 1)); letter-spacing: 0; }
.chart.title-margin .chart-meta { font-size: calc(9.5px * var(--chart-text) * var(--tz, 1)); }
.chart.title-margin .chart-total { margin-left: auto; font-size: calc(9.5px * var(--chart-text) * var(--tz, 1)); }

/* Without their boxes the phrases read as a run of music across the line
   rather than a row of cards. Some charts want the cards; some want the
   music to breathe. The bar count stays either way — it is the number the
   eye scans for. */
.chart.hide-boxes .phrase {
  border-color: transparent;
  background: none;
  padding-left: 0;
  padding-right: 0;
}
.chart.hide-boxes .phrase-run { gap: calc(14px * var(--chart-space)); }

/* --- staff notation --------------------------------------------------- */

.notation { line-height: 0; }
.notation svg { display: block; max-width: 100%; height: auto; }
/* VexFlow paints in absolute black; keep it that way in print. */
.notation svg path, .notation svg rect { fill: currentColor; }
.notation svg text { fill: currentColor; }

/* The bar Play will start from, in a groove several bars long that was
   selected by clicking that bar (markStartBar in chart.js). Named apart from
   the rule above, which would otherwise paint it solid in the page's ink. */
.notation svg rect.start-bar { fill: #1f5fd0; fill-opacity: 0.13; stroke: none; }
.notation svg rect.picked-bars { fill: none; stroke: #1f5fd0; stroke-width: 2px; vector-effect: non-scaling-stroke; }

.phrase.sounding-bar { outline: 2px solid #1f5fd0; outline-offset: 1px; }

/* The note being struck, inside the phrase that is sounding. VexFlow paints
   noteheads, stems and flags as fills and strokes on a group, so colouring the
   group means setting both — a stem is a stroked path, a notehead a filled one.
   A drum notehead is often a thin glyph (an X for a hi-hat), so colour alone
   reads as barely more than a hairline; a glow behind it is what actually
   catches the eye at a glance, the way the eye is meant to use this. */
.sounding-note,
.sounding-note path,
.sounding-note rect,
.sounding-note text { fill: #1f5fd0; stroke: #1f5fd0; stroke-width: 1.6px; }
.sounding-note {
  filter: drop-shadow(0 0 1.5px #1f5fd0) drop-shadow(0 0 4px #1f5fd0);
}

/* A rest inside a groove is lit the same way, by the same class, while the
   music is passing through it (see restSteps in notation.js). A bar that is
   nothing but rest — a Rest phrase, a groove with nothing written in it yet —
   is one mark standing for the whole bar, with no steps to follow, so it is
   lit for as long as its bar is the one sounding. */
.phrase.sounding-bar .rest-mark .vf-stavenote,
.phrase.sounding-bar .rest-mark .vf-stavenote path,
.phrase.sounding-bar .rest-mark .vf-stavenote rect,
.phrase.sounding-bar .rest-mark .vf-stavenote text { fill: #1f5fd0; stroke: #1f5fd0; stroke-width: 1.6px; }
.phrase.sounding-bar .rest-mark .vf-stavenote {
  filter: drop-shadow(0 0 1.5px #1f5fd0) drop-shadow(0 0 4px #1f5fd0);
}

/* The same note, when the groove is drawn as the compact grid instead of
   notation — a plain span, not an SVG group, so it takes the highlight as a
   background rather than a fill. */
.gcell.sounding-note { background: #1f5fd0; color: #fff; }
.gcell.sounding-note .glyph { color: #fff; }

/* --- the drum part ---------------------------------------------------- */

/* Score's other page style (renderNotationPage in notationpage.js): the
   chart written as a drum part, a line of bars at a time. Its sections are
   .chart-section so that Score pours them across pages as it does the
   boxes (paginate in reading.js), but none of a box section's dressing is
   theirs: no name column, no rule between them — the boxed name at the
   start of the line says where a section starts. Each line is drawn to a
   width of its own (see drawLine), so that the size dial's zoom, and the
   notation dial through --chart-notation, grow it as they grow the boxes. */
.notation-page .chart-body { column-count: 1; }
.notation-page .chart-section {
  display: block;
  padding: 0;
  border-top: 0;
  margin-top: calc(12px * var(--chart-space));
}
.notation-page .chart-section:first-child { margin-top: 0; }
.notation-page .chart-section.selected { background: none; box-shadow: none; }
.notation-page .phrases { display: flex; flex-direction: column; gap: calc(3px * var(--chart-space)); }
.notation-page .np-line svg { display: block; height: auto; }
/* Where a section carries on over a page turn: at the foot of the page, by
   the last barline, where it covers nothing. */
.notation-page .sec-cont-note { left: auto; right: 0; bottom: -4px; }
/* Each bar's area, behind its ink (drawLine in notationpage.js): picked by a
   tap in Score, as a box is, and the bar Play starts from tinted as a box
   tints it. While it plays, only its notes light (.sounding-note): no
   outline round the bar, and no tint over the section the recording is in. */
.notation-page .np-line.pickable { cursor: pointer; }
.notation-page rect.np-bar { fill: transparent; stroke: none; }
.notation-page rect.np-bar.selected { fill: #1f5fd0; fill-opacity: 0.13; }
.notation-page .chart-section.playing { background: none; box-shadow: none; }

/* --- writing straight into the chart --------------------------------- */

/* The way in to a section with nothing in it yet (addSlot in js/chart.js):
   a dotted box where its first bar goes, the size of the empty bar a click
   writes (110 x 59 at the chart's own size, its staff through the notation
   dial), so the bar takes its place and nothing under it moves; always
   showing (22 Oct; it was a dashed lozenge a quarter of that size).
   Pointed at, it is drawn as a box about to be written. */
.phrase-add {
  position: relative;
  flex: 0 0 auto;
  font-size: calc(16px * var(--tz, 1));
}
.phrase-add-box {
  box-sizing: border-box;
  width: calc(110px * var(--chart-notation, 1));
  height: calc(16px + 43px * var(--chart-notation, 1));
  border: 1.5px dashed var(--chart-line);
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--chart-faint);
  cursor: pointer;
  user-select: none;
  transition: border-color 0.1s ease, background-color 0.1s ease, color 0.1s ease;
}
.phrase-add-box .plus-icon svg { width: 18px; height: 18px; }
.phrase-add:hover .phrase-add-box { border-color: #1f5fd0; color: #1f5fd0; background: #f2f7ff; }
.plus-icon, .ph-tool-icon { display: inline-flex; align-items: center; justify-content: center; line-height: 0; }

.phrase-add-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 5px;
  background: #fff;
  border: 1px solid var(--chart-line);
  border-radius: 4px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.16);
}
.phrase-add-menu .btn { white-space: nowrap; text-align: left; }

/* Nothing to add on a printed page. */
@media print { .phrase-add, .phrase-ghost, .ph-insert, .sec-insert, .sec-reach, .ph-tools { display: none; } }

/* The groove's label, written in place on the selected box in Build: reads
   as the label it is, with no field drawn round it until it has the caret.
   It is laid over its line, which holds the label as written, unseen: the
   line is then exactly as tall and as wide as it is when the box is not
   selected. A field's own height (30px, from app.css) put the selected box's
   staff below the ones beside it, and a field is as wide as it is whatever it
   holds, so a long label's box narrowed and its row wrapped differently. */
.ph-line-field { position: relative; }
.ph-line-sizer { visibility: hidden; }
.ph-label-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  border: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  font: inherit;
  color: inherit;
  outline: 0;
}
.ph-label-input::placeholder { color: currentColor; opacity: 0.4; }
.ph-label-input:focus { box-shadow: 0 0 0 1px rgba(31, 95, 208, 0.4); border-radius: 2px; }
/* A row with no line for the field has it up in the top line instead, right
   of the bar count, where Score sets a short label (see phraseCell in
   chart.js). It sits on the bar count's baseline, as a label set up there
   does, and takes no width of its own: whatever room that side has left,
   which is all of it unless the box closes a repeat and its ×N is there
   (the field goes before it, so the ×N stays where it was). So the line is
   no taller and the box no wider. */
.ph-side.has-field { gap: 0; }
.ph-field-up {
  position: relative;
  order: -1;
  flex: 1 1 0;
  width: 0;
  font-size: calc(11px * var(--chart-markings) * var(--tz, 1));
  line-height: 1;
  white-space: nowrap;
}
.ph-field-up > .ph-label-input {
  top: -2px;
  bottom: -2px;
  height: auto;
  padding-right: 4px;
  text-align: right;
}

/* The bar count, editable on the selected box. Same size and position as the
   number it replaces, so nothing moves when a box is selected — which means
   undoing what app.css gives every field: 30px of height (36px on a phone)
   and, for a number, 68px of width. The height is set outright, as the
   number's line and the padding the negative margin takes back: left to
   itself a field is never shorter than its font's own line, 3px more than
   the number's, and the staff under it went down by that. */
.ph-top .ph-bars-input {
  width: 3.8ch;
  height: calc(14px * var(--chart-text) + 6px);
  min-height: 0;
  border: 0;
  padding: 3px 0;
  margin: -3px 0;
  background: transparent;
  font: inherit;
  font-size: calc(14px * var(--chart-text) * var(--tz, 1));
  color: inherit;
  text-align: center;
  outline: 0;
  -moz-appearance: textfield;
}
.ph-bars-input::-webkit-outer-spin-button,
.ph-bars-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ph-bars-input:focus { box-shadow: 0 0 0 1px rgba(31, 95, 208, 0.4); border-radius: 2px; }

.chart-section, .phrase { position: relative; }


/* --- measuring ------------------------------------------------------- */
/* Applied for the length of a fit measurement only (see measurePageHeight in
   js/reading.js). What is hidden here is everything Build draws onto the chart
   to make it editable but that never reaches the printed page, so the badge
   reports the page rather than the workspace. */
.chart.measuring .phrase-add,
.chart.measuring .phrase-ghost,
.chart.measuring .rg-bar,
.chart.measuring .sec-tools,
.chart.measuring .ph-tools,
.chart.measuring .ph-insert,
.chart.measuring .sec-insert,
.chart.measuring .sec-reach,
.chart.measuring .add-section { display: none !important; }

/* A run of boxes repeated |: :| ×N (markPhraseRepeats in chart.js): a thick
   and a thin line with two dots inside the first box's left edge, the mirror
   of it inside the last box's right edge, and the number of times round at
   the top of the last box, beside its bar count. */
.phrase.rpt-open { padding-left: 15px; }
.phrase.rpt-close { padding-right: 15px; }
.phrase.rpt-open::before,
.phrase.rpt-close::after {
  content: '';
  position: absolute;
  top: 3px;
  bottom: 3px;
  width: 11px;
  pointer-events: none;
  --rpt-ink: var(--chart-ink);
}
.phrase.rpt-open::before {
  left: 2px;
  background:
    linear-gradient(var(--rpt-ink), var(--rpt-ink)) 0 0 / 3px 100% no-repeat,
    linear-gradient(var(--rpt-ink), var(--rpt-ink)) 5px 0 / 1px 100% no-repeat,
    radial-gradient(circle, var(--rpt-ink) 1.7px, transparent 2.2px) 7px calc(50% - 5px) / 4px 4px no-repeat,
    radial-gradient(circle, var(--rpt-ink) 1.7px, transparent 2.2px) 7px calc(50% + 5px) / 4px 4px no-repeat;
}
.phrase.rpt-close::after {
  right: 2px;
  background:
    linear-gradient(var(--rpt-ink), var(--rpt-ink)) 100% 0 / 3px 100% no-repeat,
    linear-gradient(var(--rpt-ink), var(--rpt-ink)) calc(100% - 5px) 0 / 1px 100% no-repeat,
    radial-gradient(circle, var(--rpt-ink) 1.7px, transparent 2.2px) 0 calc(50% - 5px) / 4px 4px no-repeat,
    radial-gradient(circle, var(--rpt-ink) 1.7px, transparent 2.2px) 0 calc(50% + 5px) / 4px 4px no-repeat;
}
.rpt-times {
  font-weight: 800;
  color: var(--chart-ink);
  font-variant-numeric: tabular-nums;
  font-size: calc(12px * var(--chart-text) * var(--tz, 1));
  white-space: nowrap;
}

/* The highlighter: one colour, as on a printed chart, kept to the box's
   paper so the ink on it reads as it did. */
.phrase.highlight { background: #fff1a6; }

/* --- joined boxes (joins.js) ---------------------------------------------------
   A joined groove is a box to each bar, each as wide as a bar on its own,
   drawn as one box: no gap, no border and no padding where two of its bars
   meet, and their staves run on into each other (`joined` in notation.js).
   Where the row runs out the group goes on onto the next (Adam, 22 Oct): the
   box is left open there, its edge dashed on both rows, which is how a
   chart says "carries on" (markJoinBreaks in chart.js). */
.phrase-run > .phrase.join-part:not(.join-start) {
  margin-left: calc(-5px * var(--chart-space));
  border-left-width: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  padding-left: 0;
}
.phrase-run > .phrase.join-part:not(.join-end) {
  border-right-width: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  padding-right: 0;
}
/* The first bar's top line copied onto the others, unseen (echoOf in
   chart.js), so every bar's staff starts at the same height. */
.phrase.join-part > .ph-top.ph-echo { visibility: hidden; pointer-events: none; }
/* Each bar is as wide as a box on its own (spaceJoins in chart.js): the room
   its open sides lost is padding round its drawing, which the staff runs on
   into (runStaffOn in notation.js), cut off at the bar's own edge. */
.phrase.join-part .notation[data-staff-top] { overflow: clip; }
.phrase.join-part .notation[data-staff-top] svg { overflow: visible; }
/* The end of a row before a joined groove that starts a row of its own
   rather than run out of row partway through (keepJoinsWhole in chart.js):
   a line of no height, its gap taken back so the rows sit as they would. */
.phrase-run > .join-wrap { flex-basis: 100%; height: 0; margin-top: calc(-5px * var(--chart-space)); }
.chart.hide-boxes .phrase-run > .join-wrap { margin-top: calc(-14px * var(--chart-space)); }
.phrase-run > .phrase.join-part.join-row-start { margin-left: 0; border-left: 1px dashed var(--chart-line); }
.phrase-run > .phrase.join-part.join-row-end { border-right: 1px dashed var(--chart-line); }
/* Selected or sounding, the group is outlined as one box: top and bottom on
   every bar, the ends only at its ends. */
.phrase.join-part.selected { outline: none; box-shadow: 0 -2px 0 0 #1f5fd0, 0 2px 0 0 #1f5fd0; }
.phrase.join-part.join-start.selected,
.phrase.join-part.join-row-start.selected { box-shadow: 0 -2px 0 0 #1f5fd0, 0 2px 0 0 #1f5fd0, -2px 0 0 0 #1f5fd0; }
.phrase.join-part.join-end.selected,
.phrase.join-part.join-row-end.selected { box-shadow: 0 -2px 0 0 #1f5fd0, 0 2px 0 0 #1f5fd0, 2px 0 0 0 #1f5fd0; }
/* Some bars of a joined groove picked (Practice): one box round those bars,
   its ends where the run starts and stops (showSelectionOnPage). */
.phrase.join-part.selected.pick-l { box-shadow: 0 -2px 0 0 #1f5fd0, 0 2px 0 0 #1f5fd0, -2px 0 0 0 #1f5fd0; }
.phrase.join-part.selected.pick-r { box-shadow: 0 -2px 0 0 #1f5fd0, 0 2px 0 0 #1f5fd0, 2px 0 0 0 #1f5fd0; }
.phrase.join-part.selected.pick-l.pick-r { box-shadow: 0 -2px 0 0 #1f5fd0, 0 2px 0 0 #1f5fd0, -2px 0 0 0 #1f5fd0, 2px 0 0 0 #1f5fd0; }
/* The bar playing inside what is picked: the picked box is the one box drawn
   round it (Adam, 30 Oct: "one box would be better"); its notes still light. */
.phrase.join-part.selected.sounding-bar { outline: none; }
/* A joined bar is drawn under the next one (they overlap to close the gap):
   one outlined, picked or playing, comes to the top, or its right-hand line
   is hidden under its neighbour (Adam, 30 Oct: "only draws 3/4 lines"). */
.phrase.join-part:is(.selected, .sounding-bar) { z-index: 1; }
.phrase.join-part.start-bar { background: #e2ebfa; }
/* The group's count, drawn on its first bar and moved over the middle of the
   group (markJoinBreaks): above the bars it is moved over, whose paper would
   otherwise cover it. */
.phrase.join-start > .ph-top > .ph-bars { position: relative; z-index: 2; }
