/* ============================================================
   Ostinato Builder 2.0 — Eagle View Music

   The palette and the musical colour language are shared with
   Rhythm Poetry 2.0 on purpose: a student who has learned that
   orange means "a sound starts here" should not have to learn
   it again in a second app.
   ============================================================ */

:root {
  /* ----------------------------------------------------------------
     Deliberately not Rhythm Poetry's palette. That app is warm cream
     paper and burnt orange — a page you write on. This one is cool
     slate and indigo — a stage you build on. The two sit side by side
     in the same folder and were, until this, pixel-identical in colour,
     which made them easy to mistake for one another at a glance.

     The warm wood-and-brass instrument artwork is the other reason for
     a cool ground: it has nothing to compete with here.
     ---------------------------------------------------------------- */

  /* ground & ink */
  --paper:        #F4F7FC;
  --paper-2:      #E1E8F4;
  --surface:      #FFFFFF;
  --ink:          #1B2340;
  --ink-2:        #4C5573;
  --ink-3:        #7C8399;
  --hairline:     #DCE2EF;
  --hairline-2:   #C2CADD;

  /* what the notation is drawn in */
  --notation:     #1B2340;

  /* Musical meaning. The three sounding states run round the cool side
     of the wheel — violet to azure to teal — so a sound carrying on
     reads as the same event continuing rather than a new one, and
     silence is the one that has almost no colour left in it. */
  --sound:        #5B4BD6;   /* a note begins here         */
  --sound-soft:   #E3DFFB;
  --hold:         #2E90D9;   /* the sound is still ringing */
  --hold-16:      #2FB9A4;   /* held across sixteenths     */
  --rest:         #C3CADA;   /* silence                    */

  /* Lines: each line of the score on a ground of its own, in turn, so an
     eye coming back from the end of one line finds the next by its
     colour. Pale enough that the notes read as they do on paper; the
     edge is the same hue, strong enough to find at a glance. No violet:
     that is the colour of a sound starting, and of what lights as it
     plays. */
  --line-1:       #E4EEFB;   /* sky  */
  --line-1-edge:  #5B9BE0;
  --line-2:       #DFF3EC;   /* mint */
  --line-2-edge:  #3FAE90;
  --line-3:       #F8EEDF;   /* sand */
  --line-3-edge:  #D99B45;
  --line-4:       #F6E7EF;   /* rose */
  --line-4-edge:  #D06A98;

  --accent:       #5B4BD6;
  --accent-soft:  #EAE7FD;
  --accent-deep:  #4A3BC0;   /* the pressed / hovered accent */
  --good:         #1E8E7E;
  --danger:       #D6365C;
  --danger-soft:  #FCE7EC;

  /* form */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --shadow-1: 0 1px 2px rgba(27,35,64,.06), 0 2px 8px rgba(27,35,64,.06);
  --shadow-2: 0 4px 12px rgba(27,35,64,.10), 0 12px 32px rgba(27,35,64,.12);
  --shadow-3: 0 12px 28px rgba(27,35,64,.16), 0 32px 64px rgba(27,35,64,.18);

  --topbar-h: 56px;
  --toolbar-h: 76px;

  --font: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-round: 'Baloo 2', 'Nunito', system-ui, sans-serif;
  --font-signature: 'Noto Serif', Georgia, 'Times New Roman', serif;

  /* notation base sizes — the grid is scaled by JS from these */
  --dot: 22px;
  --note-h: 62px;
  --head-w: 142px;           /* the instrument column on the left */
  --instrument-size: 104px;  /* the picture that labels a track */
  --track-name-size: 17px;
  --mini-btn: 34px;          /* mute, on the picture's bottom corner */
  --corner-btn: 26px;        /* remove, on its top corner */
  --text-scale: 1;           /* syllables and words alone: View → Text size */

  /* Set from script.js, which needs the same numbers to work out how
     wide a beat has to be before the browser lays it out. */
  --gutter-l: 22px;
  --gutter-r: 10px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overscroll-behavior: none;
  /* A board has no cursor, and the core gesture here — tap the same dot
     twice to turn a note on and then off again — is indistinguishable
     from a double-tap. `manipulation` keeps panning and pinch-zoom but
     drops double-tap-to-zoom, so the second tap lands on the dot instead
     of zooming the page out from under the class. */
  touch-action: manipulation;
}

body {
  font-family: var(--font);
  background: var(--paper);
  color: var(--ink);
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  /* Not transparent: on a board this tint is the only acknowledgement a
     tap gets before the :active styles land, and an unacknowledged tap
     reads as a tap that didn't work. */
  -webkit-tap-highlight-color: var(--accent-soft);
}

button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; padding: 0; }
svg { display: block; }
/* inline icons are stroked line art; the notation opts out below */
svg[viewBox] { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }


/* ============================================================
   TOP BAR
   ============================================================ */

.topbar {
  flex: 0 0 auto;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
  z-index: 40;
}

/* The right-hand cluster is a row of its own. Without this the toggle and
   the chip were plain inline buttons in a block, lined up on a text
   baseline — which sat the taller chip lower than the toggle. */
.topbar-right {
  display: flex;
  align-items: stretch;      /* the toggle takes the chip's height, whatever it is */
  gap: 8px;
  min-width: 0;
}

/* Back to the Eagle View Music hub, as in Rhythm Poetry. Hidden when the
   app is framed, where there is no hub to go back to. */
.topbar-left { display: flex; align-items: center; gap: 6px; min-width: 0; }
.home-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  padding: 7px 12px 7px 8px;
  border-radius: 999px;
  color: var(--ink-2);
  text-decoration: none;
  font-size: 14px;
  font-weight: 700;
  transition: background .15s, color .15s;
}
.home-link svg { flex: none; }
.home-link:hover,
.home-link:active { background: var(--paper-2); color: var(--ink); }
html.in-iframe .home-link { display: none; }

.app-mark {
  font-family: var(--font-round);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--ink);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 46vw;
  padding: 8px 12px 8px 14px;
  border-radius: 999px;
  background: var(--paper-2);
  border: 1.5px solid var(--hairline);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  transition: background .15s, border-color .15s;
}
.chip:hover,
.chip:active { background: var(--accent-soft); border-color: var(--accent); }
/* --- auto-save ---
   On, it is quiet: this is the ordinary state and should not shout.
   Off, it is the one thing on the bar that is not normal, so it says so
   in the warning ink and keeps a dashed edge, like the sandbox chip. */
.autosave-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  min-height: 38px;
  padding: 0 11px;
  border-radius: 999px;
  border: 1.5px solid transparent;
  background: var(--paper-2);
  font-size: 12px;
  font-weight: 800;
  color: var(--ink-3);
  transition: background .15s, color .15s, border-color .15s;
}
.autosave-toggle[hidden] { display: none; }
.autosave-toggle:hover,
.autosave-toggle:active { background: var(--accent-soft); color: var(--accent); }
.autosave-icon { flex: none; }

/* The Clear pill beside the chip, shown only in the sandbox. Quiet until
   reached for, then red, because what it does cannot be undone. */
.sandbox-clear {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  min-height: 38px;
  padding: 0 11px;
  border-radius: 999px;
  background: var(--paper-2);
  font-size: 12px;
  font-weight: 800;
  color: var(--ink-3);
  transition: background .15s, color .15s;
}
.sandbox-clear[hidden] { display: none; }
.sandbox-clear svg { flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.sandbox-clear:hover,
.sandbox-clear:active { background: var(--danger-soft); color: var(--danger); }
@media (max-width: 640px) {
  .sandbox-clear { padding: 0 9px; }
  .sandbox-clear-label { display: none; }
}

.autosave-toggle.is-off {
  background: transparent;
  border-style: dashed;
  border-color: var(--danger);
  color: var(--danger);
}
.autosave-toggle.is-off:hover,
.autosave-toggle.is-off:active { background: var(--danger-soft); color: var(--danger); }
/* A shared ostinato is never saved into, so the toggle is not a warning
   but the way to keep your changes: Save my copy. */
.autosave-toggle.is-off.is-shared {
  border-style: solid;
  border-color: var(--accent);
  color: var(--accent);
}
.autosave-toggle.is-off.is-shared:hover,
.autosave-toggle.is-off.is-shared:active { background: var(--accent-soft); color: var(--accent); }

.chip-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.1; text-align: left; }
.chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* where the work is going: Library, Lesson or Sandbox */
.chip-kicker {
  font-size: 9.5px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}
.chip-kicker[hidden] { display: none; }
/* the sandbox reads as unfiled: a dashed edge and no fill */
.chip.is-sandbox {
  background: transparent;
  border-style: dashed;
  border-color: var(--accent);
  min-height: 42px;
}
.chip.is-sandbox .chip-label { color: var(--accent); }
.chip svg { flex: none; color: var(--ink-3); }


/* ============================================================
   STAGE
   ============================================================ */

.stage {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 18px 0 8px;
  background:
    linear-gradient(var(--paper), var(--paper)) padding-box,
    var(--paper);
}

#grid-fit { padding: 0 18px 12px; }

#grid {
  width: max-content;
  transform-origin: top left;
  will-change: transform;
  transition: transform .22s cubic-bezier(.25,.9,.35,1);
}
@media (prefers-reduced-motion: reduce) { #grid { transition: none; } }

.empty-note {
  margin: 40px auto;
  max-width: 340px;
  text-align: center;
  color: var(--ink-3);
  font-size: 15px;
  line-height: 1.5;
}


/* ============================================================
   THE BEAT RULER
   ------------------------------------------------------------
   Bar numbers and beat counts across the top. It sits on the
   same column widths as every track, so the numbers stay over
   the beats they name however the tracks are subdivided.
   ============================================================ */

.ruler {
  display: flex;
  align-items: flex-end;
  margin-bottom: 12px;   /* the first track's chains sit in here */
  height: 22px;
}
.ruler-head { flex: 0 0 var(--head-w); }
.ruler-body { overflow-x: clip; overflow-y: visible; }
.ruler-inner { position: relative; display: flex; align-items: flex-end; width: max-content; }

.ruler-measure { display: flex; position: relative; }
.ruler-measure-number {
  position: absolute;
  top: -2px;
  left: 4px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  color: var(--ink-3);
  pointer-events: none;
}
.ruler-beat {
  display: grid;
  place-items: end center;
  /* boxed exactly like the dots row below it — same gutters, same
     transparent border, same 3px between beats — so a beat number sits
     over the slots it counts rather than over the pill's left cap */
  box-sizing: border-box;
  margin: 0 3px;
  border: 1.5px solid transparent;
  padding-left: var(--gutter-l);
  padding-right: var(--gutter-r);
  padding-bottom: 2px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-3);
  border-bottom: 2px solid transparent;
  transition: color .12s, border-color .12s;
}
.ruler-beat.playing { color: var(--sound); border-bottom-color: var(--sound); }
.ruler-gap { flex: 0 0 var(--divider-w, 14px); }


/* ============================================================
   A TRACK
   ============================================================ */

.track {
  display: flex;
  align-items: stretch;
  margin-bottom: 16px;
}

.track.muted .track-body { opacity: .42; }

/* --- the instrument, on the left ---
   The picture is the row's label, so it is drawn large enough to be read
   at a glance, and the things you can do to a track hang off it rather
   than queueing underneath: mute rides the picture's bottom corner like a
   badge on an app icon, and remove (top corner) and the order arrows
   (left edge) come up over it while the pointer is on it. That leaves
   the name alone on the line below, centred under the picture and close
   to it.

   Sat on the floor of the row (justify-content: flex-end) so the block
   lines up with the notation rather than with the dots above it.

   The gap only has to clear the mute badge, which hangs 6px below the
   picture's corner: a name wider than the space beside the badge would
   otherwise run into it. */
.track-head {
  flex: 0 0 var(--head-w);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  padding-right: 10px;
}

/* holds the picture and the badge that overhangs it — a button cannot be
   nested in a button, so the two are siblings and this positions them */
.instrument-slot {
  position: relative;
  flex: none;
  width: var(--instrument-size);
  height: var(--instrument-size);
}

.instrument-btn {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1.5px solid var(--hairline);
  transition: border-color .14s, transform .12s, box-shadow .14s;
}
.instrument-btn:hover,
.instrument-btn:active {
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: var(--shadow-1);
}
.instrument-btn img {
  width: 100%;
  height: 100%;
  padding: 6px;
  object-fit: contain;
  filter: drop-shadow(0 2px 0 rgba(0,0,0,.12));
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
}
.track.muted .instrument-btn img { filter: grayscale(1) opacity(.5); }

/* --- mute, on the picture's corner --- */
.mute-badge {
  position: absolute;
  right: -6px;
  bottom: -6px;
  border-radius: 50%;
  color: var(--ink-2);
  background: var(--surface);
  border: 1.5px solid var(--hairline-2);
  box-shadow: var(--shadow-1);
}
.mute-badge:hover,
.mute-badge:active { background: var(--paper-2); border-color: var(--ink-3); color: var(--ink); }
.mute-badge.muted { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }

/* --- the name, under the picture --- */
.track-name {
  max-width: 100%;
  font-size: var(--track-name-size);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: .01em;
  color: var(--ink-2);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.mini-btn {
  flex: none;
  width: var(--mini-btn);
  height: var(--mini-btn);
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ink-3);
  background: transparent;
  transition: background .13s, color .13s, border-color .13s;
}
.mini-btn svg { width: calc(var(--mini-btn) * 0.56); height: calc(var(--mini-btn) * 0.56); }
.mini-btn:hover,
.mini-btn:active { background: var(--paper-2); color: var(--ink); }
.mini-btn.muted { color: var(--danger); }

/* --- remove and reorder, over the picture ---
   Both are laid over the picture rather than set beside it, so bringing
   them up cannot nudge the name, re-measure the head column or re-fit
   the score — the picture simply gains an × and a pair of arrows.

   Faded out rather than visibility: hidden, because a hidden element
   cannot take focus: keyboard users would never reach them, and the
   :focus-within that is supposed to bring them up could never fire.
   pointer-events does the job visibility was doing, and a Tab onto the
   picture reveals them the same way the pointer does. */
.remove-badge,
.track-move {
  position: absolute;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity .14s;
}
.instrument-slot:hover .remove-badge,
.instrument-slot:hover .track-move,
.remove-badge:focus-visible,
.track-move:focus-within { opacity: 1; pointer-events: auto; }
/* on its own: a browser that does not know :has() would drop the whole
   list above along with it */
.instrument-slot:has(:focus-visible) .remove-badge,
.instrument-slot:has(:focus-visible) .track-move { opacity: 1; pointer-events: auto; }


/* the × on the top corner — the mute badge's counterpart, a size down,
   because it is the one to be sure about */
.remove-badge {
  top: -8px;
  right: -8px;
  width: var(--corner-btn);
  height: var(--corner-btn);
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ink-2);
  background: var(--surface);
  border: 1.5px solid var(--ink-3);
  box-shadow: var(--shadow-1);
  transition: opacity .14s, background .13s, color .13s, border-color .13s;
}
.remove-badge svg { width: 60%; height: 60%; stroke-width: 2.6; }
.remove-badge:hover,
.remove-badge:active { background: var(--danger); border-color: var(--danger); color: #fff; }

/* the arrows, an up over a down in one pill, floating on the middle of
   the picture's left edge */
.track-move {
  left: -13px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  padding: 2px;
  gap: 1px;
  border-radius: 999px;
  background: var(--surface);
  border: 1.5px solid var(--hairline-2);
  box-shadow: var(--shadow-1);
}

.move-btn {
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ink-2);
  background: transparent;
  transition: background .13s, color .13s;
}
.move-btn:hover,
.move-btn:active { background: var(--accent-soft); color: var(--accent); }
.move-btn:disabled { opacity: .25; pointer-events: none; }
.move-btn svg { width: 15px; height: 15px; stroke-width: 2.4; }

/* A board has no pointer to hover with, so there both simply live on the
   picture — the same bargain the rest of this app's hover affordances
   make under (hover: none). */
@media (hover: none) {
  .remove-badge,
  .track-move { opacity: .85; pointer-events: auto; }
}

/* --- adding an instrument, under the last one ---
   A row of its own with only an instrument column: a dashed outline as
   wide as the pictures above it, like the space the next one will fill.
   Kept short, because every pixel of it is height the fit has to find.
   After Lines' last line it needs the gap a track would have left. */
.add-track-row { display: flex; }
.system + .add-track-row { margin-top: 16px; }
.add-track-cell {
  flex: 0 0 var(--head-w);
  display: flex;
  justify-content: center;
  padding-right: 10px;
}
.add-track-btn {
  width: var(--instrument-size);
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  border-radius: var(--r-md);
  border: 2px dashed var(--hairline-2);
  color: var(--ink-3);
  font-size: 15px;
  font-weight: 800;
  transition: background .14s, border-color .14s, color .14s;
}
.add-track-btn svg { stroke-width: 2.6; }
.add-track-btn:hover,
.add-track-btn:active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* --- the beats, on the right ---
   .track-body is the window a page is seen through; .body-inner holds the
   whole piece and is slid sideways to bring a page into view. Scrolling
   across is the same machinery with the window opened to full width and
   nothing slid. */
.track-body { overflow-x: clip; overflow-y: visible; }
/* positioned so a bar's offsetLeft is measured from the strip it sits in,
   not from whatever happens to be the nearest positioned ancestor further
   up — page offsets are read straight off these numbers */
.body-inner { position: relative; display: flex; align-items: flex-start; width: max-content; }

.measure { display: flex; align-items: flex-start; }


/* ============================================================
   A SYSTEM  (the Lines layout)
   ------------------------------------------------------------
   One block of bars, with the whole instrument column beside it,
   and the next block underneath — a printed score's line, and the
   reason a tall band of instruments can be read on a wide screen
   without being shrunk to fit one long row.

   A reader has to see at a glance where one line ends and the
   next begins, and a gap alone does that badly: the tracks are
   16px apart, so anything near that reads as just another
   instrument. So each line sits on a coloured ground of its
   own, taking the four colours in turn (--line-1 … --line-4),
   and the eye coming back from the end of a line finds the
   next one by its colour.

   Only when there is more than one line (#grid.lined): a score
   that is all one line has nothing to tell apart, and the frame
   would only cost it room. The panel is as wide as the widest
   line, so a short last line still reads as a line. Its edge is
   a border, not a shadow, so the saved picture carries it too.
   LINE_FRAME_X / _Y and SYSTEM_GAP in script.js must match the
   padding, edge and gap here.
   ============================================================ */
/* the bottom margin of a system's last track, inside the block */
.system > .track:last-child { margin-bottom: 0; }

#grid.lined > .system {
  padding: 10px 14px 14px 12px;
  border-left: 6px solid var(--line-edge);
  border-radius: var(--r-lg);
  background: var(--line-bg);
}
#grid.lined > .system + .system { margin-top: 12px; }
#grid.lined > .system.tone-0 { --line-bg: var(--line-1); --line-edge: var(--line-1-edge); }
#grid.lined > .system.tone-1 { --line-bg: var(--line-2); --line-edge: var(--line-2-edge); }
#grid.lined > .system.tone-2 { --line-bg: var(--line-3); --line-edge: var(--line-3-edge); }
#grid.lined > .system.tone-3 { --line-bg: var(--line-4); --line-edge: var(--line-4-edge); }
/* Add stays under the instrument column, past the panel's edge */
#grid.lined > .add-track-row { padding-left: 18px; }


/* ============================================================
   ONE BEAT
   ------------------------------------------------------------
   The dots and the notation sit on the same column grid, so a
   note is always directly above the dot that triggers it.

   --beat-w is shared by every track for a given beat, so the
   grid stays vertically aligned even when one instrument is
   playing sixteenths against another's quarters. Inside that
   width each track lays out its own number of slots.
   ============================================================ */

.group {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 3px;
  --slots: 2;
  --beat-w: 110px;
}

.group .dots,
.group .notes-box {
  box-sizing: border-box;
  padding-left: var(--gutter-l);
  padding-right: var(--gutter-r);
  border: 1.5px solid transparent;
  width: var(--beat-w);
}

/* --- the tappable dots --- */
.dots {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--slots), 1fr);
  align-items: center;
  justify-items: center;
  min-height: calc(var(--dot) + 14px);
  padding-top: 7px;
  padding-bottom: 7px;
  border-color: var(--hairline-2);
  border-radius: 999px;
  background: var(--surface);
  transition: border-color .18s, background .18s;
}

/* Scoped to the dots row on purpose. The notation engine names its
   augmentation dots `class="dot"` too, and an unscoped rule here reaches
   inside the SVG: `transform: scale()` on hover fights the transform
   attribute that places the glyph, and the dot jumps off its notehead. */
.dots .dot {
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: var(--rest);
  cursor: pointer;
  flex: none;
  transition: background .13s, transform .13s;
}
.dots .dot:hover,
.dots .dot:active { transform: scale(1.14); }
.dots .dot.active      { background: var(--sound); }
.dots .dot.sustain     { background: var(--hold); }
.dots .dot.sustain-16  { background: var(--hold-16); }

/* --- + / − subdivision --- */
.subdivision-controls {
  position: absolute;
  left: 4px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.subdivision-btn {
  width: 16px;
  height: 14px;
  display: grid;
  place-items: center;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
  color: var(--ink-3);
  background: transparent;
  transition: background .13s, color .13s;
}
.subdivision-btn:hover,
.subdivision-btn:active { background: var(--paper-2); color: var(--ink); }
.subdivision-btn.active { background: var(--sound); color: #fff; }
.subdivision-btn:disabled { opacity: .22; cursor: default; }
.subdivision-btn:disabled:hover,
.subdivision-btn:disabled:active { background: transparent; color: var(--ink-3); }

/* --- the printed notation --- */
.notes-box {
  position: relative;
  height: calc(var(--note-h) + var(--note-extra, 0px));
  margin-top: 6px;
  overflow: visible;
}

.rhythm-svg {
  position: absolute;
  top: 0;
  left: 0;
  overflow: visible;
  pointer-events: none;
  user-select: none;
  z-index: 2;
}
/* the global icon rule above strokes anything with a viewBox — the
   notation is filled artwork, so it opts out explicitly */
.rhythm-svg path,
.rhythm-svg rect { fill: var(--notation, #1B2340); stroke: none; }
.rhythm-svg .bracket {
  fill: none;
  stroke: var(--notation, #1B2340);
  stroke-linecap: butt;
  stroke-linejoin: miter;
}


/* ============================================================
   BAR LINES
   ============================================================ */

.measure-divider,
.final-divider {
  flex: 0 0 auto;
  align-self: stretch;
  margin: 0 5px;
  width: 4px;
  position: relative;
}
.measure-divider::after {
  content: '';
  position: absolute;
  inset: 2px auto 0 1px;
  width: 2px;
  background: var(--hairline-2);
  border-radius: 2px;
}
.final-divider::after,
.final-divider::before {
  content: '';
  position: absolute;
  top: 2px;
  bottom: 0;
  background: var(--ink);
  border-radius: 2px;
}
.final-divider::before { left: 0; width: 2px; }
.final-divider::after  { left: 5px; width: 4px; }
.final-divider { width: 9px; }


/* ============================================================
   TOOLBAR
   ============================================================ */

.toolbar {
  flex: 0 0 auto;
  min-height: var(--toolbar-h);
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border-top: 1px solid var(--hairline);
  box-shadow: 0 -2px 14px rgba(27,35,64,.05);
  z-index: 30;
}

.toolbar-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
}

.play-btn {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: #fff;
  box-shadow: var(--shadow-2);
  transition: transform .12s, background .14s;
}
.play-btn:hover,
.play-btn:active { transform: scale(1.05); }
.play-btn:active { transform: scale(.96); }
.play-btn.playing { background: var(--danger); }
.play-glyph { font-size: 21px; line-height: 1; margin-left: 2px; }
.play-btn.playing .play-glyph { margin-left: 0; }

.tool-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 44px;
  padding: 0 15px;
  border-radius: var(--r-md);
  background: var(--paper-2);
  color: var(--ink);
  font-size: 15px;
  font-weight: 700;
  transition: background .14s, color .14s, box-shadow .14s;
}
.tool-btn:hover,
.tool-btn:active { background: var(--accent-soft); }
.tool-btn.active { background: var(--accent); color: #fff; }
.tool-btn svg { flex: none; }

.bpm-value { font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }
.bpm-unit { font-size: 12px; font-weight: 700; color: var(--ink-3); letter-spacing: .04em; }
.tool-btn.active .bpm-unit { color: rgba(255,255,255,.8); }

/* Present ends the row, as it does in Rhythm Poetry and the Music Stand:
   named, in the accent, and set off from the tools before it by a rule. */
.present-btn.tool-btn {
  position: relative;
  margin-left: 11px;
  background: var(--accent-soft);
  color: var(--accent);
}
.present-btn.tool-btn:hover,
.present-btn.tool-btn:active { background: var(--accent); color: #fff; }
.present-btn::before {
  content: '';
  position: absolute;
  left: -11px;
  top: 8px;
  bottom: 8px;
  border-left: 1px solid var(--hairline);
}

/* --- time signature ---
   Two numerals stacked the way they sit on a staff: no fraction bar and
   nearly touching. A button's line box does not inherit the parent's
   line-height, so the tight setting has to be stated on .ts-num itself —
   left to `normal` the numerals drift a third of their height apart. */
.time-sig {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  width: 44px;
  height: 54px;
  border-radius: var(--r-md);
  background: var(--paper-2);
  transition: background .13s;
}
.time-sig:hover,
.time-sig:active { background: var(--hairline); }
.ts-num {
  font-family: var(--font-signature);
  font-size: 22px;
  font-weight: 900;
  line-height: .78;
  color: var(--ink);
  font-variant-numeric: lining-nums tabular-nums;
  padding: 0 1px;
  border-radius: 4px;
  transition: background .13s, color .13s, transform .1s;
}
.ts-num:hover,
.ts-num:active { background: var(--accent-soft); color: var(--accent); transform: scale(1.08); }
.ts-num:active { transform: scale(.94); }

/* --- measure stepper --- */
.stepper {
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 4px;
  border-radius: var(--r-md);
  background: var(--paper-2);
}
.step-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 17px;
  font-weight: 800;
  color: var(--ink-2);
  transition: background .13s, color .13s;
}
.step-btn:hover,
.step-btn:active { background: var(--surface); color: var(--accent); }
.step-btn:disabled { opacity: .3; cursor: default; }
.step-btn:disabled:hover,
.step-btn:disabled:active { background: transparent; color: var(--ink-2); }
.step-body { display: flex; align-items: baseline; gap: 4px; padding: 0 3px; min-width: 56px; justify-content: center; }
.step-value { font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }
.step-unit { font-size: 12px; font-weight: 700; color: var(--ink-3); }

/* tempo typed in place */
.inline-input {
  width: 66px;
  height: 34px;
  text-align: center;
  font-family: inherit;
  font-size: 17px;
  font-weight: 800;
  color: var(--ink);
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: var(--r-sm);
  outline: none;
}


/* ============================================================
   INSTRUMENT PICKER
   ============================================================ */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(27,35,64,.36);
  backdrop-filter: blur(2px);
  z-index: 200;
}
.sheet-backdrop.open { display: flex; }

.sheet {
  width: min(560px, 100%);
  max-height: min(80vh, 640px);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 20px 12px;
  border-bottom: 1px solid var(--hairline);
}
.sheet-head h2 {
  margin: 0;
  font-family: var(--font-round);
  font-size: 19px;
  font-weight: 700;
}
.sheet-close {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ink-3);
  transition: background .13s, color .13s;
}
.sheet-close:hover,
.sheet-close:active { background: var(--paper-2); color: var(--ink); }

.instrument-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 10px;
  padding: 16px 20px 22px;
  overflow-y: auto;
}

.instrument-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px 9px;
  border-radius: var(--r-md);
  background: var(--paper);
  border: 2px solid transparent;
  transition: border-color .14s, background .14s, transform .12s;
}
.instrument-card:hover,
.instrument-card:active { transform: translateY(-2px); background: var(--accent-soft); }
.instrument-card.selected { border-color: var(--accent); background: var(--accent-soft); }
.instrument-card img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  filter: drop-shadow(0 3px 0 rgba(0,0,0,.13));
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
}
.instrument-card span {
  font-size: 12px;
  font-weight: 800;
  color: var(--ink-2);
  text-align: center;
  line-height: 1.2;
}


/* ============================================================
   JOINING TWO BEATS
   ------------------------------------------------------------
   The chain sits on the seam between two beats. Every track has
   its own, because a bass drum may hold a whole note while the
   shaker above it plays sixteenths.
   ============================================================ */

.beat-link-btn {
  position: absolute;
  /* Clear of the pill, not on top of it: sitting level with the pill would
     put the chain straight over the next beat's +/− controls. */
  right: -10px;
  top: -15px;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--hairline-2);
  color: var(--ink-3);
  z-index: 3;
  transition: background .14s, color .14s, border-color .14s, opacity .14s;
}
.beat-link-btn svg { width: 12px; height: 12px; }
.beat-link-btn:hover,
.beat-link-btn:active { border-color: var(--hold); color: var(--hold); }
.beat-link-btn.active { background: var(--hold); border-color: var(--hold); color: #fff; }

/* A joined run is one pill-worth of rhythm spread over several beats;
   the warm border says so without adding another line to read. */
.dots.linked { border-color: var(--hold); }

.dots.dots-hidden { opacity: 0; pointer-events: none; }

/* --- EASY ---
   The dots stay laid out underneath, unseen, because the notation is
   placed on their centres; the coloured circles sit over the whole pill.
   The circle that is lit is full colour with a ring round it, the others
   wait at a third strength. A beat held by a note that began earlier is
   washed in that note's colour. */
.dots.easy .dot { visibility: hidden; pointer-events: none; }
.easy-row {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;              /* EASY_GAP */
  padding: 0 8px;        /* EASY_PAD */
  border-radius: inherit;
}
.easy-row.held { background: color-mix(in srgb, var(--held, var(--hold)) 16%, transparent); }
.easy-choice {
  flex: none;
  width: var(--dot);
  height: var(--dot);
  padding: 0;
  border-radius: 50%;
  background: var(--c);
  opacity: .3;
  cursor: pointer;
  transition: opacity .13s, transform .13s, box-shadow .13s;
}
.easy-choice:hover { opacity: .6; transform: scale(1.12); }
.easy-choice:active { transform: scale(.94); }
.easy-choice.on {
  opacity: 1;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--c);
}

/* ---- the beat-dot button ----
   Drawn as Rhythm Poetry draws it: a rounded square holding the dot row,
   tinted while the dots are up. */
.dots-btn {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  flex: none;
  border-radius: var(--r-md);
  background: var(--paper-2);
  color: var(--ink-3);
  transition: background .13s, color .13s;
}
.dots-btn svg { width: 24px; height: 24px; }
.dots-btn:hover,
.dots-btn:active { background: var(--hairline); }
.dots-btn.active { background: var(--accent-soft); color: var(--accent); }

/* In EASY it reads EASY, a letter in each circle's colour. */
.dots-btn .easy-label {
  display: none;
  font-family: var(--font-round);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .06em;
}
.dots-btn.easy { width: auto; min-width: 44px; padding: 0 10px; }
.dots-btn.easy svg { display: none; }
.dots-btn.easy .easy-label { display: inline; }
.dots-btn.easy,
.dots-btn.easy:hover,
.dots-btn.easy:active { background: #1F2433; }
.dots-btn .easy-label b { font-weight: inherit; }
.dots-btn .easy-label b:nth-child(1) { color: #FF6B6F; }
.dots-btn .easy-label b:nth-child(2) { color: #FFB938; }
.dots-btn .easy-label b:nth-child(3) { color: #4CD37F; }
.dots-btn .easy-label b:nth-child(4) { color: #5DB2F5; }

/* Layout settings: the list of circles, one row each. */
.easy-set-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}
.easy-set-head .lesson-sub { margin: 0; }
.easy-count { display: inline-flex; align-items: center; gap: 4px; }
.easy-count-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--paper-2);
  font-size: 17px;
  font-weight: 800;
  color: var(--ink-2);
}
.easy-count-btn:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent); }
.easy-count-btn:disabled { opacity: .35; cursor: default; }
.easy-count-value { min-width: 72px; text-align: center; font-size: 13px; font-weight: 700; color: var(--ink-2); }
.easy-set { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.easy-set-row {
  display: grid;
  grid-template-columns: 22px 96px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
}
.easy-swatch { width: 22px; height: 22px; border-radius: 50%; background: var(--c); }
.easy-pic { display: flex; align-items: flex-end; justify-content: center; height: 48px; }
.easy-pic .rhythm-svg { position: static; overflow: visible; }
.easy-select { width: 100%; min-width: 0; max-width: none; }

/* --- the beat that is sounding ---
   Two lights, each with its own switch in View (body.light-beats,
   body.light-notes): the box of dots above the beat, and a box round its
   notes. Both are drawn the same way — the sounding colour's wash inside,
   its line round the edge, thickened by a ring outside the border so
   nothing shifts when it comes on — and Rhythm Poetry draws its two
   exactly so, in its own colour. */
.notes-box.playing { transition: background .12s, border-color .12s, box-shadow .12s; }
body.light-beats .dots.playing,
body.light-notes .notes-box.playing {
  border-color: var(--sound);
  background: var(--sound-soft);
  box-shadow: 0 0 0 1.5px var(--sound);
}
body.light-notes .notes-box.playing { border-radius: var(--r-md); }
/* Bar and beat numbers are two switches on one row. Either can go on its
   own and the row keeps its height, so the tracks do not jump; only with
   both off does the row itself go. A beat number that is hidden takes its
   playing underline with it — a line under nothing reads as a glitch. */
body.hide-bar-numbers .ruler-measure-number { display: none; }
body.hide-beat-numbers .ruler-beat,
body.hide-beat-numbers .ruler-beat.playing { color: transparent; border-bottom-color: transparent; }
body.hide-bar-numbers.hide-beat-numbers .ruler { display: none; }
/* A line with something spoken under it keeps its picture level with
   the notation, not with the words: the head is padded by exactly the
   words row (its 4px margin and its height), which grows with Text size. */
.track.speaks .track-head { padding-bottom: calc(4px + 24px * var(--text-scale)); }

/* ============================================================
   BEAT REPEAT (SIMILE MARK) QUICK-COPY

   An invisible hit area sitting on the quarter rest. Hover it and the
   rest fades out while a simile mark fades in on the same spot, in the
   same ink — the beat reads as what a click would make it. Move away
   and the rest comes back. Nothing is drawn until the mouse is there,
   so a page of rests stays a page of rests.
   ============================================================ */

.beat-repeat-btn {
  position: absolute;
  display: grid;
  place-items: center;
  /* a fallback only: the real size is set in staff spaces once the grid
     has been measured, so the hit area tracks the note size */
  width: 34px;
  height: 38px;
  padding: 0;
  background: none;
  border: 0;
  transform: translate(-50%, -50%);
  z-index: 6;
  cursor: pointer;
}

/* the mark itself — hidden until the rest under it is hovered */
.simile-svg {
  display: block;
  overflow: visible;
  opacity: 0;
  transform: scale(.88);
  transition: opacity .13s ease, transform .13s cubic-bezier(.25,.9,.35,1);
}
/* the global icon rule strokes anything with a viewBox; this is filled
   artwork in the notation's own ink, like the notes beside it */
.simile-svg path,
.simile-svg circle { fill: var(--notation, #1B2340); stroke: none; }

.beat-repeat-btn:hover .simile-svg,
.beat-repeat-btn:active .simile-svg,
.beat-repeat-btn:focus-visible .simile-svg,
.measure-repeat-btn:hover .simile-svg,
.measure-repeat-btn:active .simile-svg,
.measure-repeat-btn:focus-visible .simile-svg {
  opacity: 1;
  transform: scale(1);
}
.beat-repeat-btn:active .simile-svg,
.measure-repeat-btn:active .simile-svg { transform: scale(.94); }

/* while the mark is showing, the rest steps aside for it */
.beat-repeat-btn:hover ~ .rhythm-svg .rest,
.beat-repeat-btn:active ~ .rhythm-svg .rest,
.beat-repeat-btn:focus-visible ~ .rhythm-svg .rest {
  opacity: 0;
  transition: opacity .13s ease;
}
.rhythm-svg .rest { transition: opacity .13s ease; }

/* ---- the same mark a bar at a time ----
   On the bar line between a bar that sounds and a bar that is silent,
   which is where a copyist writes it. Invisible until the line is
   hovered; while it shows, the empty bar's rests step aside for it. */
.measure-repeat-btn {
  position: absolute;
  display: grid;
  place-items: center;
  width: 38px;          /* a fallback: the real size is set in staff spaces */
  height: 42px;
  padding: 0;
  background: none;
  border: 0;
  transform: translate(-50%, -50%);
  z-index: 7;
  cursor: pointer;
}
.measure.repeat-preview .rhythm-svg .rest { opacity: 0; }

/* ---- how long the piece is, on the closing bar line ----
   + adds a bar, × takes the last one away. One pair for the whole score,
   drawn on the top line only, because the length is the piece's and not
   one instrument's. Faint until the score is hovered, so the end of the
   music stays the end of the music until someone reaches for it — and
   fully lit from the moment either is touched or focused.
   Sized in staff spaces by placeEndMarks(); the numbers here are the
   fallback for the moment before the grid has been measured. */
.measure-add-btn,
.measure-cut-btn {
  position: absolute;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  line-height: 1;
  font-weight: 800;
  font-family: inherit;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--hairline-2);
  color: var(--ink-3);
  transform: translate(-50%, -50%);
  opacity: .32;
  z-index: 7;
  cursor: pointer;
  transition: opacity .15s, background .15s, color .15s, border-color .15s, transform .15s;
}
#grid:hover .measure-add-btn,
#grid:hover .measure-cut-btn { opacity: .8; }
.measure-add-btn:hover,
.measure-add-btn:focus-visible,
.measure-cut-btn:hover,
.measure-cut-btn:focus-visible { opacity: 1; }
.measure-add-btn:hover,
.measure-add-btn:focus-visible {
  background: var(--accent); border-color: var(--accent); color: #fff;
  transform: translate(-50%, -50%) scale(1.1);
}
.measure-cut-btn:hover,
.measure-cut-btn:focus-visible {
  background: var(--danger); border-color: var(--danger); color: #fff;
  transform: translate(-50%, -50%) scale(1.1);
}
/* The bar that is about to go, going — on every line at once. It is the
   last `.measure` in each row, which is the child before the closing bar
   line, the one element that always follows it. */
#grid.cutting .body-inner > .measure:nth-last-child(2) { opacity: .26; }
.body-inner > .measure { transition: opacity .13s ease; }

/* A finger has no hover to show it the pair with, so on a touch screen
   they simply stand there. */
@media (pointer: coarse) {
  .measure-add-btn,
  .measure-cut-btn { opacity: .8; }
}

.beat-repeat-btn:disabled,
.beat-repeat-btn.disabled {
  display: none;
}


/* ============================================================
   SYLLABLES
   ============================================================ */

.words {
  box-sizing: border-box;
  padding-left: var(--gutter-l);
  padding-right: var(--gutter-r);
  border: 1.5px solid transparent;
  width: var(--beat-w);
  display: grid;
  grid-template-columns: repeat(var(--slots), 1fr);
  justify-items: center;
  align-items: flex-start;
  margin-top: 4px;
  min-height: calc(24px * var(--text-scale));
}

.word-container { display: inline-flex; justify-content: center; min-width: 0; }

.word {
  font-family: var(--font);
  font-size: calc(17px * var(--text-scale));
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  text-align: center;
  white-space: nowrap;
}
.word.rest { color: var(--ink-3); opacity: .5; font-weight: 400; }

/* --- one box per note ---
   A word of the line's own spans from its note to the next one in the
   beat (buildSpeech). Inside, a box one slot wide centres a short word
   on its notehead; a longer one grows out of it to the right, the way
   words are set under a song. min-width: 0 on the container keeps a long
   word from widening the grid's columns and pulling the words off their
   dots — the beat is widened for it instead. */
.words .word-container {
  position: relative;
  justify-self: stretch;
  justify-content: flex-start;
}
.word-box {
  flex: none;
  min-width: calc(100% / var(--span, 1));
  text-align: center;
  white-space: nowrap;
  border-radius: 6px;
  transition: background .13s;
}

/* Double-click a syllable to change it (EDITING A SYLLABLE in script.js). */
.words.editable { user-select: none; -webkit-user-select: none; }
.words.editable [data-note] { cursor: text; }
.words.editable [data-note]:hover .word-box { background: var(--accent-soft); }
body.present-mode .words.editable [data-note] { cursor: default; }
body.present-mode .words.editable [data-note]:hover .word-box { background: transparent; }
.word-container.editing .word-box { visibility: hidden; }
.word-input {
  position: absolute;
  top: -3px;
  left: 0;
  z-index: 5;
  min-width: calc(100% / var(--span, 1));
  box-sizing: border-box;
  padding: 1px 5px;
  font-family: var(--font);
  font-size: calc(17px * var(--text-scale));
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--accent);
  border-radius: 6px;
  box-shadow: 0 0 0 3px var(--accent-soft);
  outline: none;
}


/* ============================================================
   POPOVER
   ============================================================ */

.popover {
  position: fixed;
  display: none;
  flex-direction: column;
  gap: 14px;
  width: min(300px, calc(100vw - 24px));
  max-height: calc(100vh - 24px);
  overflow-y: auto;
  padding: 16px;
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  z-index: 150;
}
.popover.open { display: flex; }

.popover-section { display: flex; flex-direction: column; gap: 4px; }

.popover-title {
  margin: 0 0 4px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  text-align: left;
  transition: background .13s;
}
.switch-row:hover,
.switch-row:active { background: var(--paper-2); }

.switch {
  flex: none;
  width: 38px;
  height: 22px;
  border-radius: 999px;
  background: var(--rest);
  position: relative;
  transition: background .16s;
}
.switch::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(27,35,64,.28);
  transition: transform .16s;
}
.switch-row.active .switch { background: var(--accent); }
.switch-row.active .switch::after { transform: translateX(16px); }
/* some of a group on — see .vocab-head.partial */
.switch-row.partial .switch { background: var(--hairline-2); }
.switch-row.partial .switch::after { transform: translateX(8px); }

/* --- Sound: one switch per instrument ---
   Hung under the switch for all of them, indented behind a rule so the
   list reads as that switch's own. A picture to know each one by, since
   that is how the score names them. */
.sound-voices {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 0 0 4px 14px;
  padding-left: 8px;
  border-left: 2px solid var(--hairline);
}
.sound-voices:empty { display: none; }
.voice-switch {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 4px 10px 4px 6px;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-2);
  text-align: left;
  transition: background .13s;
}
.voice-switch:hover,
.voice-switch:active { background: var(--paper-2); }
.voice-switch img {
  flex: none;
  width: 30px;
  height: 30px;
  object-fit: contain;
  transition: opacity .16s, filter .16s;
}
.voice-switch:not(.active) img { opacity: .4; }
.voice-switch.muted img { filter: grayscale(1); }
.voice-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.voice-switch .switch { flex: none; }

/* its volume, on a line of its own under the switch, lined up with the name */
.voice-row { display: flex; flex-direction: column; }
.voice-level {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px 6px 46px;
}
.voice-level .slider { height: 4px; }
.voice-level .slider-readout { min-width: 38px; font-size: 12px; cursor: default; }
.voice-row.silent .voice-level { opacity: .45; }
.voice-switch.active .switch { background: var(--accent); }
.voice-switch.active .switch::after { transform: translateX(16px); }

.popover-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 4px 10px 2px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-2);
}
.popover-field[hidden] { display: none; }

.tool-select {
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  padding: 9px 10px;
  border-radius: var(--r-sm);
  border: 1.5px solid var(--hairline-2);
  background: var(--paper);
  cursor: pointer;
}
.tool-select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }


/* ============================================================
   SHEETS: LIBRARY, NAMING, BACKUP
   ============================================================ */

.sheet-narrow { width: min(420px, 100%); }

.now-editing {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 9px;
  margin: 14px 20px 0;
  padding: 12px 15px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
}
.now-editing-label {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.now-editing-title {
  flex: 1;
  min-width: 100px;
  font-family: var(--font-round);
  font-size: 17px;
  font-weight: 700;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.now-editing-note {
  flex-basis: 100%;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.4;
}
.now-editing-note[hidden] { display: none; }

.sheet-body { padding: 16px 20px; display: flex; flex-direction: column; gap: 10px; }
.sheet-scroll { overflow-y: auto; gap: 0; padding: 0; }
.sheet-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 4px 20px 18px;
}
.sheet-sub { margin: 0; font-size: 14px; color: var(--ink-2); line-height: 1.45; }

.sheet-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--hairline);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 15px;
  border-radius: var(--r-md);
  background: var(--paper-2);
  color: var(--ink);
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
  transition: background .14s, color .14s, opacity .14s;
}
.btn:hover,
.btn:active { background: var(--accent-soft); }
.btn:disabled { opacity: .45; cursor: default; }
.btn:disabled:hover,
.btn:disabled:active { background: var(--paper-2); }
.btn svg { flex: none; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover,
.btn-primary:active { background: var(--accent-deep); }
.btn-danger { background: var(--danger-soft); color: var(--danger); }
.btn-danger:hover,
.btn-danger:active { background: var(--danger); color: #fff; }

.text-field {
  width: 100%;
  height: 42px;
  padding: 0 13px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--hairline-2);
  border-radius: var(--r-sm);
  outline: none;
}
.text-field:focus { border-color: var(--accent); }
.text-field[readonly] { color: var(--ink-2); }

.field-label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.status-msg { font-size: 13px; font-weight: 700; color: var(--ink-2); min-height: 17px; }
.status-msg.error { color: var(--danger); }
.status-msg.good { color: var(--good); }

/* --- save picture options modal --- */
.picture-pages-group { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.picture-pages-group[hidden],
.picture-scroll-note[hidden] { display: none !important; }
.picture-page-options { display: flex; flex-direction: column; gap: 8px; }
.radio-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 11px 13px;
  border: 1.5px solid var(--hairline-2);
  border-radius: var(--r-md);
  background: var(--surface);
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.radio-card:hover { background: var(--paper); }
.radio-card:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.radio-card input[type="radio"] {
  margin-top: 2px;
  accent-color: var(--accent);
  width: 17px;
  height: 17px;
  flex: none;
  cursor: pointer;
}
.radio-card-body { display: flex; flex-direction: column; gap: 2px; }
.radio-card-body strong { font-size: 14px; font-weight: 700; color: var(--ink); }
.radio-card-body .sub-desc { font-size: 12px; color: var(--ink-3); line-height: 1.35; }
.picture-scroll-note {
  margin: 4px 0 0;
  padding: 10px 13px;
  border-radius: var(--r-sm);
  background: var(--paper-2);
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.4;
}

/* --- the song list ---
   Drawn as Rhythm Poetry and the Music Stand draw theirs: each group under
   an icon and a rule, each ostinato a card of its own, and Open a real
   button in the accent. */
.library-list {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 16px 20px 20px;
  overflow-y: auto;
}

.library-group-head {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 9px;
}
.library-group-title {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}
.library-group-rule { flex: 1; height: 1px; background: var(--hairline); }
.library-group-head svg { flex: none; width: 16px; height: 16px; color: var(--accent); }
/* the sandbox: scratch pages, drawn unfiled; a book from the Teacher
   Library (lib/evm-shelf.js) in plain ink */
.library-group-sandbox .library-group-title,
.library-group-sandbox .library-group-head svg { color: var(--ink-2); }
.library-group-book .library-group-title,
.library-group-book .library-group-head svg { color: var(--ink); }

.library-song {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 13px;
  margin-bottom: 6px;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1.5px solid var(--hairline);
  transition: border-color .14s, box-shadow .14s;
}
.library-song:hover,
.library-song:active { border-color: var(--hairline-2); }
.library-song.current { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* the sandbox row: scratch work, drawn unfiled */
.sandbox-song { border-style: dashed; background: var(--paper); }
.sandbox-song.current { border-style: solid; }

.meter-badge {
  flex: none;
  min-width: 42px;
  padding: 4px 7px;
  border-radius: 7px;
  background: var(--paper-2);
  font-family: var(--font-round);
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  color: var(--ink-2);
}

.library-song-title {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* An ostinato shared with you: it stays as it was sent. */
.shared-tag {
  display: inline-block;
  margin-left: 7px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1.5px solid var(--hairline-2);
  font-size: 11px; font-weight: 800; letter-spacing: .02em;
  color: var(--ink-3);
  vertical-align: 2px;
}
.library-song-sub {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-3);
  white-space: nowrap;
}

.library-song-actions { display: flex; align-items: center; gap: 5px; flex: none; }

.lib-btn {
  height: 33px;
  padding: 0 12px;
  border-radius: 9px;
  background: var(--paper-2);
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-2);
  transition: background .13s, color .13s;
}
.lib-btn:hover,
.lib-btn:active { background: var(--hairline); color: var(--ink); }
.lib-btn.open-btn { background: var(--accent); color: #fff; }
.lib-btn.open-btn:hover,
.lib-btn.open-btn:active { background: var(--accent-deep); color: #fff; }
.lib-btn.is-current { background: transparent; color: var(--accent); cursor: default; }
.lib-btn.is-current:hover,
.lib-btn.is-current:active { background: transparent; }
.lib-btn.danger { width: 33px; padding: 0; font-size: 15px; }
.lib-btn.danger:hover,
.lib-btn.danger:active { background: var(--danger); color: #fff; }

.library-empty {
  padding: 10px 8px 14px;
  font-size: 14px;
  color: var(--ink-3);
}

/* --- Share & backup ---
   The cards Rhythm Poetry and the Music Stand use: white on the paper,
   each titled with its icon. Scoped, because .card-title and .card-desc
   also dress the Layout settings and lesson sheets. */
.backup-cards { padding: 16px 20px 4px; background: var(--paper); }
.backup-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 17px;
  margin-bottom: 14px;
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1px solid var(--hairline);
}
.backup-card .card-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 7px;
  font-family: var(--font-round);
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
}
.backup-card .card-title svg { flex: none; width: 17px; height: 17px; color: var(--accent); }
.backup-card .card-desc { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; }
.backup-card .card-head-row { width: 100%; align-items: baseline; }
.backup-card .inline-check { margin-bottom: 12px; }
.backup-card .inline-check em { font-style: normal; color: var(--ink-3); }
.backup-card .export-list { margin-bottom: 12px; }
.backup-card .share-row { margin-top: 12px; }
.backup-card .status-msg:not(:empty) { margin-top: 8px; }
.backup-card-danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 18%, var(--surface)); }
.backup-card-danger .card-title,
.backup-card-danger .card-title svg { color: var(--danger); }
/* On the pink card the pink button needs its edge, as the other two draw it. */
.backup-card-danger .btn-danger { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--danger) 32%, var(--surface)); }

.filename-row { display: flex; align-items: center; gap: 8px; width: 100%; margin-bottom: 12px; }
.filename-row .text-field { flex: 1; }
.filename-ext { font-size: 14px; font-weight: 700; color: var(--ink-3); }

.text-tools { display: flex; gap: 2px; }
.text-tool {
  padding: 3px 8px;
  border-radius: 6px;
  font-size: 12px;
  font-weight: 800;
  color: var(--ink-2);
  transition: background .13s, color .13s;
}
.text-tool:hover,
.text-tool:active { background: var(--paper-2); color: var(--accent); }

.share-row { display: flex; gap: 6px; width: 100%; }
.share-row .text-field { flex: 1; min-width: 0; }

.export-list {
  width: 100%;
  max-height: 190px;
  overflow-y: auto;
  border: 1.5px solid var(--hairline);
  border-radius: var(--r-sm);
  padding: 4px;
}
.export-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 7px;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.export-row:hover,
.export-row:active { background: var(--paper); }
.export-row input { accent-color: var(--accent); width: 16px; height: 16px; flex: none; }
.export-row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- the picture ---
   html2canvas serialises the page without its stylesheet transforms, so
   the capture is taken unscaled and the controls are hidden rather than
   filtered out one by one. */
body.capturing .subdivision-controls,
body.capturing .beat-link-btn,
body.capturing .beat-repeat-btn,
body.capturing .track-move,
body.capturing .remove-badge,
body.capturing .mini-btn,
body.capturing .easy-row { visibility: hidden; }
/* a picture taken in EASY shows the ordinary dots, which say the rhythm */
body.capturing .dots.easy .dot { visibility: visible; }
body.capturing .instrument-btn { border-color: transparent; }
/* a saved picture is a score, not a snapshot of playback: whichever beat
   happens to be sounding when the button is pressed must not be lit in it */
body.capturing .dots.playing { background: var(--surface); border-color: var(--hairline-2); box-shadow: none; }
body.capturing .notes-box.playing { background: none; border-color: transparent; box-shadow: none; }
body.capturing .ruler-beat.playing { color: var(--ink-3); border-bottom-color: transparent; }

/* The picture is the whole piece on white, not the window on to it.
   #grid is normally a page-sized peephole: the bar strips are clipped to
   one page and slid sideways under it. For the shot the clipping comes
   off and the strips go back to their true width, so an eight-bar
   ostinato prints as eight bars rather than as whichever page was open.

   The padding is not decoration. Flush against the edge of the canvas the
   last bar line and the descender of the bottom instrument's name are
   shaved off by a pixel or two, which is exactly how the old picture
   looked wrong. */
body.capturing #grid {
  background: #fff;
  padding: 22px 26px;
}
body.capturing.capturing-full .track-body,
body.capturing.capturing-full .ruler-body {
  overflow: visible !important;
  width: auto !important;
}
body.capturing.capturing-full .body-inner,
body.capturing.capturing-full .ruler-inner { transform: none !important; }

body.capturing.capturing-page .track-body,
body.capturing.capturing-page .ruler-body {
  overflow: hidden !important;
}

/* html2canvas must not catch a colour or a size halfway through a fade */
body.capturing *,
body.capturing *::before,
body.capturing *::after {
  transition: none !important;
  animation: none !important;
}


/* ============================================================
   PRESENT MODE

   The same score, with everything that is not the score taken away:
   no top bar, no toolbar, no tappable furniture on the beats. The stage
   keeps its own machinery, so a piece that pages still pages and a piece
   that scrolls still scrolls — present mode shows the layout the user
   built rather than inventing one — it simply has the whole window to
   do it in, and render() is called on the way in and out so the fit is
   worked out against the size the score actually has.
   ============================================================ */

.present-bar {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 70;
  display: none;
  align-items: center;
  gap: 12px;
  padding: 9px 9px 9px 10px;
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--shadow-3);
  opacity: .32;
  transition: opacity .25s;
}
.present-bar:hover,
.present-bar:active,
.present-bar:focus-within { opacity: 1; }

body.present-mode .present-bar { display: flex; }
body.present-mode .topbar,
body.present-mode .toolbar { display: none; }
body.present-mode .stage { padding: 26px 0 8px; }

/* the furniture of editing: hidden rather than removed, so nothing the
   engraver measured moves when the mode is turned on */
body.present-mode .subdivision-controls,
body.present-mode .beat-link-btn,
body.present-mode .beat-repeat-btn,
body.present-mode .measure-repeat-btn,
body.present-mode .measure-add-btn,
body.present-mode .measure-cut-btn,
body.present-mode .track-move,
body.present-mode .remove-badge { visibility: hidden; }
/* gone rather than hidden: it is a row of its own, and a blank one
   would cost the score its height */
body.present-mode .add-track-row,
body.capturing .add-track-row { display: none; }

/* nothing on the page should look pressable that is not */
body.present-mode .instrument-btn:hover,
body.present-mode .instrument-btn:active {
  border-color: var(--hairline);
  transform: none;
  box-shadow: none;
}

.present-play {
  width: 50px;
  height: 50px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 19px;
  line-height: 1;
  transition: background .18s;
}
.present-play.playing { background: var(--danger); }

.present-exit {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 42px;
  padding: 0 16px 0 13px;
  border-radius: 999px;
  background: var(--paper-2);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-2);
  transition: background .14s, color .14s;
}
.present-exit svg { width: 17px; height: 17px; }
.present-exit:hover,
.present-exit:active { background: var(--ink); color: #fff; }


/* ============================================================
   PAGING AND SIZE
   ============================================================ */

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  margin: 6px auto 2px;
  padding: 4px;
  width: max-content;
  border-radius: 999px;
  background: var(--surface);
  border: 1.5px solid var(--hairline);
  box-shadow: var(--shadow-1);
}
.pager[hidden] { display: none; }

.pager-btn {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ink-2);
  transition: background .13s, color .13s, opacity .13s;
}
.pager-btn:hover,
.pager-btn:active { background: var(--paper-2); color: var(--accent); }
.pager-btn:disabled { opacity: .28; cursor: default; }
.pager-btn:disabled:hover,
.pager-btn:disabled:active { background: transparent; color: var(--ink-2); }

.pager-label {
  padding: 0 8px;
  font-size: 13px;
  font-weight: 800;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.seg {
  display: flex;
  gap: 3px;
  padding: 3px;
  margin: 0 10px 4px;
  border-radius: 999px;
  background: var(--paper-2);
}
.seg-btn {
  flex: 1;
  padding: 8px 10px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 800;
  color: var(--ink-2);
  transition: background .14s, color .14s;
}
.seg-btn:hover,
.seg-btn:active { color: var(--ink); }
.seg-btn.active { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-1); }

.chip-row { display: flex; gap: 4px; flex-wrap: wrap; }
.chip-btn {
  min-width: 34px;
  padding: 7px 10px;
  border-radius: 999px;
  background: var(--paper-2);
  font-size: 13px;
  font-weight: 800;
  color: var(--ink-2);
  transition: background .13s, color .13s;
}
.chip-btn:hover,
.chip-btn:active { background: var(--accent-soft); color: var(--accent); }
.chip-btn.active { background: var(--accent); color: #fff; }
.chip-btn:disabled { opacity: .3; cursor: default; }
.chip-btn:disabled:hover,
.chip-btn:disabled:active { background: var(--paper-2); color: var(--ink-2); }

.slider-row { display: flex; align-items: center; gap: 9px; }
.slider {
  flex: 1;
  min-width: 0;
  -webkit-appearance: none;
  appearance: none;
  height: 5px;
  border-radius: 999px;
  background: var(--hairline-2);
  outline: none;
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #fff;
  box-shadow: var(--shadow-1);
  cursor: pointer;
}
.slider::-moz-range-thumb {
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #fff;
  cursor: pointer;
}
.slider-readout {
  min-width: 42px;
  text-align: right;
  font-size: 13px;
  font-weight: 800;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

.popover-note {
  margin: 2px 10px 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink-3);
}

/* the popover has a lot in it now */
.popover { max-height: min(76vh, 580px); overflow-y: auto; }


/* ============================================================
   TOAST
   ============================================================ */

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--toolbar-h) + 22px);
  transform: translate(-50%, 14px);
  padding: 11px 18px;
  border-radius: 999px;
  background: var(--ink);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  box-shadow: var(--shadow-2);
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s, transform .2s;
  z-index: 300;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }


/* ============================================================
   SMALL SCREENS
   ============================================================ */

@media (max-width: 640px) {
  :root {
    --head-w: 122px; --dot: 20px; --note-h: 56px;
    --instrument-size: 84px; --track-name-size: 14.5px; --mini-btn: 29px;
    --corner-btn: 23px;
  }
  .app-mark { font-size: 17px; }
  .home-link span { display: none; }
  .home-link { padding: 7px; }
  .tool-label { display: none; }
  .autosave-toggle { padding: 0 9px; }
  .autosave-label { display: none; }
}


/* ============================================================
   TOUCH AND SMART BOARD

   A classroom board is a coarse pointer with no cursor. Three things
   follow from that, and everything in this block is one of them:

   1. Nothing hovers. Anything a mouse only ever discovers by hovering
      is, on a board, invisible.
   2. Hover latches. After a tap, :hover stays on the tapped element
      until something else is tapped, so a hover effect meant to last a
      moment sits there instead. The :not(:active) guards below let the
      press itself through and only cancel the state it leaves behind.
   3. A finger is much wider than a mouse tip, and the board is used
      standing up, at arm's length, by someone facing a class.
   ============================================================ */

@media (hover: none), (pointer: coarse) {

  /* --- the beat-repeat mark ---
     The button is a transparent hit area sitting on a rest, and the
     simile glyph inside it is the only thing that says so. Revealed on
     :hover alone, a board gets an invisible control that duplicates the
     previous beat when it is brushed — and :focus-visible does not stand
     in for it, because it does not match a touch tap.

     Showing the glyph outright instead is worse: it lands on top of the
     rest it is offering to replace and the two read as one smudged
     symbol. So what appears on a board is the button's own outline — a
     dashed box saying something here is tappable, in the chrome's colour
     rather than the notation's — and the mark itself still waits for the
     press, when the rest steps aside for it exactly as it does under a
     cursor. */
  .beat-repeat-btn {
    border: 1.5px dashed var(--hairline-2);
    border-radius: 7px;
    opacity: .8;
  }
  .beat-repeat-btn:active { border-color: var(--accent); opacity: 1; }

  /* The rest steps aside for a press, not for the hover state left
     behind by the last one. */
  .beat-repeat-btn:hover:not(:active) ~ .rhythm-svg .rest { opacity: 1; }
  .beat-repeat-btn:hover:not(:active) .simile-svg { opacity: 0; }

  /* --- +/− subdivision ---
     16x14 stacked a pixel apart is a mis-tap waiting to happen. The
     gutter they sit in is widened to match by GUTTER_L in script.js,
     which is what the beat-width maths uses. */
  .subdivision-controls { left: 3px; gap: 2px; }
  .subdivision-btn { width: 26px; height: 24px; border-radius: 7px; font-size: 15px; }
  .dots { min-height: calc(var(--dot) + 28px); }

  /* --- dots, link and mute ---
     The dot's drawn size is what the notation engine measures its
     centres from, so it stays put and an invisible collar around it
     takes the tap. COLUMN_MIN (30px) is the tightest the dots ever
     pack, so a 4px collar meets its neighbour without overlapping. */
  .dots .dot { position: relative; }
  .dots .dot::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; }

  .beat-link-btn::after { content: ''; position: absolute; inset: -6px; border-radius: 50%; }

  .mute-badge::after,
  .remove-badge::after { content: ''; position: absolute; inset: -5px; border-radius: 50%; }

  /* --- the time signature ---
     Two separate targets stacked inside one 44x48 button; each digit
     needs to be reachable without catching the other. */
  .ts-num { padding: 3px 7px; }

  /* --- hover states that would otherwise stay behind ---
     Only the ones that move or reveal something. A tint left on the last
     thing tapped is harmless, and undoing it here would need each rule to
     restate a base colour that a state class (.selected, .muted) is
     entitled to override. */
  .dots .dot:hover:not(:active) { transform: none; }
  .instrument-card:hover:not(:active) { transform: none; }
  .play-btn:hover:not(:active) { transform: none; }
  .ts-num:hover:not(:active) { transform: none; }
}


/* ============================================================
   LAYOUT SETTINGS & LESSONS

   Two sheets built out of the pieces the app already has — the
   same cards, chips, switches and segmented controls — so the
   place where the room is arranged does not look like a
   different program from the room.
   ============================================================ */

/* Removed, not disabled. Everything a layout or a lesson takes
   away goes entirely: a greyed button with a lock on it is an
   invitation to wonder what is behind it, which is the opposite of
   what a narrowed app is for. */
.policy-off { display: none !important; }

.sheet-wide { width: min(620px, 100%); }

.settings-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--hairline);
}
.settings-card:last-child { border-bottom: none; }
.card-title { margin: 0; font-size: 15px; font-weight: 800; }
.card-desc { margin: 0; font-size: 13px; color: var(--ink-2); line-height: 1.45; }
.card-head-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.card-head-row .card-title,
.card-head-row .card-desc { flex: 1; min-width: 0; }

.switch-list { display: flex; flex-direction: column; gap: 2px; }

/* The switch rows in these sheets carry a line of explanation under
   the name; the popover's carry only a name. Same pill either way. */
.switch-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.switch-name { font-size: 15px; font-weight: 700; }
.switch-desc { font-size: 12px; font-weight: 600; color: var(--ink-3); line-height: 1.35; }
.switch-row .switch { flex: none; }

.settings-card .seg,
.settings-card .chip-row { margin: 0; }
.settings-card .seg { margin: 2px 0; }

/* A way through to another sheet, reading as a line of the popover
   rather than as a button competing with the switches above it. */
.popover-link {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink-2);
  text-align: left;
  transition: background .13s, color .13s;
}
.popover-link svg { width: 16px; height: 16px; flex: none; }
.popover-link:hover,
.popover-link:active { background: var(--paper-2); color: var(--ink); }
.popover-link-strong { color: var(--ink); }
.popover-link-strong svg { color: var(--accent); }

/* ---- the tempo slider ----
   Rhythm Poetry's hover gauge: a small card above the BPM button with a
   slider to drag, so the button's own tap still types. Mouse only. */
.hover-gauge {
  position: fixed;
  z-index: 65;
  display: none;
  align-items: center;
  gap: 12px;
  width: 260px;
  padding: 10px 16px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-3);
}
.hover-gauge.show { display: flex; }
.hover-gauge-label {
  flex: none;
  min-width: 58px;
  font-family: var(--font-round);
  font-size: 13px;
  font-weight: 700;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.gauge-slider {
  flex: 1;
  min-width: 0;
  height: 6px;
  appearance: none;
  border-radius: 999px;
  background: var(--hairline);
  cursor: pointer;
}
.gauge-slider::-webkit-slider-thumb {
  appearance: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--surface);
  box-shadow: 0 1px 4px rgba(27,35,64,.3);
  cursor: pointer;
}
.gauge-slider::-moz-range-thumb {
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--surface);
  cursor: pointer;
}
@media (hover: none), (pointer: coarse) {
  .hover-gauge { display: none !important; }
}
body.present-mode .hover-gauge { display: none !important; }

/* ---- the kit ---- */
.kit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
  gap: 6px;
}
.kit-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 8px 4px 6px;
  border: 2px solid transparent;
  border-radius: var(--r-sm);
  background: var(--paper);
  transition: background .13s, border-color .13s, opacity .13s;
  opacity: .42;
}
.kit-cell img { width: 40px; height: 40px; object-fit: contain; filter: grayscale(1); }
.kit-cell span { font-size: 11px; font-weight: 700; color: var(--ink-2); text-align: center; }
.kit-cell.picked { opacity: 1; background: var(--accent-soft); border-color: var(--accent); }
.kit-cell.picked img { filter: none; }
.kit-cell.picked span { color: var(--ink); }
.kit-cell:hover { background: var(--paper-2); }
.kit-cell.picked:hover { background: var(--accent-soft); }

/* ---- the rhythm vocabulary ---- */
.vocab-list { display: flex; flex-direction: column; gap: 8px; }

.vocab-block {
  padding: 8px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--paper);
  transition: opacity .14s;
}
.vocab-block.off { opacity: .55; }

.vocab-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 6px 6px 8px;
  text-align: left;
}
.vocab-head-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.vocab-head-name { font-size: 15px; font-weight: 800; color: var(--ink); }
.vocab-head-blurb { font-size: 12px; font-weight: 600; color: var(--ink-3); line-height: 1.35; }
.vocab-head .switch { flex: none; }
.vocab-head.active .switch { background: var(--accent); }
.vocab-head.active .switch::after { transform: translateX(16px); }
/* Half a group on: the pill sits halfway, which is the only honest
   place for it and needs no third colour to say so. */
.vocab-head.partial .switch { background: var(--hairline-2); }
.vocab-head.partial .switch::after { transform: translateX(8px); }

.vocab-grid { display: flex; flex-wrap: wrap; gap: 5px; align-items: flex-end; }

.vocab-cell {
  position: relative;
  display: flex;
  align-items: flex-end;
  padding: 4px 8px 2px;
  border: 2px solid transparent;
  border-radius: var(--r-sm);
  background: var(--surface);
  opacity: .34;
  transition: opacity .13s, border-color .13s, background .13s;
}
.vocab-cell.picked { opacity: 1; border-color: var(--accent); background: var(--accent-soft); }
.vocab-cell:hover { background: var(--paper-2); }
.vocab-cell.picked:hover { background: var(--accent-soft); }
.vocab-cell.static { cursor: default; }
.vocab-cell .rhythm-svg { position: static; }

.vocab-note {
  margin: 4px 6px 2px;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.4;
}

/* ---- the task cards ---- */
.task-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 6px;
}
.task-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border: 2px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--paper);
  text-align: left;
  transition: border-color .13s, background .13s;
}
.task-card:hover { background: var(--paper-2); }
.task-card.active { border-color: var(--accent); background: var(--accent-soft); }
.task-card-label { font-size: 14px; font-weight: 800; color: var(--ink); }
.task-card-desc { font-size: 12px; font-weight: 600; color: var(--ink-2); line-height: 1.35; }

/* ---- small number fields ---- */
.text-field.num { width: 84px; flex: none; text-align: center; }
.range-row, .limit-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.range-row[hidden] { display: none; }
.limit-row .field-label { margin: 0; }
.range-dash, .range-unit { font-size: 13px; font-weight: 700; color: var(--ink-2); }

.inline-check {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  width: 100%;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  line-height: 1.45;
  cursor: pointer;
}
.inline-check input { accent-color: var(--accent); width: 16px; height: 16px; flex: none; margin-top: 2px; }

.lesson-song-list { display: flex; flex-direction: column; gap: 2px; }
.lesson-song-list .inline-check {
  padding: 7px 8px;
  border-radius: var(--r-sm);
  color: var(--ink);
  transition: background .13s;
}
.lesson-song-list .inline-check:hover { background: var(--paper); }
.lesson-song-list .inline-check em {
  font-style: normal;
  font-weight: 700;
  color: var(--ink-3);
  font-size: 12px;
}
.lesson-warn {
  padding: 8px 10px;
  border-radius: var(--r-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-weight: 700;
}

.lesson-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.lesson-actions .btn { flex: 1; min-width: 150px; justify-content: center; }

.lesson-saved-list { display: flex; flex-direction: column; gap: 2px; }
.lesson-saved-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  transition: background .13s;
}
.lesson-saved-row:hover { background: var(--paper); }
.lesson-saved-title { flex: 1; min-width: 0; font-size: 14px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.lesson-sub {
  margin: 6px 2px 0;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-3);
}


/* ============================================================
   IN A LESSON

   The strip that says what the task is, the bar a teacher gets
   while trying their own link on, and the handful of controls
   that stop being controls.
   ============================================================ */

.task-strip {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  padding: 9px 18px;
  background: var(--accent-soft);
  border-bottom: 1px solid var(--hairline);
}
.task-strip[hidden] { display: none; }
.task-strip-title { font-size: 14px; font-weight: 800; color: var(--accent-deep); }
.task-strip-note { font-size: 13px; font-weight: 600; color: var(--ink-2); }
body.present-mode .task-strip { display: none; }

.preview-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 9px 18px;
  background: var(--ink);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
}
.preview-bar[hidden] { display: none; }
.preview-bar .btn { background: rgba(255,255,255,.14); color: #fff; }
.preview-bar .btn:hover,
.preview-bar .btn:active { background: rgba(255,255,255,.26); }

/* A value that cannot be changed still has to be readable. These stop
   being buttons and become the plain text of what the piece is set to,
   rather than disappearing and leaving a student guessing. */
.time-sig.fixed { cursor: default; background: transparent; }
.time-sig.fixed:hover,
.time-sig.fixed:active { background: transparent; }
.time-sig.fixed .ts-num,
.ts-num.fixed { cursor: default; pointer-events: none; }
.stepper.fixed .step-body { min-width: 0; padding: 0 8px; }

.dots .dot.fixed { cursor: default; }
.dots .dot.fixed:hover,
.dots .dot.fixed:active { transform: none; }
.instrument-btn.fixed { cursor: default; }
.instrument-btn.fixed:hover,
.instrument-btn.fixed:active { background: var(--surface); border-color: var(--hairline); transform: none; }

@media (max-width: 640px) {
  .settings-card { padding: 14px 16px; }
  .kit-grid { grid-template-columns: repeat(auto-fill, minmax(68px, 1fr)); }
}

/* A tempo a lesson has fixed: still the same readout, no longer a button. */
.tool-btn.fixed { cursor: default; }
.tool-btn.fixed:hover,
.tool-btn.fixed:active { background: transparent; }

/* The way through to Layout settings closes the Settings popover, set
   apart from the switches above it by a rule. */
.popover-foot { padding-top: 12px; border-top: 1px solid var(--hairline); }


/* ============================================================
   EMBEDDED IN THE MUSIC STAND  (?embed=music-stand)
   The Music Stand shows this app in a frame as a score
   and nothing else. The body carries `present-mode` as well, so
   everything present mode already hides stays hidden; this only
   removes present mode's own transport, which the Music Stand replaces,
   and gives the pane back the room a projector needed.
   ============================================================ */
body.embedded .present-bar,
body.embedded .task-strip,
body.embedded .pager,
body.embedded .toast { display: none !important; }
body.embedded .stage { padding: 14px 0 6px; }
body.embedded .dots .dot,
body.embedded .instrument-btn.fixed { cursor: default; }
/* centred in its pane, the way a page sits on a stand */
body.embedded #grid-fit { margin: 0 auto; }

/* ---- editing in a pane ----
   The Music Stand can hand the score back to the pointer. What comes
   back is the score's own controls and nothing else: the divisions, the
   joins, the two repeat marks, the pair that changes the length, and
   the ones that add, remove and reorder instruments. The
   topbar and toolbar stay away — what is in them either belongs to the
   Music Stand now (the tempo, the mutes, the count) or has no meaning in
   a pane (Auto-save, the library, sharing).

   These undo present mode's own hiding, so they have to out-weigh it:
   one class deeper is enough, and both live on the body. */
body.embedded.editing .subdivision-controls,
body.embedded.editing .beat-link-btn,
body.embedded.editing .beat-repeat-btn,
body.embedded.editing .measure-repeat-btn,
body.embedded.editing .measure-add-btn,
body.embedded.editing .measure-cut-btn,
body.embedded.editing .track-move,
body.embedded.editing .remove-badge { visibility: visible; }
body.embedded.editing .add-track-row { display: flex; }
body.embedded.editing .dots .dot { cursor: pointer; }
body.embedded.editing .instrument-btn:hover,
body.embedded.editing .instrument-btn:active { transform: none; }

/* EASY circles are the stand's to switch on (the pane's View menu) and
   answer the pointer only while it is editing, like the dots do. */
body.embedded .easy-choice { cursor: default; }
body.embedded:not(.editing) .easy-choice:hover,
body.embedded:not(.editing) .easy-choice:active { transform: none; }
body.embedded:not(.editing) .easy-choice:not(.on):hover { opacity: .3; }
body.embedded.editing .easy-choice { cursor: pointer; }

/* Which instruments there are, and in what order, is an edit of the
   piece like any other, so the ×, the arrows and the Add row come back
   with Edit (above) and not before. The other two on a track head
   answer whether or not the pane is editing, and each answers to the
   mixer rather than instead of it:

     the mute badge   always — a mute is a mixer's job, and the mixer is
                      live whether or not the score is being edited. It
                      sets the stand's mute for that voice, so the badge
                      and the mixer button are one switch seen twice.
     the instrument   always, unless a lesson has fixed it — it opens
                      the stand's picker (the same one the mixer's
                      pictures open), and the change comes back as an
                      edit of the piece on this stand.

   Both are the same size they are in the app; a track head in a pane is
   small enough already. */
body.embedded .instrument-btn { pointer-events: none; }
body.embedded .instrument-btn:not(.fixed) { pointer-events: auto; cursor: pointer; }

/* The picker is a sheet, and a pane can be short — let it use the room
   it has rather than run off the bottom of the frame. */
body.embedded .sheet-backdrop { padding: 10px; }
body.embedded .sheet { max-height: 100%; }


/* ============================================================
   HOW THIS WORKS
   Rhythm Poetry's help sheet, in this app's colours.
   ============================================================ */
.help-body { padding: 16px 20px 22px; }
.help-side-note {
  margin: 0 0 16px;
  padding: 12px 15px;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
}
.help-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 13px; }
.help-list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-2);
}
.help-list b { color: var(--ink); }

.help-key {
  flex: none;
  min-width: 30px; height: 30px;
  /* inline-grid so the badges used mid-sentence do not become block-level */
  display: inline-grid; place-items: center;
  vertical-align: middle;
  padding: 0 7px;
  border-radius: 8px;
  background: var(--paper-2);
  border: 1px solid var(--hairline-2);
  font-size: 14px; font-weight: 800; color: var(--ink-2);
}
.help-list > li > .help-key { margin-top: 1px; }

.dot-demo { flex: none; display: flex; gap: 4px; align-items: center; height: 30px; }
.dot-demo .dot { width: 13px; height: 13px; border-radius: 50%; display: block; }
.dot-demo .dot.on  { background: var(--sound); }
.dot-demo .dot.off { background: var(--rest); }
