/* Application chrome: everything that is not the chart itself. */

/* --- the palette ----------------------------------------------------- */
/* One theme, dark. Every colour the chrome draws with is named here and
   nowhere else. Dark is for the room, not the page: a chart gets read on a
   stand in a venue with the lights down, and a full-white screen at arm's
   length is the wrong instrument for that. There was a light theme to choose
   until 26 Oct 2026; Adam took it out ("dark mode is by far the best way to
   use the app").

   The chart is deliberately not part of this. It keeps its own tokens in
   chart.css and its own white background, because the chart is a picture of
   a piece of paper and paper does not have a dark mode — the PDF, the print
   and the page on the stand are all the same object. What changes here is
   the room the paper is lying in. */
/* A double tap anywhere is never the iPad zooming the whole screen in, with
   no way back out where the page takes the pinch itself (Adam, 23 Oct 2026,
   in the iPad app). Panning and pinching are left as they were; a double tap
   the page listens for still arrives. */
html { touch-action: manipulation; }
:root {
  /* Declared as well as in the <meta>, so native controls are dressed dark
     whatever the system is set to. */
  color-scheme: dark;

  --ink: #eceef2;
  --ink-soft: #9aa1ad;
  /* Faint text lands on --paper and on --chrome alike, so it has to read
     on both. */
  --ink-faint: #8a919d;
  --line: #343943;
  --line-soft: #262a31;
  --chrome: #15171b;
  --paper: #1d2026;
  /* A step above --paper, for things that float over it: menus, the mixer,
     the Layout panel. A lighter grey, or on a dark ground the shadow is
     invisible. */
  --raised: #262a31;
  --accent: #6fa0f2;
  --accent-soft: #232c3f;
  /* A filled button needs a darker blue than a blue word does: a blue light
     enough to read as text against near-black is only 3.7:1 under white
     text. */
  --accent-fill: #2f62c9;
  /* Text that sits on top of an --accent-fill. */
  --accent-ink: #ffffff;
  /* The logo's own blue, from the brand sheet rather than the UI accent. */
  --brand-accent: #5b8cff;
  /* The accent at low alpha, for hovers and washes. */
  --wash: rgba(91, 141, 239, 0.14);
  --wash-firm: rgba(91, 141, 239, 0.26);
  --danger: #ef6a6a;
  --danger-line: #6d2f2c;
  --warn: #e8a33d;
  --warn-ink: #1d1d1f;

  /* The ground behind the page. Layout puts the page on a desk so you can
     see it is a page; Read used to paint this white, which on anything but
     a 4:3 screen left the chart floating in a white field with no visible
     edge. Both are surfaces now, and the page carries a hairline. */
  --desk-build: #131519;
  --desk: #0f1114;
  --page: #ffffff;
  /* The floating control clusters in Layout and Read, which sit over the
     page and the desk alike, so they have to read against both. */
  --glass: rgba(38, 42, 49, 0.94);
  --page-edge: rgba(255, 255, 255, 0.16);

  /* Does it fit. */
  --ok-bg: #16301f;      --ok-fg: #62d79a;   --ok-line: #245239;
  --tight-bg: #2e2618;   --tight-fg: #e8a33d; --tight-line: #4a3c1e;
  --over-bg: #33191a;    --over-fg: #ef6a6a;  --over-line: #5e2b2b;

  /* Cells and marks in the groove editor. */
  --cell-beat: #191c21;
  --cell-on: #ffffff;
  --cell-slash-on: #232c3f;
  --cell-cue-on: #2e2618;
  --wave-bg: #16191e;

  --shadow-1: 0 1px 4px rgba(0, 0, 0, 0.5);
  --shadow-2: 0 6px 20px rgba(0, 0, 0, 0.55);
  --shadow-page: 0 0 0 1px var(--page-edge), 0 24px 56px rgba(0, 0, 0, 0.7);

  --radius: 7px;
  --tap: 34px;
}

/* Where the theme stops.
 
   The chart is a picture of a piece of paper, and Build draws real app chrome
   onto it: the tempo's Detect button, the note-value picker, the section-name
   fields. Those inherit the chrome palette, so in a dark room a dark select
   landed in the middle of a white page. Inside the page the chrome palette is
   pinned to its light values whatever the room is set to — including
   color-scheme, or a native select's dropdown opens dark over the paper.
 
   This is also what keeps the printed page and the PDF honest: they are
   rendered from these same rules, and paper has one appearance. */
.chart,
.ge-preview {
  color-scheme: light;

  --ink: #14161a;
  --ink-soft: #5c626d;
  --ink-faint: #6b717c;
  --line: #d9dce2;
  --line-soft: #eceef2;
  --chrome: #f6f7f9;
  --paper: #ffffff;
  --raised: #ffffff;
  --accent: #1f5fd0;
  --accent-soft: #e8f0ff;
  --accent-fill: #1f5fd0;
  --accent-ink: #ffffff;
  --wash: rgba(31, 95, 208, 0.08);
  --wash-firm: rgba(31, 95, 208, 0.16);
  --danger: #b3261e;
  --danger-line: #f2c9c6;
  --warn: #a15c00;
  --warn-ink: #ffffff;
  --shadow-1: 0 1px 4px rgba(0, 0, 0, 0.18);
  --shadow-2: 0 6px 20px rgba(0, 0, 0, 0.14);
}

* { box-sizing: border-box; }

html { height: 100%; }

body {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  color: var(--ink);
  background: var(--chrome);
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

#app { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

.muted { color: var(--ink-soft); }
.small { font-size: 12px; }
.spacer { flex: 1; }

/* --- toolbar -------------------------------------------------------- */

.toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  padding-top: max(8px, env(safe-area-inset-top));
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}

.size-nudge { display: inline-flex; align-items: center; gap: 4px; }
.size-value { min-width: 34px; text-align: center; font-variant-numeric: tabular-nums; }

/* --- the view switch ------------------------------------------------- */
/* Map, Build, Score as one control. They are the three places the same chart
   lives, so they read as three faces of one thing rather than as two buttons
   at opposite ends of a crowded bar. */
/* Dead centre of the bar whatever is beside it, until a narrow window needs
   the room (see --bar-right-lane below).
 
   Three columns rather than a row with spacers in it. Spacers balance the
   space *between* the groups, so the switch drifted with whatever those groups
   weighed — 52px right of centre in Build and Layout. Taking it out of flow
   fixed the drift but left nothing holding its space, so the moment a group
   grew (the click coming on, say) it slid straight over the top of it.
 
   `1fr auto 1fr` gives the two side columns equal width whatever is in them,
   which puts the middle one at true centre, and gives the switch a lane
   nothing else can enter. */
/* Only the editor's bar and Map's. The library's toolbar wears the same class
   but holds a title, a search field and two buttons — no groups, no switch —
   and putting that in three lanes stacked it into a mess. */
/* The switch is centred on the window, and the space it needs is reserved by a
   spacer that cannot shrink below its width. Nothing else can reach it.
 
   Equal thirds were tried first and are wrong: the two sides are not equal
   weights — Map's left runs to 649px against its right's 421 — so a lane wide
   enough for the heavy side wastes 200px on the light one, and a lane sized to
   the average gets overflowed by the heavy one. Overflowed, its contents ran
   straight over the switch, which is what happens the moment a status line or
   the play progress appears. Here the groups take exactly what they need, the
   spacer takes the rest and never less than the switch, and if the bar ever
   genuinely runs out of room it wraps a row rather than overlapping. */
.toolbar.chart-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  position: relative;
}
/* The lane the right-hand group needs: its play controls and the menu, as
   wide as they get on that bar (Build and Score's, the heaviest; Map's is
   lighter), plus a little room. Above 1300 Loop and Click carry their words
   (see .pc-label below) and the bar is wide enough to centre around them. */
.toolbar.chart-bar,
.av-bar { --bar-right-lane: 392px; }
@media (hover: none) and (pointer: coarse) {
  .toolbar.chart-bar,
  .av-bar { --bar-right-lane: 404px; }
}
@media (min-width: 1300px) {
  .toolbar.chart-bar,
  .av-bar { --bar-right-lane: 488px; }
}
@media (min-width: 1300px) and (hover: none) and (pointer: coarse) {
  .toolbar.chart-bar,
  .av-bar { --bar-right-lane: 500px; }
}
/* Centred on the bar until the right-hand group's lane would be narrower than
   it needs, then that lane's width in from the right edge, so on a narrow
   window it moves left with the edge. Where it sits depends on the window's
   width alone: never on what is selected or which view you are in, so it is
   in the same place in Map, Build and Score and does not shift under you.
   127px is the bar's padding, the air beside the switch and half the switch. */
.toolbar.chart-bar > .view-switch,
.av-bar > .view-switch {
  position: absolute;
  left: min(50%, calc(100% - var(--bar-right-lane) - 127px));
  transform: translateX(-50%);
}
/* View and Edit (viewswitch.js): the chart's name in the middle of Build's
   bar, where the switch was. */
.view-switch.edit-middle { gap: 10px; align-items: center; min-width: 0; background: none; border: 0; padding: 0; }
.edit-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 32vw; }
.bar-gap { flex: 1 1 auto; min-width: 0; }

/* Neither group may reach the switch. Half the width minus half the switch
   minus a little air is how much room each side has while the switch is
   centred; once it has moved left, the right side keeps its lane and the left
   gives up what the switch took. Capped here rather than left to a reserved
   gap, because a gap between two groups of different weights is not centred
   on the bar and the switch would still overhang the heavier side.

   No overflow clipping here, tempting as a backstop looks: the Mix menu and
   Map's speed and A-B popovers are positioned inside these groups, and hiding
   the overflow hid them along with it. The caps are what prevents the overlap,
   and they do it without ever needing to clip. */
.bar-group {
  max-width: calc(50% - 114px);
}
.bar-left { max-width: min(calc(50% - 114px), calc(100% - var(--bar-right-lane) - 230px)); }
.bar-right { max-width: max(calc(50% - 114px), var(--bar-right-lane)); }
/* Nothing may wrap the bar to a second row: the switch is out of flow, so a
   wrapped row would run underneath it. */
.toolbar.chart-bar,
.av-bar { flex-wrap: nowrap; }
.bar-group {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 0 1 auto;
}
/* Controls keep their size; the two things that report rather than do — the
   status line and the play progress — are what shrink when the lane is tight. */
.bar-group > * { flex: 0 0 auto; }
.bar-left { justify-self: start; }
.bar-right { justify-self: end; }
.av-bar .bar-group { gap: 6px; }


.view-switch {
  display: inline-flex;
  background: var(--chrome);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 2px);
  padding: 3px;
  gap: 3px;
}
.view-switch .btn.view-tab {
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  min-height: calc(var(--tap) - 6px);
  padding: 0 15px;
  border-radius: var(--radius);
  font-weight: 500;
}
.view-switch .btn.view-tab:hover:not(:disabled) { background: var(--line-soft); color: var(--ink); }
/* The fill has to be restated: .view-switch .btn.view-tab out-specifies
   .btn.primary, so the transparent background above was winning and leaving
   the current view as white text on nothing. */
.view-switch .btn.view-tab.primary,
.view-switch .btn.view-tab.primary:hover {
  background: var(--accent-fill);
  color: var(--accent-ink);
  font-weight: 600;
}
.view-switch .btn.view-tab:disabled { background: transparent; }

/* The round, glassy shape the floating corner cluster wears. */
.view-switch-round {
  border-radius: 999px;
  background: var(--glass);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(6px);
  border-color: transparent;
  padding: 4px;
}
/* The theme switch wears the cluster's shape in Read too, same as the click. */
.reading-controls .theme-btn {
  border-radius: 999px;
  min-height: 44px;
  width: 44px;
  background: var(--glass);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(6px);
  border-color: transparent;
  font-size: 15px;
}

.view-switch-round .btn.view-tab {
  border-radius: 999px;
  min-height: 36px;
  padding: 0 16px;
  font-size: 14px;
}

.sound-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 50;
  /* Wide enough for the click's three choices side by side: at 268px the last
     of them wrapped. */
  min-width: 300px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 13px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 3px);
  box-shadow: var(--shadow-2);
}
.sound-group { display: flex; flex-direction: column; gap: 7px; }
.sound-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Stepping the size dial, beside the dial itself in the Layout panel. */
.lp-nudge { display: inline-flex; gap: 4px; justify-content: flex-end; }
.lp-nudge .btn { min-width: 38px; }
.lp-nudge .lp-count { min-width: 22px; text-align: center; align-self: center; font-size: 13px; font-variant-numeric: tabular-nums; }

.app-title { font-size: 17px; margin: 0; font-weight: 600; letter-spacing: -0.01em; }
/* The DC logo before the name: the letters follow the title's colour, the
   coda cross takes the brand blue, so it holds in either theme. Sized to
   the cap height so the header row does not grow. */
.app-title { display: inline-flex; align-items: center; gap: 7px; }
.app-mark { width: 1.25em; height: auto; flex: none; }
.app-mark .ink { stroke: currentColor; }
.app-mark .beat { stroke: var(--brand-accent); }

.btn {
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 11px;
  min-height: var(--tap);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.btn:hover { background: var(--line-soft); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.4; cursor: default; }
.btn.primary { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--accent-ink); }
.btn.primary:hover { filter: brightness(1.08); }
.btn.danger { color: var(--danger); }
.btn.small { padding: 4px 9px; min-height: 30px; font-size: 13px; }
.btn.tiny { padding: 3px 8px; min-height: 27px; font-size: 12px; }
.btn.ghost { border-style: dashed; color: var(--ink-soft); }
.icon-btn { padding: 4px 8px; min-width: 30px; min-height: 30px; font-size: 14px; line-height: 1; }

/* --- split layout --------------------------------------------------- */

.split { flex: 1 1 auto; display: flex; min-height: 0; position: relative; }

/* The whole screen is rebuilt on every edit (see app.js), which the browser's
   own scroll anchoring reads as content having shifted around above the
   fold — it "corrects" for that by moving the scroll position on its own,
   fighting the deliberate restore app.js already does. Disabled, so the one
   thing actually allowed to move this pane's scroll is the person reading
   it. */
.pane { overflow: auto; overflow-anchor: none; -webkit-overflow-scrolling: touch; min-height: 0; }

.editor-pane {
  flex: 1 1 46%;
  /* The bottom padding used to be 40vh — 360px on a 900px window — which put a
     scrollbar on this panel and pushed the grid under a fold even when
     everything in it fitted with room to spare. Nothing needed the space: the
     groove editor's cell menu is positioned against the window and flips above
     its anchor when it would run off the bottom (see placeGrooveMenu), so it
     never depended on the panel being scrollable. It scrolls now only when the
     groove genuinely is taller than the panel. */
  padding: 12px 12px 16px;
  border-right: 1px solid var(--line);
  background: var(--chrome);
}

/* Build's detail editor is permanently docked down this side, not a panel that
   comes and goes. The groove editor is what is usually in it and it is used
   constantly, so it is furniture rather than a pop-up.
 
   The width matters more than it looks: every column of the groove grid is
   sized from this box through a container query (see cellColumns in
   grooveeditor.js), so a narrow panel silently halves the cells you tap. 46%
   is what the pane came to before, which is the width the grid was built
   against; the floor keeps it usable on a small window and the ceiling stops
   it eating half of an ultrawide screen for a grid that has stopped needing
   the room. */
@media (min-width: 900px) {
  .editor-pane.build-pane {
    flex: 0 0 clamp(392px, 46%, 720px);
  }
}

.editor-pane-empty {
  margin: 20% 12px 0;
  text-align: center;
  font-size: 13px;
}

.chart-pane {
  flex: 1 1 54%;
  padding: 16px;
  background: var(--desk-build);
}

/* --- Score ------------------------------------------------------------ */
/* The page as an object on a desk, with room for the controls that finish it
   — and no editor pane behind it. */

body.layout-view .editor-pane { display: none; }

/* Score keeps the bar, so Map / Build / Score is reachable from every view
   rather than only from the one it was built into, and play sits in the same
   corner there as everywhere else. */
body.layout-view .toolbar { background: var(--paper); }

body.layout-view .split { display: block; height: 100%; }

body.layout-view .chart-pane {
  flex: 1 1 100%;
  height: 100%;
  /* The page is centred in whatever is left of the window. */
  display: flex;
  align-items: center;
  justify-content: center;
  overscroll-behavior: contain;
  /* Nothing on the page is text to copy: a shift-click to take in more
     boxes swept the browser's text highlight across the columns first
     (Adam, 21 Oct). */
  -webkit-user-select: none;
  user-select: none;
}

/* The page sits on a desk, so you can see it *as* a page — where its edges
   are, how much of it is still empty. That reading depends entirely on there
   being a surface behind it that is not the page. */
body.layout-view .chart-pane {
  background: var(--desk);
  padding: 28px max(20px, env(safe-area-inset-right))
           calc(28px + 56px) max(20px, env(safe-area-inset-left));
}

/* The frame is taken out of flow by its own transform, so the stage stands in
   for the size it is actually drawn at — see applyFit, which sets both. Its
   auto margins centre it while it fits and give way when it does not: a page
   zoomed in past the screen (setScoreViewZoom) scrolls out to every edge,
   where centring by the pane would leave the left and top out of reach. */
.frame-stage { position: relative; flex: 0 0 auto; }
body.layout-view .frame-stage { margin: auto; }
body.layout-view .chart-pane.zoomed { justify-content: flex-start; align-items: flex-start; }
body:not(.score-zoomed) .fit-page-btn { display: none; }
body.layout-view .chart-pane.zoomed { overflow: auto; }
/* On a touch screen Score's page is moved by the fingers as View's is
   (fingerPinch, wireScoreZoom in editor.js), never by the browser's own
   scrolling, which took the fingers from a pinch part way through. */
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) body.layout-view .chart-pane { touch-action: none; }
}

body.layout-view .chart-frame {
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  background: var(--page);
}

body.layout-view .chart-frame {
  box-shadow: var(--shadow-page);
  border-radius: 2px;
}

body.layout-view .chart {
  box-shadow: none;
  border-radius: 0;
  min-height: 100%;
}

/* --- the Layout panel ------------------------------------------------ */
/* The one place in the app that can afford a control surface at all — and
   even here it earns its height: one question showing, the rest folded (see
   panelDisclosure in editor.js). */

body { --panel-w: 268px; }

.layout-panel {
  position: fixed;
  /* Below the bar, not over it. Layout keeps the bar now, and a panel pinned
     to the top of the window covered the fit badge, the theme, the click and
     play — and padding the bar out of its way instead would have shifted the
     view switch off centre, which is the one thing that has to stay put. */
  top: var(--bar-h, 0px);
  right: 0;
  bottom: 0;
  width: var(--panel-w);
  z-index: 45;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Sized to fit an iPad's height without scrolling: scrolling a panel to find
     the one control you want, while the page it changes sits still beside it,
     is what this panel used to ask. The action strip sits beside the panel,
     not under it, so the bottom needs no room kept for it. */
  padding: 10px 14px calc(12px + env(safe-area-inset-bottom));
  padding-top: max(12px, env(safe-area-inset-top));
  background: var(--paper);
  border-left: 1px solid var(--line);
  box-shadow: -2px 0 14px rgba(0, 0, 0, 0.07);
}

/* The page keeps the room the panel is not using, so it stays centred in
   what is left rather than sliding behind the controls. */
body.panel-open.layout-view .chart-pane { padding-right: calc(var(--panel-w) + 20px); }
body.panel-open.layout-view .reading-controls { right: var(--panel-w); }

.lp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
}
.lp-heading { font-size: 15px; margin: 0; font-weight: 600; letter-spacing: -0.01em; }

/* A folded group: its heading is the control. Shut, it costs one row — which
   is what lets the panel show the question it is actually open for without
   the answers to a dozen others underneath it. */
.lp-fold-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 6px 0;
  background: none;
  border: 0;
  color: var(--ink-soft);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
}
.lp-fold-toggle:hover { color: var(--ink); }
.lp-fold-chev { font-size: 9px; width: 9px; }
.lp-fold-body { display: flex; flex-direction: column; gap: 8px; padding-top: 2px; }
.lp-sub { margin: 2px 0 0; }
.lp-select { flex: 1 1 auto; min-width: 0; min-height: 30px; font-size: 13px; }

.lp-group { padding: 6px 0; border-bottom: 1px solid var(--line-soft); }
.lp-group:last-of-type { border-bottom: 0; }
.lp-title {
  font-size: 11px;
  margin: 0 0 3px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

.lp-row {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr) 40px;
  align-items: center;
  gap: 8px;
  padding: 2px 0;
}
.lp-name { font-size: 12px; color: var(--ink-soft); }
.lp-value { text-align: right; font-variant-numeric: tabular-nums; }
/* A dial held at the edge of the page says so (panelSlider). */
.lp-value.at-edge { color: var(--accent); font-weight: 600; }
.lp-slider { width: 100%; min-width: 0; accent-color: var(--accent); }

/* A choice row has no readout to line up with, so it takes the width back. */
.lp-choices { grid-column: 2 / -1; display: flex; flex-wrap: wrap; gap: 4px; }
/* Three answers, too wide to sit beside their name (Style): the name over
   them, and the answers sharing the panel's whole width. */
.lp-row-stacked { grid-template-columns: minmax(0, 1fr); gap: 4px; }
.lp-row-stacked .lp-choices { grid-column: 1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.lp-row-stacked .lp-choices .btn { min-width: 0; }
.lp-choices .btn { flex: 1 1 auto; min-width: 34px; padding: 3px 6px; }
/* Play time's Pattern and Written choices: a few words each, side by side. */
.time-choices { display: inline-flex; gap: 4px; }
.time-choices .btn { min-width: 34px; }

.lp-toggles { display: grid; grid-template-columns: 1fr 1fr; gap: 0 4px; }
.lp-toggles.one { grid-template-columns: minmax(0, 1fr); }
/* The cut drum panel's one sentence when the part will not land on its pages. */
.lp-hint { margin: 4px 0 2px; line-height: 1.35; }
.lp-toggle {
  font: inherit;
  font-size: 12px;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: 5px;
  padding: 4px;
  min-height: 32px;
  display: flex;
  align-items: center;
  gap: 6px;
  text-align: left;
  cursor: pointer;
}
.lp-toggle:hover { background: var(--line-soft); }
.lp-tick {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  border: 1px solid var(--line);
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  line-height: 1;
  color: #fff;
  background: var(--paper);
}
.lp-toggle.on .lp-tick { background: var(--accent-fill); border-color: var(--accent-fill); }

.lp-wide { width: 100%; margin-top: 6px; }
.lp-reset { margin-top: 8px; color: var(--ink-soft); }

/* A touch screen's Layout panel (editor.js layoutPanelShown): Hide in its
   head, Export at its foot; put away, Layout and Export float under the
   page. Touch screens only, never the Mac app. */
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) .lp-hide { flex: 0 0 auto; }
  html:not(.drumchart-mac) .lp-foot { margin-top: auto; padding-top: 12px; display: flex; }
  html:not(.drumchart-mac) .lp-export { flex: 1 1 auto; min-height: 44px; font-size: 15px; }
  html:not(.drumchart-mac) .export-cluster { gap: 8px; }
  /* Where the panel's Hide is, so the panel goes and comes back from one
     place: its padding, then the head's. */
  html:not(.drumchart-mac) .layout-show-btn {
    position: fixed;
    z-index: 45;
    top: calc(var(--bar-h, 0px) + max(12px, env(safe-area-inset-top)));
    right: 14px;
    box-shadow: var(--shadow-1);
  }
  html:not(.drumchart-mac) .layout-show-btn::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 44px; transform: translateY(-50%); }
}

/* A narrow window has no room for a page and a panel side by side, so the
   panel takes the bottom of the screen instead and the page keeps the top. */
@media (max-width: 760px) {
  .layout-panel {
    top: auto;
    left: 0;
    width: auto;
    max-height: 52vh;
    border-left: 0;
    border-top: 1px solid var(--line);
    box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.09);
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
  }
  /* The action strip moves to the top on a narrow window, so the page has to
     get out from under it — it was sitting behind the buttons. */
  body.panel-open.layout-view .chart-pane {
    padding-right: 20px;
    padding-top: calc(max(10px, env(safe-area-inset-top)) + 56px);
    padding-bottom: 54vh;
  }
  body.panel-open.layout-view .reading-controls {
    right: max(10px, env(safe-area-inset-right));
    left: auto;
    bottom: auto;
    top: max(10px, env(safe-area-inset-top));
    justify-content: flex-end;
  }
}

.reading-controls {
  position: fixed;
  right: max(10px, env(safe-area-inset-right));
  bottom: max(10px, env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 6px;
  z-index: 40;
  opacity: 0.45;
  transition: opacity 0.15s;
}
.reading-controls:hover,
.reading-controls:focus-within { opacity: 1; }

/* Layout's cluster is not in the way of anything — there is a desk around
   the page to put it on — so it is fully lit and centred under the page,
   rather than faded into a corner the way Read's single ✕ is. A control you
   have to go looking for is a control that costs you time you meant to
   spend on the chart. */
body.layout-view .reading-controls {
  left: 0;
  right: 0;
  bottom: max(12px, env(safe-area-inset-bottom));
  justify-content: center;
  flex-wrap: wrap;
  opacity: 1;
  pointer-events: none;
}
body.layout-view .reading-controls > * { pointer-events: auto; }

/* The drawing palette (js/pencil.js): out of sight until the Apple Pencil
   touches the page, then floating at the top right, under whatever bar is
   across the top, over Score's page or Practice's: the pens, the eraser,
   undo, Clear page and Done. Only on a touch screen with nothing else to
   point with, so a computer never has one. */
.ink-palette {
  position: fixed;
  right: max(14px, env(safe-area-inset-right));
  /* Over view mode (1100), where Practice draws. */
  z-index: 1150;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  min-height: 48px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--glass);
  color: var(--ink);
  box-shadow: var(--shadow-2);
  backdrop-filter: blur(6px);
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}
.ink-tool {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--ink);
  cursor: pointer;
}
.ink-tool.on { background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--accent); }
.ink-rule { align-self: stretch; width: 1px; margin: 10px 4px; background: var(--line); }
.ink-clear {
  min-height: 38px;
  padding: 0 12px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--danger);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.ink-clear:disabled { color: var(--ink-faint); opacity: 0.6; cursor: default; }
.ink-undo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--ink);
  cursor: pointer;
}
.ink-undo:disabled { opacity: 0.35; cursor: default; }
.ink-done {
  min-height: 38px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: var(--accent-fill);
  color: var(--accent-ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}

.reading-zoom { display: inline-flex; gap: 6px; }

.btn.round {
  border-radius: 999px;
  min-width: 44px;
  min-height: 44px;
  padding: 0 12px;
  background: var(--glass);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(6px);
  font-size: 15px;
}
.btn.round.wide-btn { padding: 0 16px; }
/* .btn.primary is defined further up this file and carries exactly the same
   specificity as .btn.round, so the pale fill above was winning the tie on
   source order alone — which left every "on" control in this cluster (the
   PDF button, the click, "Repeats: collapsed") drawing white text on white.
   The accent fill and the hover feedback are both restated here so a round
   button reads the same as any other button does. */
.btn.round.primary { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--accent-ink); }
.btn.round:hover { background: var(--paper); }
.btn.round.primary:hover { background: var(--accent-fill); }

/* One shape for "press this to make sound happen," wherever it appears —
   the dock, the Build toolbar, and Map's transport all use it, so the eye
   doesn't have to relearn what play looks like on every screen. */
.btn.transport {
  border-radius: 999px;
  min-width: 40px;
  min-height: 40px;
  padding: 0;
  font-size: 15px;
}
.btn.transport.big { min-width: 48px; min-height: 48px; font-size: 18px; }

/* Score is a viewer for the chart itself, not for the recording — the dock
   belongs to Build, where you are tapping timestamps against it. */
body.layout-view #audio-dock { display: none; }

@media (max-width: 900px) {
  .split { flex-direction: column; }
  .editor-pane { flex: none; border-right: 0; border-bottom: 1px solid var(--line); padding-bottom: 16px; }
  .chart-pane { flex: none; padding: 10px; }
  /* The constant panel is a desktop fix for the chart jumping sideways —
     stacked on top of the chart instead of beside it, an empty one is just
     the chart pushed down for nothing, so here it goes back to only
     appearing once there is something to show in it. */
  .editor-pane:has(.editor-pane-empty) { display: none; }
}

/* --- Build's bottom dock: a touch screen held upright --------------------
   On an iPad held upright the editor used to stack above the chart, and the
   two scrolled as one page: tap a box low on the chart and the groove grid
   was off the top of the screen. Here the chart scrolls on its own and the
   editor is docked under it, the full width of the screen, so the grid's
   cells are as big as they are on the side dock. It fits what is in it (up
   to 62% of the height) unless its line has been dragged (dockGrip in
   editor.js), and is never less than 40%, which a groove without its tom
   rows fits in (with them, 72px more, below): stepping from a groove to a line of text and back with ›
   leaves › where it was under your finger. Touch screens only, never the Mac app, never by width alone:
   a computer's window looks as it did. Held sideways, the side dock stays. */
.dock-grip { display: none; }
.pep-step { display: none; }
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) .pep-step { display: inline-flex; align-items: center; justify-content: center; min-width: var(--tap); min-height: var(--tap); padding: 0; flex: 0 0 auto; }
}
@media (hover: none) and (pointer: coarse) and (orientation: portrait) and (min-width: 600px) {
  html:not(.drumchart-mac) body:not(.layout-view) .split { flex-direction: column; }
  html:not(.drumchart-mac) body:not(.layout-view) .split > .chart-pane { order: 0; flex: 1 1 auto; min-height: 0; padding: 10px; }
  html:not(.drumchart-mac) body:not(.layout-view) .split > .dock-grip {
    order: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    height: 18px;
    background: var(--chrome);
    border-top: 1px solid var(--line);
    touch-action: none;
    position: relative;
    z-index: 3;
  }
  /* 18 pt to look at, 44 pt to take hold of (Adam, 26 Oct: "the handle's
     quite difficult to grab"): the area runs over the chart's foot and the
     editor's top margin, which have nothing to press there. */
  html:not(.drumchart-mac) body:not(.layout-view) .split > .dock-grip::after {
    content: ''; position: absolute; left: 0; right: 0; top: -16px; bottom: -10px;
  }
  html:not(.drumchart-mac) body:not(.layout-view) .split > .dock-grip .dock-grip-bar { width: 54px; height: 6px; }
  html:not(.drumchart-mac) body:not(.layout-view) .split > .editor-pane.build-pane {
    order: 2;
    display: block;
    flex: 0 0 auto;
    height: var(--bottom-dock-h, auto);
    min-height: var(--bottom-dock-h, 40%);
    max-height: max(62%, var(--bottom-dock-h, 0px));
    border: 0;
    padding: 0 12px 12px;
  }
  /* Nothing selected: one line saying what to do, and the chart has the
     rest, whatever height the line was dragged to. The dock comes back to
     full height at the first box tapped; the chart above does not move. */
  html:not(.drumchart-mac) body:not(.layout-view) .split > .editor-pane.build-pane:has(> .editor-pane-empty) { height: auto; min-height: 0; }
  /* Toms pressed: three more rows, which at the cells' smallest were 59 pt
     more than 40% held, so the panel scrolled (against the one-screen rule).
     The dock grows by that much while they show and goes back when they are
     put away; a line dragged taller than that is kept (Adam, 20 Oct). */
  html:not(.drumchart-mac) body:not(.layout-view) .split:has(.groove-editor.toms) > .editor-pane.build-pane { min-height: max(var(--bottom-dock-h, 40%), calc(40% + 72px)); }
  html:not(.drumchart-mac) body:not(.layout-view) .split > .editor-pane .editor-pane-empty { margin: 6px 12px 4px; }
  /* Sliding up or down (slideDock in editor.js): the dock over the chart,
     which comes after it on the page and would otherwise be painted on top
     of it, its top edge shadowed as a sheet's is, and nothing let out past
     the foot of the split onto the recording's dock. */
  html:not(.drumchart-mac) body:not(.layout-view) .split.dock-moving { overflow: hidden; }
  html:not(.drumchart-mac) body:not(.layout-view) .split.dock-moving > :is(.editor-pane, .dock-grip) { position: relative; z-index: 4; }
  html:not(.drumchart-mac) body:not(.layout-view) .split.dock-moving > .dock-grip { box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35); }
  html:not(.drumchart-mac) body:not(.layout-view) .split.dock-moving > .editor-pane.dock-leaving {
    z-index: 5;
    border-top: 1px solid var(--line);
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.35);
    pointer-events: none;
  }
}
.dock-grip-bar { width: 40px; height: 5px; border-radius: 3px; background: var(--line); }
.dock-grip.dragging .dock-grip-bar { background: var(--accent); }

/* A touch screen's 44 pt (Apple's smallest target; Adam, 19 Oct: "bigger to
   touch, same look"). A control drawn smaller than that takes a touch over
   an invisible area that size around it, so nothing looks different and
   nothing on the chart covers more of the boxes. Where controls sit side by
   side (a box's tab, Undo and Redo, the repeat's buttons) the area grows up
   and down only, and out at the ends, so one never takes a touch meant for
   its neighbour. A box's or a section's tab grows upwards, away from the
   box it belongs to. On the chart the areas are divided by its zoom
   (--chart-zoom, chart.js), so they come to 44 pt on the screen at any
   size. An area is laid out inside its control's border, so a border is
   made up too. Text boxes cannot carry one, and a menu's rows are stacked
   edge to edge, so those are as they were. Touch screens only, never the
   Mac app. */
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) :is(.ph-tools .ph-tool, .ph-tools .ph-align-btn, .name-field-toggle, .rg-bar .btn, .sec-insert-btn,
    .bar-left > .btn, .view-switch .view-tab, .settings-btn, #audio-dock .btn) { position: relative; }
  html:not(.drumchart-mac) :is(.ph-tools .ph-tool, .ph-tools .ph-align-btn, .name-field-toggle, .rg-bar .btn, .sec-insert-btn, .ph-insert-btn,
    .bar-left > .btn, .view-switch .view-tab, .settings-btn, #audio-dock .btn)::after {
    content: '';
    position: absolute;
    --touch: calc(44px / var(--chart-zoom, 1));
  }
  html:not(.drumchart-mac) :is(.ph-tools .ph-tool, .ph-tools .ph-align-btn)::after { top: calc(28px - var(--touch)); bottom: -2px; left: 0; right: 0; }
  html:not(.drumchart-mac) .ph-tools > :first-child::after,
  html:not(.drumchart-mac) .ph-tools > .ph-align > :first-child::after { left: -8px; }
  html:not(.drumchart-mac) .ph-tools > :last-child::after { right: -8px; }
  html:not(.drumchart-mac) .ph-insert-btn::after { inset: calc((14px - var(--touch)) / 2); border-radius: 50%; }
  html:not(.drumchart-mac) .sec-insert-btn::after { inset: calc((16px - var(--touch)) / 2) calc((18px - var(--touch)) / 2); }
  html:not(.drumchart-mac) .name-field-toggle::after { inset: calc((28px - var(--touch)) / 2); }
  html:not(.drumchart-mac) .rg-bar .btn::after { top: calc((25px - var(--touch)) / 2); bottom: calc((25px - var(--touch)) / 2); left: 0; right: 0; }
  html:not(.drumchart-mac) .bar-left > .btn::after { top: -8px; bottom: -8px; left: -4px; right: -4px; }
  html:not(.drumchart-mac) .view-switch .view-tab::after { top: -8px; bottom: -8px; left: 0; right: 0; }
  html:not(.drumchart-mac) .settings-btn::after { top: -8px; bottom: -8px; left: -5px; right: -5px; }
  html:not(.drumchart-mac) #audio-dock .btn::after { top: -8px; bottom: -8px; left: -2px; right: -2px; }
  html:not(.drumchart-mac) #audio-dock .btn.transport::after { inset: -3px; }
  html:not(.drumchart-mac) .au-grip::after { content: ''; position: absolute; left: 0; right: 0; top: -14px; bottom: -21px; }
}

/* The groove editor under a finger (Adam, 20 Oct). A finger held on a cell
   opens the cell's menu, and not iOS's text selection and its Copy / Look Up
   bar, which the hold brought up over the grid. The menu's rows are 44 pt,
   and the finger still down can slide onto one and let go to pick it
   (sliding in grooveeditor.js): the row under it is lit. Touch screens
   only, never the Mac app. */
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) .groove-editor { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  html:not(.drumchart-mac) .groove-editor :is(input, textarea) { -webkit-user-select: text; user-select: text; }
  html:not(.drumchart-mac) .ge-menu { padding: 4px; }
  html:not(.drumchart-mac) .ge-menu-item { min-height: 44px; font-size: 15px; padding: 8px 14px; gap: 10px; }
  html:not(.drumchart-mac) .ge-menu-glyph { width: 18px; font-size: 16px; }
  html:not(.drumchart-mac) .ge-menu-item.under-finger { background: var(--accent-soft); }
}

/* Build's sheet under a finger (Adam, 20 Oct: the "+" to write a phrase and
   its choices "so tiny", the title "really small"). The head is for reading
   and typing into here, not the printed page's margin, so it is drawn at
   page size whatever Score's title setting; an empty section's "+" is a
   finger's width; the choices under a "+" are 44 pt rows; the "+" between
   boxes and between sections are drawn bigger, their touch areas as they
   were. Touch screens only, never the Mac app. */
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .chart .chart-title { font-size: 22px; letter-spacing: -0.01em; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .chart :is(.chart-meta, .cm-field) { font-size: 14px; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .phrase-add-box { border-width: 2px; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .phrase-add-box .plus-icon svg { width: 22px; height: 22px; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .phrase-add-menu { gap: 4px; padding: 6px; border-radius: 8px; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .phrase-add-menu .btn.tiny { min-height: 44px; font-size: 15px; padding: 8px 14px; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .ph-insert-btn { width: 24px; height: 24px; }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .ph-insert-btn::after { inset: calc((24px - var(--touch)) / 2); }
  html:not(.drumchart-mac) body:not(.layout-view) .chart-pane .sec-insert-btn { min-width: 26px; min-height: 24px; }
}

/* The same 44 pt areas on Map's and Score's own small controls (Adam's pick
   of the iPad audit of Map and Score, 19 Oct): each control's area is 44 pt
   tall, centred on it, as wide as it is (wider for the zoom's − and +).
   Map's rows hold one line of controls each, so no area reaches another
   control. In Score's Layout panel each row is given 44 pt for the same
   reason, the controls drawn as they were; its dials take the whole row's
   height. Map's beat handles are as wide as the gap to the next beat allows
   (positionBeatHandles). Touch screens only, never the Mac app. */
@media (hover: none) and (pointer: coarse) {
  html:not(.drumchart-mac) :is(.av-mac-row .btn, .av-mac-seg button, .av-mac-disclose, .av-zoom-float .btn, .av-beat-bubble .btn,
    .layout-panel :is(.lp-choices .btn, .lp-nudge .btn, .lp-wide, .lp-hide)) { position: relative; }
  html:not(.drumchart-mac) :is(.av-mac-row .btn, .av-mac-seg button, .av-mac-disclose, .av-zoom-float .btn, .av-beat-bubble .btn,
    .layout-panel :is(.lp-choices .btn, .lp-nudge .btn, .lp-wide, .lp-hide))::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
  }
  html:not(.drumchart-mac) .av-zoom-float .btn.icon-btn::after { left: -8px; right: -8px; }
  html:not(.drumchart-mac) .layout-panel .lp-nudge .btn::after { left: -2px; right: -2px; }
  html:not(.drumchart-mac) .av-flag::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 44px; transform: translateY(-50%); }

  html:not(.drumchart-mac) .layout-panel .lp-row { min-height: 44px; padding: 0; }
  /* A finger held on the panel (on − or +, to keep stepping) is not picking
     out its words to copy. */
  html:not(.drumchart-mac) .layout-panel { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  html:not(.drumchart-mac) .layout-panel :is(input, textarea) { -webkit-user-select: text; user-select: text; }
  html:not(.drumchart-mac) .layout-panel .lp-row-stacked .lp-choices { min-height: 44px; align-items: center; }
  html:not(.drumchart-mac) .layout-panel .lp-slider { height: 44px; margin: 0; }
  html:not(.drumchart-mac) .layout-panel :is(.lp-fold-toggle, .lp-toggle) { min-height: 44px; }

  html:not(.drumchart-mac) .av-beat-handle { width: var(--hit, 10px); transform: translateX(calc(var(--hit, 10px) / -2)); }
  html:not(.drumchart-mac) .av-beat-handle.downbeat { width: var(--hit, 14px); transform: translateX(calc(var(--hit, 14px) / -2)); }
  html:not(.drumchart-mac) .av-beat-handle::before { left: calc(50% - 1px); }
  html:not(.drumchart-mac) .av-beat-handle::after { left: calc(50% - 4px); }
  html:not(.drumchart-mac) .av-beat-handle.selected::before { left: calc(50% - 1.5px); }
  html:not(.drumchart-mac) .av-beat-handle.downbeat::before { left: calc(50% - 1.25px); }
  html:not(.drumchart-mac) .av-beat-handle.downbeat::after { left: calc(50% - 5.5px); }

  /* Map's bar spaced as Build's and Score's (11 and its border is their
     12, and under the status bar as theirs are), so with Undo and Redo on
     it its view switch and Play land where theirs do. */
  html:not(.drumchart-mac) .av-bar { padding-left: 11px; padding-right: 11px; padding-top: max(8px, env(safe-area-inset-top)); }
  html:not(.drumchart-mac) .audio-view { padding-top: 0; }
  html:not(.drumchart-mac) .av-bar .bar-group { gap: 8px; }
}

/* An iPad held sideways (Adam, 19 Oct): the recording's dock sits under the
   editor, down the left, rather than across the whole foot of the screen, so
   the chart beside it has the full height. As wide as the editor (46% of
   the window, .editor-pane.build-pane above), and the editor keeps the
   dock's height clear at its foot (--rec-dock-h, set by audio.js), so the
   groove editor has the room it had before. Held upright, or in Score, the
   dock stays across the foot. Touch screens only, never the Mac app. */
@media (hover: none) and (pointer: coarse) and (orientation: landscape) and (min-width: 900px) {
  html:not(.drumchart-mac) body:not(.layout-view):has(.split > .editor-pane.build-pane) #audio-dock {
    position: fixed;
    left: 0;
    bottom: 0;
    z-index: 5;
    width: clamp(392px, 46vw, 720px);
    border-right: 1px solid var(--line);
  }
  html:not(.drumchart-mac) body:not(.layout-view) .split > .editor-pane.build-pane { padding-bottom: calc(16px + var(--rec-dock-h, 0px)); }
}

/* Phones: bigger targets, and the toolbar has to survive a 390px screen. */
@media (max-width: 560px) {
  :root { --tap: 40px; }
  .toolbar { flex-wrap: wrap; gap: 6px; padding: 6px 8px; }
  .toolbar .btn { padding: 6px 9px; }
  .app-title { font-size: 16px; }
  .size-nudge { order: 3; }
  .editor-pane { padding: 8px 8px 30vh; }
  .chart-pane { padding: 8px; }
  .pep-head { flex-wrap: wrap; }
  .pc-summary { flex: 1 0 100%; order: 5; white-space: normal; }
  .ph-tools .icon-btn, .sec-head .icon-btn { min-width: 36px; min-height: 36px; }
  .phrase-add-menu .btn.tiny { min-height: 34px; padding: 5px 9px; }
  .field { font-size: 13px; }
  input, select { min-height: 36px; font-size: 16px; } /* 16px stops iOS zooming on focus */
}

.row { display: flex; align-items: center; gap: 8px; }
.row.wrap { flex-wrap: wrap; }

.field { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-soft); }
.field-label { white-space: nowrap; }

input, select, textarea {
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 5px 7px;
  min-height: 30px;
  max-width: 100%;
}
input:focus, select:focus { outline: 2px solid var(--accent-soft); border-color: var(--accent); }
/* Every menu draws the same chevron, its own rather than the browser's (Adam,
   23 Oct: the arrows on Map's Time and Snap sat against the right edge, as if
   spilling out of the box). Safari puts its arrow hard against the border of a
   select given a border and background of its own, and each browser places it
   somewhere else; this one sits 9px in from the edge and centred top to
   bottom, in the select's own text colour, so it follows every theme, the
   page's pinned palette and the Mac look, and the text stops short of it. Two
   squares each carry one stroke of the V. Rules that set a select's
   background say background-color, which leaves the chevron alone. */
select:not([multiple]):not([size]) {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 22px;
  background-image:
    linear-gradient(45deg, transparent 40%, currentColor 40%, currentColor 62%, transparent 62%),
    linear-gradient(135deg, transparent 38%, currentColor 38%, currentColor 60%, transparent 60%);
  background-size: 5px 5px;
  background-position: right 14px center, right 9px center;
  background-repeat: no-repeat;
}
input.narrow { width: 62px; }
input.wide { width: 100%; }
input[type="number"] { width: 68px; }

/* --- the phrase editor panel ------------------------------------------ */
/* The left pane's only content now: whichever phrase is open. The section
   and phrase list itself lives in the chart pane (see chart.css) — this is
   just the detail behind it. */

.phrase-editor-panel { display: flex; flex-direction: column; gap: 8px; }

.pep-head { display: flex; align-items: center; gap: 8px; min-height: var(--tap); }
/* − ×N + on a touch screen's top line (timesStepper in editor.js). */
.pep-times { display: inline-flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.pep-times .btn { min-width: var(--tap); min-height: var(--tap); padding: 0; font-size: 18px; justify-content: center; }
.pep-times-n { min-width: 36px; text-align: center; font-variant-numeric: tabular-nums; }
.pep-bars-n { min-width: 52px; font-weight: 600; white-space: nowrap; }
/* Label, its field and where it sits, a line under a touch screen's heading
   (labelRow in editor.js). 16px type: iOS zooms into a field set smaller. */
.pep-label { display: flex; align-items: center; gap: 8px; }
.pep-label-name { color: var(--ink-soft); font-size: 13px; flex: 0 0 auto; }
.pep-label-input { flex: 1 1 auto; min-width: 0; min-height: var(--tap); font-size: 16px; }
.pep-label-aligns { display: inline-flex; gap: 2px; flex: 0 0 auto; }
.pep-label-align {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--paper);
  color: var(--ink-soft);
}
.pep-label-align.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
/* A section's bars, typed or stepped (sectionPanel in editor.js). */
.pep-bars-input { width: 4em; min-height: var(--tap); text-align: center; font-size: 16px; font-variant-numeric: tabular-nums; }

.pc-type {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  background: var(--line-soft);
  border-radius: 4px;
  padding: 2px 6px;
  white-space: nowrap;
  flex: 0 0 auto;
}
.pc-summary { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-soft); }

.phrase-editor { display: flex; flex-direction: column; gap: 8px; }
/* Repeats, Plays and Label share one line above the groove editor, and the
   Label box is the one of them that can be narrower at no cost. So on a
   narrow panel it gives ground, down to 140px with its name, rather than let
   the line wrap — a wrapped line is 38px of the panel's height. It is never
   wider than it has always been: the cap is its own natural width, so with
   room to spare nothing looks different. */
.phrase-editor .label-field { flex: 1 1 140px; min-width: 0; max-width: max-content; }
.phrase-editor .label-field input { flex: 1 1 auto; min-width: 0; }
.groove-url { flex: 1 1 auto; min-width: 120px; }

.btn.add { border-style: dashed; color: var(--ink-soft); }
.btn.add:hover { color: var(--ink); border-style: solid; }

.add-section { width: 100%; margin-top: 10px; border-style: dashed; }

/* Drag-to-reorder: the handle is grabbed, the card it belongs to tracks the
   pointer with no transition of its own, and its siblings slide over with one
   so the reorder reads as a single continuous motion. */
.drag-handle { cursor: grab; color: var(--ink-faint); touch-action: none; }

/* --- grid editor ---------------------------------------------------- */

.grid-editor { border: 1px solid var(--line); border-radius: 5px; padding: 8px; background: var(--paper); }
.grid-controls { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 8px; }
.grid-controls label { display: inline-flex; gap: 5px; align-items: center; font-size: 12px; color: var(--ink-soft); }

.grid-view.editable .gcell.editable {
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 0;
  min-height: 24px;
}
.grid-view.editable .gcell.editable:hover { background: var(--accent-soft); }
.grid-view.editable .glabel.wide { width: 118px; flex: 0 0 118px; }
.grid-view.editable .glabel.wide select { width: 100%; font-size: 11px; min-height: 24px; padding: 2px 22px 2px 4px; }
.brush-static { font-size: 11px; color: var(--ink-soft); }

/* --- library -------------------------------------------------------- */

.lib-search { flex: 1 1 160px; max-width: 320px; font-size: 14px; }
.library { flex: 1 1 auto; min-height: 0; overflow: hidden; padding: 14px; display: flex; flex-direction: column; }
.song-table {
  width: 100%;
  background: var(--paper);
  border: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.song-row {
  /* The row's tools: View and ⋯ on All songs; View and × on a set. */
  --tools-w: 196px;
  display: grid;
  grid-template-columns: 1fr 170px var(--tools-w);
  align-items: center;
  column-gap: 8px;
}
/* Put in order by hand: a grip ahead of each title. */
.song-row.manual-row { grid-template-columns: 40px 1fr 170px var(--tools-w); }
/* A portrait iPad: the titles keep the room, the artists give it up. */
@media (max-width: 900px) {
  .song-row { grid-template-columns: 1fr 120px var(--tools-w); }
  .song-row.manual-row { grid-template-columns: 36px 1fr 120px var(--tools-w); }
  .song-row.set-row { grid-template-columns: 64px 1fr 120px var(--tools-w); }
}
.song-table-head { flex: 0 0 auto; background: var(--chrome); border-bottom: 1px solid var(--line); }
/* The header stays put outside this box; only the rows themselves scroll,
   so a long library never pushes the header (or the toolbar above it) off
   screen — see drum-chart-standing-constraints in memory: a chart reader
   never needs to scroll to see the whole picture, and neither should this. */
.song-table-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.song-table-body > .song-row + .song-row { border-top: 1px solid var(--line-soft); }
.song-table-body > .song-row:hover { background: var(--line-soft); }
.col-artist { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.col-title { min-width: 0; }
.song-table-head > * { overflow: hidden; }
.song-table-body .col-artist { padding: 3px 10px; font-size: 13px; }
/* Rows tall enough to hit with a finger: the list is used on an iPad too,
   between songs in a rehearsal. */
.song-open {
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 10px 10px;
  cursor: pointer;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}
.song-open:hover { color: var(--accent); }
/* The star, ahead of the title. Faint until its row is pointed at, so a list
   of songs is not a column of empty stars, and never hidden outright: an iPad
   has no hover to find it with. One that is on is always plain to see. */
.col-title.starred-col { display: flex; align-items: center; }
.song-name { flex: 1 1 auto; min-width: 0; }
.song-star {
  flex: 0 0 auto;
  background: none;
  border: 0;
  padding: 4px 0 4px 10px;
  font-size: 15px;
  line-height: 1;
  color: var(--ink-faint);
  opacity: 0.4;
  cursor: pointer;
}
.song-row:hover .song-star, .song-star:focus-visible { opacity: 1; }
.song-star.on { color: #e2a50c; opacity: 1; }
.starred-col .song-open { padding-left: 8px; }
/* The column's heading starts where the titles do, past the star. */
.song-table-head .starred-col .col-label { padding-left: 33px; }
.song-meta-mobile { display: none; padding: 0 10px 5px; }
.starred-col .song-meta-mobile { padding-left: 8px; }
.song-tools { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; }
.col-tools { display: flex; justify-content: flex-end; }
.view-btn { gap: 5px; font-weight: 600; }
/* On an iPad the chart's name opens it to read (songRow in library.js), so a
   View button beside it would be a second way to do one thing. */
@media (hover: none) and (pointer: coarse) {
  .song-row .view-btn { display: none; }
}
.view-icon { font-size: 9px; color: var(--accent); }
.more-btn { font-size: 16px; padding: 4px 6px; }
.more-btn.open { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* Select (library.js): the whole row picks its song, and a ring where the
   star was fills and numbers it in the order picked, the order it goes into
   a set. Sized and placed so the title starts where it did beside the star. */
.song-table.picking .pick-row { cursor: pointer; user-select: none; -webkit-user-select: none; }
.song-pick {
  flex: 0 0 auto;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  margin-left: 8px;
  padding: 0 4px;
  border: 1.5px solid var(--ink-faint);
  border-radius: 9px;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent-ink);
}
.pick-row.picked .song-pick { background: var(--accent-fill); border-color: var(--accent-fill); }
.song-table-body > .pick-row.picked { background: var(--accent-soft); }

/* Over the list: how many songs it holds, a word on what can be done with
   it, and how it is sorted. */
.list-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  min-height: 32px;
  margin-bottom: 6px;
}
.list-count { font-variant-numeric: tabular-nums; white-space: nowrap; }
.list-hint { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sort-switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
}
.sort-switch-name { padding: 0 6px 0 6px; }
.sort-choice {
  font: inherit;
  font-size: 13px;
  min-height: 26px;
  padding: 2px 10px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
}
.sort-choice:hover { color: var(--ink); background: var(--line-soft); }
.sort-choice.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
/* While picking, the bar is about what is picked: Add to setlist and Cancel
   take the sort's place on the right. */
.select-bar .list-count { font-weight: 600; }
.select-actions { display: inline-flex; gap: 6px; margin-left: auto; }
.row-menu-item:disabled { cursor: default; color: var(--ink-soft); }
.row-menu-item:disabled:hover { background: none; }
.song-list { display: flex; flex-direction: column; min-height: 0; }
.song-list > .song-table { max-height: 100%; }
.library-body > .song-list { flex: 1 1 auto; min-width: 0; align-self: stretch; }

/* A song's own menu, drawn over the page where its ⋯ was (see rowMenuPanel
   in library.js): Add to setlist and the rest, and the sets in its place. */
.row-menu {
  position: fixed;
  z-index: 60;
  min-width: 210px;
  max-width: min(320px, calc(100vw - 16px));
  max-height: min(420px, 70vh);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}
.row-menu-head { padding: 4px 10px 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; font-size: 11px; }
.row-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: var(--tap);
  padding: 4px 10px;
  font: inherit;
  font-size: 14px;
  text-align: left;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.row-menu-item:hover { background: var(--accent-soft); }
.row-menu-item.danger { color: var(--danger); }
.row-menu-tick { flex: 0 0 16px; text-align: center; color: var(--accent); font-weight: 700; }
.row-menu-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-menu-new { border-top: 1px solid var(--line-soft); margin-top: 3px; color: var(--ink-soft); }
/* Add to setlist leads the menu and opens the sets in its place; their
   heading goes back. Each kept apart from what follows by a line. */
.row-menu-sets, .row-menu-back { border-bottom: 1px solid var(--line-soft); margin-bottom: 3px; }
.row-menu-back { font-weight: 600; }
.row-menu-back .row-menu-tick { color: var(--ink-soft); font-size: 17px; font-weight: 400; }
.row-menu-count { flex: 0 0 auto; }
.row-menu-more { flex: 0 0 auto; color: var(--ink-faint); font-size: 17px; line-height: 1; }
.row-menu-empty { margin: 4px 10px 6px; }
.empty { text-align: center; color: var(--ink); margin-top: 18vh; }
/* A library with nothing in it: "+ New chart" in the middle, the one thing to
   do, opening the same two ways in as the one on the bar (Adam, 2026-10-13). */
.empty.first-chart { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.empty.first-chart p { margin: 0; }
.new-song-first > .btn { font-size: 16px; padding: 10px 20px; }
.new-song-first .new-song-menu { left: 50%; right: auto; transform: translateX(-50%); }

/* Export, over the list it is about (library.js): All charts, or a set. */
.export-btn { gap: 5px; }
.export-icon { display: inline-flex; line-height: 0; }

/* The Export panel (exportchoice.js), on the feedback card: a tick for each
   kind of file, its name, and what it will be. */
.export-subject { margin-top: -6px !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.export-choices { display: flex; flex-direction: column; gap: 2px; }
.export-choice {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 10px; border-radius: 6px; cursor: pointer;
}
.export-choice:hover { background: var(--accent-soft); }
.export-choice input { margin: 3px 0 0; width: 18px; height: 18px; flex: 0 0 auto; accent-color: var(--accent-fill); }
.export-choice-words { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.export-choice-words strong { font-weight: 600; }
.export-choice.off { cursor: default; opacity: 0.55; }
.export-choice.off:hover { background: none; }

/* Setlists (library.js): a narrow column of lists beside whichever one is
   showing. Both sit in one centred block, so the song table keeps roughly
   the width it had before the column arrived. */
.library-body {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  max-width: 980px;
  margin: 0 auto;
  display: flex;
  gap: 14px;
}
.library-body > .song-table,
.library-body > .empty,
.set-view { flex: 1 1 auto; min-width: 0; }
.library-body > .song-table { align-self: flex-start; max-height: 100%; }
.set-view { display: flex; flex-direction: column; min-height: 0; gap: 8px; }
.set-view > .song-table { max-height: 100%; }
.set-view > .list-bar { margin-bottom: -2px; }

.set-nav {
  flex: 0 0 180px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow-y: auto;
  min-height: 0;
  align-self: flex-start;
  max-height: 100%;
}
.set-nav-item, .set-nav-new {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: var(--tap);
  padding: 5px 10px;
  font: inherit;
  font-size: 14px;
  text-align: left;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.set-nav-item:hover { background: var(--line-soft); }
.set-nav-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.set-nav-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.set-nav-count { font-size: 12px; font-weight: 400; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.set-nav-head {
  margin: 12px 10px 3px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-faint);
}
.set-nav-new { color: var(--ink-soft); margin-top: 2px; }
.set-nav-new:hover { color: var(--accent); background: none; }

.set-head { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.set-name {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-size: 17px;
  font-weight: 600;
  padding: 3px 8px;
  margin-left: -8px;
  color: var(--ink);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 5px;
}
.set-name:hover { border-color: var(--line); }
.set-name:focus { outline: none; border-color: var(--accent); background: var(--paper); }
.set-count { white-space: nowrap; font-variant-numeric: tabular-nums; }

.set-row { --tools-w: 128px; grid-template-columns: 64px 1fr 170px var(--tools-w); }
.col-pos { display: flex; align-items: center; gap: 2px; padding-left: 4px; }
.set-drag {
  width: 30px;
  height: 30px;
  padding: 0;
  font-size: 15px;
  background: none;
  border: 0;
}
.set-pos { font-size: 13px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
/* Where the grip goes in a set listed A–Z, which cannot be dragged. */
.set-drag-space { display: inline-block; width: 30px; }
.col-label {
  display: block;
  padding: 7px 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-faint);
}
.set-row.song-table-head .col-pos .col-label { padding-left: 34px; }
.set-row { transition: transform .12s ease; }
.set-row.dragging {
  position: relative;
  z-index: 5;
  transition: none;
  background: var(--paper);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
}
.set-row.dragging .set-drag { cursor: grabbing; }

/* "+ Add songs" opens a list to pick from, anchored under the button. */
.set-add { position: relative; display: inline-flex; }
.set-picker {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 320px;
  max-width: calc(100vw - 24px);
  max-height: min(420px, 60vh);
  padding: 8px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}
.set-picker-search { font-size: 14px; }
.set-picker-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.set-picker-song {
  display: grid;
  grid-template-columns: 18px 1fr;
  column-gap: 8px;
  align-items: center;
  width: 100%;
  min-height: var(--tap);
  padding: 4px 8px;
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}
.set-picker-song:hover { background: var(--accent-soft); }
.set-picker-plus { grid-row: span 2; color: var(--accent); font-weight: 600; text-align: center; }
.set-picker-title { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.set-picker-artist { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.set-picker-artist:empty { display: none; }
.set-picker-empty { margin: 10px 8px; }
/* The two ways to start a song, sat above Done with a rule between them and
   the list: adding one that exists and writing one that does not are
   different actions, and should not read as two more rows of the same list. */
.set-picker-new {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding-top: 8px;
  border-top: 1px solid var(--line-soft);
}
.set-picker-foot { display: flex; justify-content: flex-end; }

/* Below 560px there's nowhere to put an artist column; fold it into a second
   line under the title instead, and drop to three grid tracks. */
@media (max-width: 560px) {
  .song-row { grid-template-columns: 1fr var(--tools-w); }
  .song-row.manual-row { grid-template-columns: 40px 1fr var(--tools-w); }
  .song-row.set-row { grid-template-columns: 58px 1fr var(--tools-w); }
  .col-artist { display: none; }
  .song-meta-mobile { display: block; font-size: 11px; }
  .list-hint { display: none; }
}

/* No room for a column of setlists beside the songs on a phone or a portrait
   iPad in split view: they become one row of tabs above them instead. */
@media (max-width: 700px) {
  .library { padding: 10px; }
  .library-body { flex-direction: column; gap: 8px; }
  .set-nav {
    flex: 0 0 auto;
    flex-direction: row;
    align-self: stretch;
    gap: 4px;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
  }
  .set-nav { scrollbar-width: none; }
  .set-nav::-webkit-scrollbar { display: none; }
  .set-nav-head { display: none; }
  .set-nav-item, .set-nav-new { width: auto; flex: 0 0 auto; max-width: 180px; min-height: 30px; font-size: 13px; }
  .set-nav-item { border: 1px solid var(--line); background: var(--paper); border-radius: 20px; }
  .set-nav-item.active { border-color: var(--accent); }
  .set-nav-new { white-space: nowrap; }
  .library-body > .song-table { align-self: stretch; }
  .set-head { flex-wrap: wrap; position: relative; }
  .set-name { flex-basis: 100%; }
  /* The button sits at the left here, so the list hangs from the whole
     header instead of from the button, or it would open off screen. */
  .set-add { position: static; }
  .set-picker { top: 100%; left: 0; right: 0; width: auto; max-width: none; }
}

/* "+ New song" asks how you are starting (library.js). */
.new-song { position: relative; display: inline-flex; }
.new-song-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 290px;
  padding: 6px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}
.new-song-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-height: 52px;
  padding: 8px 12px;
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
.new-song-choice:hover, .new-song-choice:focus { outline: none; background: var(--accent-soft); }
.new-song-choice strong { font-size: 14px; font-weight: 600; }
/* With a setlist on screen: where the new song will land, said over the two
   ways of starting it. A set's name can be any length, so it is cut short
   rather than widening the menu. */
.new-song-into {
  padding: 5px 12px 6px;
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

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

/* One message, in one place on every screen: just above the bottom edge,
   clear of the controls along it (Build's recording strip, Score's Read / PDF
   / PNG), wrapping to a second line rather than being cut off. It takes no
   taps, so whatever is under it keeps working while it shows. */
.status {
  position: fixed;
  left: 50%;
  bottom: calc(max(12px, env(safe-area-inset-bottom)) + 60px);
  transform: translateX(-50%) translateY(8px);
  width: max-content;
  max-width: min(560px, calc(100vw - 32px));
  box-sizing: border-box;
  /* The page's own ink and paper, swapped, so it stands off either theme. */
  background: var(--ink);
  color: var(--paper);
  padding: 8px 14px;
  border-radius: 12px;
  font-size: 13px;
  line-height: 1.35;
  text-align: center;
  white-space: normal;
  overflow-wrap: anywhere;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s, transform 0.15s;
  /* Above the full-screen Map (z-index 60). Below it, every message the Map
     sets — which beat tracker ran, what the drum check did, where bar 1 went —
     was drawn behind the Map and never seen. */
  z-index: 70;
}
.status.visible { opacity: 1; transform: translateX(-50%) translateY(0); }

/* The settings menu: light or dark, and feedback (settingsmenu.js). It wears
   the Mix menu's panel, narrower, with plain rows rather than buttons. */
/* The Export menu wears the same panel as the others, opening upward — it
   hangs off a button on the strip along the bottom of the page. */
.export-cluster { position: relative; display: inline-flex; }
.export-menu {
  top: auto;
  bottom: calc(100% + 8px);
  /* Centred under the button rather than hung off its right edge: the button
     is itself centred on the page, so anything else reads as misaligned. */
  right: auto;
  left: 50%;
  transform: translateX(-50%);
  min-width: 150px;
  gap: 2px;
  padding: 6px;
}
.export-menu .settings-item { justify-content: flex-start; }

/* Feedback, on the song list. Marked out from the buttons either side of it
   without shouting: this is a beta asking to be told what is wrong, and it
   should read as an invitation rather than as another tool. */
.feedback-btn {
  border-color: var(--accent);
  color: var(--accent);
}
.feedback-btn:hover { background: var(--accent-soft); }

.settings-cluster { position: relative; display: inline-flex; align-items: center; gap: 8px; }
.settings-btn { font-size: 18px; line-height: 1; }
/* The menu's three lines (settingsmenu.js), drawn, so every device shows the
   same shape at the same weight. */
.settings-icon { display: inline-flex; line-height: 0; }
.settings-menu { min-width: 232px; gap: 2px; padding: 6px; }
.settings-menu .settings-item {
  justify-content: flex-start;
  text-align: left;
  border-color: transparent;
  background: transparent;
  min-height: 36px;
  font-size: 14px;
  white-space: pre;
}
.settings-menu .settings-item:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }

/* The rule between a chart's (or the list's) errands and the app's own,
   Settings and feedback, in the ≡ menu. */
.settings-rule {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 4px 6px;
}

/* The line on the song list saying these charts exist in one place only (see
   backupNote in library.js). Deliberately not a warning colour: it is true of
   a working library on a working machine, and something that shouts every
   time you add a song stops being read. */
.backup-note {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  /* Lines up with the song table below it, which centres itself at the same
     width — a note about the songs that did not start where they start read
     as a banner belonging to the page rather than to them. */
  width: 100%;
  max-width: 980px;
  margin: 0 auto 12px;
  flex: 0 0 auto;
  padding: 10px 14px;
  background: var(--accent-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 13px;
  color: var(--ink-soft);
}
.backup-note strong { color: var(--ink); font-weight: 600; }
/* Once a device syncs, the backup reminder gives way to this (library.js). */
.sync-note { flex: 0 0 auto; padding: 0 2px 6px; font-size: 12px; color: var(--ink-faint); }
.sync-note.trouble { color: var(--warn); }

/* --- files dragged over the page --------------------------------------- */
/* While a recording or a DrumChart file is held over the window (library.js
   openFiles): the whole page says what letting go does, in the words the
   page put on body. A pseudo-element, so there is nothing to make or take
   away, and the drag sees one surface rather than every box it crosses.
   Above Map, view mode and the status line. The opaque wash is for a
   browser without color-mix; the translucent one is what shows. */
body.dropping-files::after {
  content: attr(data-drop-text);
  position: fixed;
  inset: 0;
  z-index: 1300;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  padding: 24px;
  border: 3px dashed var(--accent);
  background: var(--accent-soft);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

/* Shown only if the songs cannot be opened because an older copy of the app
   is holding them (see openDb in store.js). */
.blocked-note {
  max-width: 440px;
  margin: 18vh auto 0;
  padding: 20px 22px;
  background: var(--raised);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 6px);
  box-shadow: var(--shadow-2);
  font-size: 14px;
  line-height: 1.45;
}
.blocked-note strong { display: block; font-size: 16px; margin-bottom: 6px; }
.blocked-note p { margin: 0; color: var(--ink-soft); }

/* --- feedback ------------------------------------------------------- */
/* The form behind the speech-bubble button (see feedback.js). Built outside
   #app, so the app redrawing underneath never takes what is being typed. */
.feedback-overlay {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.35);
  padding: 16px;
}
.feedback-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 440px;
  max-width: 100%;
  padding: 20px;
  background: var(--raised);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 6px);
  box-shadow: var(--shadow-2);
}
.feedback-title { margin: 0; font-size: 17px; font-weight: 650; }
.feedback-kinds { display: flex; gap: 6px; }
.feedback-kinds .btn { min-width: 64px; }
.feedback-text {
  width: 100%;
  box-sizing: border-box;
  min-height: 120px;
  resize: vertical;
  padding: 10px 12px;
  font: inherit;
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.feedback-text:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.feedback-context { display: flex; gap: 8px; align-items: flex-start; color: var(--ink-soft); }
.feedback-context input { margin-top: 2px; }
.feedback-note { margin: 0; min-height: 1em; }
.feedback-note:empty { display: none; }
.feedback-actions { display: flex; justify-content: flex-end; gap: 8px; }
/* The Connect Dropbox panel (recordings.js) is built from the same card, and
   so is Settings (settingspage.js), whose Mac shape is in mac.css; this is
   enough for it to read anywhere. */
.feedback-card p { margin: 0; }
/* Settings on the website (≡ › Settings…): the Mac look's layout, names in a
   column on the left and a line under each control saying what it does, in
   the website's own controls. */
.settings-card { width: 560px; gap: 0; max-height: calc(100vh - 32px); overflow-y: auto; }
.ms-head { display: flex; align-items: center; justify-content: space-between; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.ms-row { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 14px; padding: 14px 0; border-bottom: 1px solid var(--line); align-items: start; }
.ms-row:last-child { border-bottom: 0; padding-bottom: 0; }
.ms-name { text-align: right; font-size: 13px; font-weight: 600; padding-top: 5px; }
.ms-control { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; min-width: 0; }
.ms-hint { font-size: 12px; line-height: 1.4; color: var(--ink-soft); }
.ms-labelled { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ms-label { font-size: 13px; color: var(--ink-soft); }
.ms-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.ms-strong { font-weight: 600; margin: 2px 0 8px; }
.ms-devices { display: flex; flex-direction: column; }
.ms-device { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.ms-device:last-child { border-bottom: 0; }
.ms-device small { display: block; color: var(--ink-faint); font-size: 12px; }
.ms-device .btn { margin-left: auto; }
.lp-choices.ms-seg { display: inline-flex; flex-wrap: nowrap; }
.lp-choices.ms-seg .btn { flex: 0 0 auto; min-width: 64px; padding: 3px 12px; }
/* The chosen answer, in a neutral tint rather than solid blue: blue is for
   Play, the way into Map and what is playing (Adam, 25 Oct). */
.ms-seg .btn.chosen {
  background: color-mix(in srgb, var(--ink) 14%, transparent);
  border-color: color-mix(in srgb, var(--ink) 34%, transparent);
  font-weight: 600;
}
.ms-close { border-radius: 50%; }
@media (max-width: 520px) {
  .ms-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .ms-name { text-align: left; padding-top: 0; }
}
.dropbox-code {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  font: 600 14px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.dropbox-code:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* --- progress modal --------------------------------------------------- */
/* Find the beat and check-against-drums: too slow, and too worth watching,
   to squeeze into a toolbar line that gets cut off. Centred, above
   everything (including the full-screen Map at z-index 60). */
.progress-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.35);
  padding: 16px;
}
/* display:flex above otherwise beats the browser's own rule for [hidden] —
   an author stylesheet always wins over the UA one at equal specificity —
   so this modal showed on every load, permanently, no matter what state.progress
   said. This is what [hidden] actually needs to work while the class also sets
   its own display. */
.progress-overlay[hidden] { display: none; }
.progress-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 280px;
  max-width: 100%;
  padding: 26px 24px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 6px);
  box-shadow: var(--shadow-2);
  text-align: center;
}
.progress-spinner {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 3px solid var(--line-soft);
  border-top-color: var(--accent);
  animation: progress-spin 0.8s linear infinite;
}
@keyframes progress-spin { to { transform: rotate(360deg); } }
.progress-label { font-size: 14px; color: var(--ink); }
.progress-bar {
  width: 100%;
  height: 6px;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%;
  background: var(--accent-fill);
  border-radius: inherit;
  transition: width 0.15s ease-out;
}
.progress-percent { font-size: 12px; color: var(--ink-faint); }
.progress-hint { font-size: 11px; color: var(--ink-faint); opacity: 0.75; }

/* --- audio dock ----------------------------------------------------- */

#audio-dock {
  flex: 0 0 auto;
  border-top: 1px solid var(--line);
  background: var(--paper);
  padding: 6px 12px;
  padding-bottom: max(6px, env(safe-area-inset-bottom));
}
#audio-dock[hidden] { display: none; }

.audio-bar { display: flex; align-items: center; gap: 8px; }
.au-clock { font-variant-numeric: tabular-nums; white-space: nowrap; min-width: 84px; }
.au-wave {
  flex: 1 1 auto;
  height: 40px;
  min-width: 80px;
  cursor: pointer;
  border-radius: 4px;
  background: var(--wave-bg);
  border: 1px solid var(--line-soft);
}

.audio-panel { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.marker-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.marker-list li {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 2px 4px 2px 2px;
}
.marker-name { font-size: 12px; padding-right: 4px; }

/* the section the recording is currently in */
.chart-section.playing { background: #fff6de; box-shadow: inset 3px 0 0 #d08a1f; }
.phrase.playing { outline: 2px solid #d08a1f; outline-offset: 1px; }

/* --- groove editor --------------------------------------------------- */

.groove-editor {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The groove's notation is the same music the chart prints, so it is drawn on
   the same paper. Left to the theme it came out white-on-black, and inverted
   notation is markedly harder to read than the black-on-white every drummer
   has read since they started — the point of the preview is to check at a
   glance that what you tapped is what you meant. */
.ge-preview {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 5px 2px 7px;
  /* Held at one height, so writing a note never moves the grid below it — the
     drawing itself is framed to a fixed window round the staff (steadyFrame in
     notation.js) and this is that window's height. A groove that somehow
     reaches past the window keeps its proportions and sits smaller inside the
     box rather than pushing the grid down.

     It is not made to give any of that height back when the panel is short of
     room, and fitGrooveEditor leaves it alone: the drawing is exactly as tall
     as this box, so a shorter one slices the stems off top and bottom. A
     panel that scrolls is the better of those two. */
  height: 116px;
  box-sizing: border-box;
  background: var(--page);
  /* Redefining --ink above is not enough on its own: `color` was already
     computed on a dark ancestor, and VexFlow's glyphs are filled with
     currentColor (see .notation svg in chart.css). Without this the staff came
     out white on the white card. */
  color: var(--ink);
  border-radius: 5px;
  overflow-x: auto;
}
/* The drawing arrives wrapped in a .notation div (see draw() in
   notation.js), so `max-height: 100%` on the SVG had nothing definite to
   resolve against and the cap below silently did nothing at all — which is
   how a bar with the band's hits cued over it came to stand taller than this
   window and have its stems sliced off the top edge. The wrapper takes the
   window's height so the cap has something to be a percentage of, and a
   drawing that reaches further than the frame expects letterboxes inside it,
   which is what steadied() in notation.js says is supposed to happen. */
.ge-preview > .notation {
  height: 100%;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ge-preview svg { max-height: 100%; }
.ge-preview-empty { padding: 8px; }

/* One line of controls above the grid: Time, Notes, Bars, Play, Toms, and at
   the far end the cell size and the door to the rest (see the controls in
   grooveeditor.js). It still wraps if it must — a panel at its narrowest
   cannot hold it — but on a laptop's window it is one line, where it was two
   lines on a laptop and three on a narrow panel. The gap and the Bars box are
   as tight as they are for the sake of the last hundred pixels of that; Bars
   only ever holds a number from 1 to 8. */
.ge-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ge-controls label { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-soft); }
.ge-controls select, .ge-controls input { min-height: 30px; font-size: 13px; }
.ge-controls input[type="number"] { width: 48px; }
.ge-controls .ge-zoom { margin-left: auto; gap: 2px; }
.ge-controls .ge-zoom .btn { min-width: 28px; padding-left: 4px; padding-right: 4px; font-size: 15px; line-height: 1; }
.ge-controls .ge-more { min-width: 30px; font-size: 16px; line-height: 1; }
.ge-more.menu-open { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }

/* What is behind the door. It is the groove editor's own menu, so it is put
   on the screen the way a row's or a cell's is (placeGrooveMenu), but it holds
   settings rather than a list of things to do — so it is dressed as the Mix
   menu on the bar above, which is the same kind of thing. */
.ge-menu.ge-settings {
  min-width: 200px;
  gap: 12px;
  padding: 13px;
  background: var(--raised);
  border-radius: calc(var(--radius) + 3px);
  box-shadow: var(--shadow-2);
}
.ge-settings select, .ge-settings input { min-height: 30px; font-size: 13px; }
.ge-settings .ge-swing { display: flex; }
.ge-settings .ge-swing select { flex: 1 1 auto; }
.ge-swing.disabled { opacity: 0.45; }
.ge-zoom { display: inline-flex; align-items: center; gap: 4px; }

/* A wider-than-one-bar groove (or a high zoom level, see .ge-zoom) can need
   more width than the panel has — this is what lets it scroll rather than
   crush every cell to fit. All rows share one scrolling box, so they stay
   aligned with each other as it scrolls; the label column is pinned to the
   left edge of it (position: sticky) so it never scrolls out of view along
   with the cells it names. */
.ge-grid {
  display: flex; flex-direction: column; gap: 2px; overflow-x: auto;
  /* No scrollbar: it sat over the count row until it faded (Adam, 23 Oct
     2026). A trackpad, or shift and a wheel, still scrolls it, and the
     arrows at its edges (.ge-scroll) take a mouse a bar at a time. */
  scrollbar-width: none;
  /* Column widths are measured against this box (see cellColumns in
     grooveeditor.js); --ge-label-room is what the row name, the gap after it
     and the two edge lines take out of it. */
  container-type: inline-size;
  --ge-label-room: 90px;
  /* The row height this size of panel would like. fitGrooveEditor reads it
     and comes down from it when the panel is short of room. */
  --ge-cell-full: 30px;
}
.ge-grid::-webkit-scrollbar { display: none; }
.ge-grid-wrap { position: relative; min-width: 0; }
.ge-scroll {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 26px; height: 40px; padding: 0; border: 1px solid var(--line); border-radius: 8px;
  background: color-mix(in srgb, var(--raised) 88%, transparent); color: var(--ink);
  font: 600 20px/1 inherit; cursor: pointer; box-shadow: var(--shadow-1, 0 1px 3px rgba(0, 0, 0, 0.3));
}
.ge-scroll:not(.shown) { display: none; }
.ge-scroll-next { right: 2px; }
.ge-scroll:hover { background: var(--raised); }

/* A row is as long as its cells, not as wide as the panel. The name is pinned
   inside its own row, so a row that stopped at the panel's edge let go of the
   name once the grid had been scrolled about a panel's width — on a long or
   zoomed-in groove, well before the end — and the cells further along had
   nothing beside them to say which drum they were. */
.ge-row { display: flex; align-items: stretch; gap: 6px; position: relative; width: max-content; min-width: 100%; }

.ge-label {
  position: sticky;
  left: 0;
  z-index: 2;
  flex: 0 0 78px;
  width: 78px;
  /* "Hi-hat / cymbal" takes two lines in this column, which at the full row
     height costs nothing — but once the rows are shrunk to make the panel fit
     (fitGrooveEditor) it would be the name, not the cells, deciding how tall
     a row is, and shrinking the cells further would buy nothing. So the name
     comes down with them. */
  font-weight: 600;
  font-family: inherit;
  /* Longhands, not the `font` shorthand: a var() inside the shorthand makes
     the whole declaration invalid at computed-value time in some browsers,
     and the label then falls back to the pane's own much larger text. */
  font-size: clamp(9px, calc(var(--ge-cell-h, 30px) * 0.37), 11px);
  line-height: 1.1;
  text-align: left;
  color: var(--ink-soft);
  background: var(--paper);
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 2px 4px;
  cursor: pointer;
}
.ge-label:hover { background: var(--line-soft); border-color: var(--line); }
.ge-label.static { cursor: default; }
.ge-label.static:hover { background: none; border-color: transparent; }

.ge-cells {
  display: grid;
  flex: 0 0 auto;
  border-left: 2px solid var(--ink);
  border-right: 2px solid var(--ink);
  min-width: 0;
  position: relative;
}

.ge-cell {
  position: relative;
  min-width: 0;
  /* The row height the panel actually has room for, written on .ge-grid by
     fitGrooveEditor (grooveeditor.js). 30px is what it wants; it comes down
     from there, never past a size you can still hit, rather than letting the
     panel run off the bottom and be scrolled. */
  height: var(--ge-cell-h, 30px);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  /* Each cell draws only the line on its left, as a shadow that takes no
     room. Borders drew the beat's line beside the line of the cell before
     it, two lines where one was meant, and a barline's 2px moved every cell
     after it, so Hits and Play time stopped lining up with the drums under
     them (Adam, 15 Oct). */
  box-shadow: inset 1px 0 0 var(--line-soft);
  background: transparent;
  cursor: pointer;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.ge-cell.beat { box-shadow: inset 1px 0 0 var(--line); background: var(--cell-beat); }
.ge-cell.barline { box-shadow: inset 2px 0 0 var(--ink); }
/* The row's own edge is the line before its first cell. */
.ge-cell:first-child, .ge-row.counts .ge-cell { box-shadow: none; }
.ge-cell:hover { background: var(--accent-soft); }
.ge-cell.on { color: var(--cell-on); }
.ge-cell.w-ghost .glyph { font-size: 10px; opacity: 0.55; }
.ge-cell.w-accent .glyph { font-weight: 800; }
.ge-cell.menu-open { background: var(--accent-soft); }
.ge-cell .accent-mark {
  position: absolute;
  top: 0px;
  right: 2px;
  font-size: 9px;
  font-weight: 800;
  line-height: 1;
  color: var(--cell-on);
}

.ge-row.counts .ge-cell,
.ge-count {
  height: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.ge-count.beat { color: var(--ink-soft); font-weight: 700; }
.ge-row.counts .ge-cells { border-color: transparent; }

/* Crescendos are drawn along the count line (see startHairpin in
   grooveeditor.js), so its cells take a drag — including a finger's, which
   everywhere else on the grid scrolls it. */
.ge-row.counts .ge-count { cursor: crosshair; touch-action: none; user-select: none; -webkit-user-select: none; position: relative; z-index: 1; }
.ge-row.counts .ge-count:hover,
.ge-row.counts .ge-count.drawing { background: var(--accent-soft); }
.ge-row.counts .ge-count.under-hairpin { color: var(--cell-on); cursor: pointer; }
.ge-row.counts .ge-count.has-mark { color: var(--cell-on); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.ge-menu-marks { display: flex; gap: 4px; padding: 4px 10px 8px; }
.ge-menu-mark { font-family: Georgia, 'Times New Roman', serif; font-style: italic; font-weight: 700; min-width: 34px; padding: 6px 0; border-radius: 6px; border: 1px solid var(--line, #3a3d46); background: transparent; color: inherit; cursor: pointer; }
.ge-menu-mark.active { background: var(--cell-on); color: #000; }
/* A beat's number, where a tap on it asks how that beat is cut (cutBeat in
   grooveeditor.js) — a triplet in a bar of sixteenths. A pointer rather than
   the crosshair, since the tap is what it is for; the drag still works. */
.ge-row.counts .ge-count.asks { cursor: pointer; }
.ge-row.counts .ge-count.menu-open { background: var(--accent-soft); color: var(--cell-on); }
/* The row's name, cut down to the count line's own height so that naming it
   costs the panel nothing. */
.ge-label.ge-dynamics { font-size: 9px; line-height: 12px; padding: 0 4px; height: 14px; }
.ge-menu-note { font-size: 11px; color: var(--ink-faint); padding: 4px 8px 5px; white-space: nowrap; }
.ge-menu-item:disabled { opacity: 0.4; cursor: default; }
/* The wedge itself: two lines from a point to the full height of the row,
   whatever width the hairpin runs to. */
.ge-hairpin {
  pointer-events: none;
  align-self: stretch;
  margin: 0 2px;
  background:
    linear-gradient(to bottom right, transparent calc(50% - 0.7px), var(--cell-on) calc(50% - 0.7px), var(--cell-on) calc(50% + 0.7px), transparent calc(50% + 0.7px)) top / 100% 50% no-repeat,
    linear-gradient(to top right, transparent calc(50% - 0.7px), var(--cell-on) calc(50% - 0.7px), var(--cell-on) calc(50% + 0.7px), transparent calc(50% + 0.7px)) bottom / 100% 50% no-repeat;
  opacity: 0.55;
}
.ge-hairpin.dim { transform: scaleX(-1); }

/* the long-press / right-click menu, placed on the screen by placeGrooveMenu */
.ge-menu {
  position: fixed;
  z-index: 50;
  top: 0;
  left: 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16);
  padding: 3px;
  display: flex;
  flex-direction: column;
  min-width: 132px;
  text-align: left;
}
.ge-menu-item {
  display: flex;
  align-items: center;
  gap: 7px;
  font: inherit;
  font-size: 12px;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 4px;
  padding: 6px 8px;
  cursor: pointer;
  white-space: nowrap;
  color: var(--ink);
}
.ge-menu-item:hover { background: var(--accent-soft); }
.ge-menu-item.danger { color: var(--danger); }
.ge-menu-item.active { color: var(--cell-on); font-weight: 700; }
.ge-menu-glyph { width: 14px; text-align: center; }

.groove-import { margin-top: 2px; }
.groove-import summary {
  font-size: 12px;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 4px 0;
}
.groove-import[open] summary { margin-bottom: 6px; }

@media (max-width: 560px) {
  .ge-label {
    flex-basis: 58px;
    width: 58px;
    font-size: clamp(8px, calc(var(--ge-cell-h, 34px) * 0.3), 10px);
  }
  .ge-grid { --ge-label-room: 70px; --ge-cell-full: 34px; }
}

/* play-time slashes */
.ge-row.slashes { margin-bottom: 2px; }
.ge-row.slashes .ge-cells { border-color: var(--line); }
.ge-cell.slash { height: 22px; font-size: 15px; color: var(--ink-faint); }
.ge-cell.slash.on { color: var(--cell-on); background: var(--cell-slash-on); }
.ge-cell.under-slash { opacity: 0.32; }

/* --- the recording workspace ----------------------------------------- */

body.audio-view-open { overflow: hidden; }

.audio-view {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: var(--chrome);
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* No inset above the bar: it sits flush at the top of the screen the way
     Build's and Layout's do, so the view switch on it lands at the same height
     as theirs rather than nine pixels lower. The safe-area inset is still
     honoured for a notched screen. */
  padding: env(safe-area-inset-top) max(12px, env(safe-area-inset-right))
           max(10px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
}

/* The song's identity sits on the bar now rather than in a title row of its
   own, so Map has one band of chrome above the waveform instead of two. */
/* The song's name is context, not a control, so it is the first thing to give
   way when the bar is tight — capped, and shrinking to an ellipsis before
   anything you can press has to wrap to a second row. */
.av-song {
  /* Stacked, so the title gets the whole width instead of sharing a line —
     "Whipping Post (Lyric Video)" was being cut to "Whipping Post (Lyric..."
     at 150px with the file name sitting next to it. Two short lines cost the
     bar no height: it is already as tall as the buttons on it. */
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  min-width: 0;
  max-width: 280px;
  flex: 0 1 auto;
  line-height: 1.2;
}
.av-song > * { min-width: 0; }

/* As the bar narrows, context goes before controls do: the artist first, then
   the song's name — you know which song you opened. Nothing you can press
   disappears, and the bar stays one row, because every row it grows comes off
   the waveform. The bar carries far less than it did, so both survive much
   further down than they used to. */
@media (max-width: 1100px) {
  .av-song-artist { display: none; }
}
@media (max-width: 920px) {
  .av-song { display: none; }
}
.av-song-title {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The second line: who plays it, and where you are in it. One line, so it
   reads as one thing and the eye has a single left edge to come back to. */
.av-song-under {
  display: flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
  font-size: 11.5px;
}
.av-song-artist { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.av-song-dot { flex: 0 0 auto; opacity: 0.55; }

/* Map's one bar. It used to wrap to three rows the moment a beat grid
   existed, and every row it grew came off the waveform; there is little enough
   on it now that it stays one row down to about 900px.
 
   It must not be a scroll container: the speed and A-B popovers hang off
   buttons inside it, and any overflow other than visible clips them. So it
   wraps as a last resort on a window narrower than the bar instead — nothing
   ends up unreachable, and on any screen this app is used on it does not
   come to that. */
.av-bar {
  /* Three columns, same as .toolbar — declared here rather than in the shared
     block above because this rule comes after it and would otherwise win with
     its own display. minmax(0, …) for the reason given there. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  position: relative;
  gap: 6px;
  flex: 0 0 auto;
  overflow: visible;
  background: var(--paper);
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius);
  /* Matches .toolbar's, so the switch sits at the same height as it does on
     every other screen. */
  padding: 8px 10px;
  margin: 0 -12px;
}
.av-bar .av-clock { min-width: 0; flex: 0 0 auto; font-size: inherit; color: inherit; }

.av-zoom { display: inline-flex; align-items: center; gap: 2px; flex: 0 0 auto; }

/* Speed and the A-B loop: used, but not on every pass. */


/* The meter control sits with the palette, because a meter change starting at
   the playhead is one of the things you drop there. */
/* The grid's status, and the door to everything about it. */
.av-grid-chip { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.av-grid-chip.open { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.av-grid-chip-text { font-variant-numeric: tabular-nums; }
.av-grid-chev { color: var(--ink-faint); font-size: 10px; }

/* The sections' button, at the head of the marking row. */
.av-sections-toggle { flex: 0 0 auto; }
.av-sections-label { margin-left: 5px; }

.av-fix-toggle {
  align-self: flex-start;
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-soft);
  background: none;
  border: 0;
  padding: 2px 0;
  cursor: pointer;
  display: inline-flex;
  gap: 6px;
  align-items: center;
}
.av-fix-toggle:hover { color: var(--ink); }
.av-fix {
  border-top: 1px solid var(--line-soft);
  padding-top: 9px;
  gap: 18px;
}
.av-fix-group { display: inline-flex; align-items: center; gap: 4px; }
.av-fix-label {
  font-size: 11px;
  color: var(--ink-faint);
  margin-right: 3px;
  white-space: nowrap;
}
.av-clock { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--ink-soft); min-width: 96px; }

/* Zoom, sitting on the waveform it magnifies rather than on the bar. Held off
   the bottom-right corner the way a map holds its own, translucent so it never
   hides a stretch of the recording outright, and out of the way of the beat
   handles and flags, which live at the top. */
.av-zoom-float {
  position: absolute;
  right: 10px;
  bottom: 10px;
  z-index: 6;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--raised) 86%, transparent);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(6px);
}
.av-zoom-float .btn { min-height: 28px; min-width: 28px; }
.av-zoom-float .av-zoom-value { min-width: 40px; font-size: 12px; }

.av-zoom {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--ink-soft);
}
.av-zoom-value { font-variant-numeric: tabular-nums; min-width: 34px; text-align: center; color: var(--ink); }
.btn.small.set { border-color: var(--accent); color: var(--accent); }

.av-body {
  flex: 1 1 auto;
  display: flex;
  gap: 8px;
  min-height: 0;
}
.av-wave-col {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.av-sections {
  flex: 0 0 180px;
  width: 180px;
  overflow-y: auto;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 4px;
}
.av-sections-empty { margin: 8px 6px; }
.av-section-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.av-section-name {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: none;
  font: inherit;
  font-weight: 600;
  color: inherit;
  padding: 2px 3px;
  border-radius: 3px;
  /* This sidebar stays compact — a long name is clipped with an ellipsis
     here, unlike the chart in Build, which wraps it onto a second row (see
     textarea.sec-name in chart.css). A textarea wraps and scrolls by
     default, so both have to be turned off for the ellipsis to show. */
  resize: none; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.av-section-name:hover, .av-section-name:focus { outline: none; background: var(--wash); }

/* The section name field's ▾ and the menu of names it opens (sectionlabel.js). */
.name-field-toggle {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: none;
  color: var(--ink-soft);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.45;
}
.name-field:hover .name-field-toggle,
.name-field:focus-within .name-field-toggle,
.name-field-toggle:focus-visible { opacity: 1; }
.name-field-toggle:hover { background: var(--wash); color: var(--accent); }
@media (hover: none) { .name-field-toggle { opacity: 0.8; width: 28px; height: 28px; } }
.name-menu {
  position: fixed;
  z-index: 80;
  display: flex;
  flex-direction: column;
  min-width: 140px;
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  padding: 4px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  text-transform: none;
  letter-spacing: normal;
}
.name-menu-item {
  text-align: left;
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
  border: none;
  border-radius: 4px;
  background: none;
  color: inherit;
  cursor: pointer;
}
.name-menu-item:hover, .name-menu-item:focus { outline: none; background: var(--accent-soft); }
.name-menu-item.current { font-weight: 600; color: var(--accent); }

.av-overview-wrap {
  position: relative;
  flex: 0 0 54px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  touch-action: none;
}
.av-overview { display: block; width: 100%; height: 100%; }
.av-viewport {
  position: absolute;
  top: 0;
  bottom: 0;
  border: 2px solid var(--accent);
  background: var(--wash);
  pointer-events: none;
  border-radius: 3px;
}

.av-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 160px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: crosshair;
  touch-action: none;
}
.av-wave { display: block; width: 100%; height: 100%; }

/* Above the beat handles (3) and the playhead's grip (4): the flags and the
   time-signature chips sat under them, so a chip was clickable only where
   no handle crossed it, and the chip's pop-up (From the start) was covered
   by the playhead and the beats, which took the hover and the clicks
   (Adam, 20 Oct, in the Mac app). Below a flag's row the handles and the
   grip are as reachable as before. */
.av-flags { position: absolute; inset: 0; pointer-events: none; z-index: 5; }
.av-flag {
  position: absolute;
  top: 6px;
  /* set per flag by positionFlags, which deals overlapping ones into rows */
  transform: translateX(-2px);
  display: flex;
  align-items: center;
  gap: 5px;
  background: #14161a;
  color: #fff;
  border-radius: 4px;
  padding: 3px 6px;
  font-size: 11px;
  white-space: nowrap;
  pointer-events: auto;
  cursor: grab;
  touch-action: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}
.av-flag:active { cursor: grabbing; }
.av-flag-name { font-weight: 600; }
.av-flag-time { opacity: 0.65; font-variant-numeric: tabular-nums; }
.av-flag-x {
  background: none;
  border: 0;
  color: #fff;
  opacity: 0.6;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0 1px;
}
.av-flag-x:hover { opacity: 1; }

/* A section the chart places but nobody has marked (impliedFlagFor in
   audioview.js): outlined and dashed, quieter than a marker, not draggable,
   with Pin to make it one. Its line on the waveform is dashed too. */
.av-flag-implied {
  background: var(--wave-bg, #fff);
  color: var(--ink);
  border: 1px dashed var(--ink-soft);
  box-shadow: none;
  cursor: default;
  opacity: 0.92;
}
.av-flag-implied .av-flag-time { opacity: 0.6; }
.av-flag-pin {
  background: none;
  border: 1px solid currentColor;
  border-radius: 3px;
  color: inherit;
  font: 600 10px system-ui, sans-serif;
  padding: 0 4px;
  line-height: 14px;
  cursor: pointer;
  opacity: 0.75;
}
.av-flag-pin:hover { opacity: 1; }
.av-msection.implied .av-msection-bars { font-style: italic; }
.av-msection-from { font-size: 11px; font-style: italic; color: var(--ink-faint); white-space: nowrap; }

/* A time signature on the Map (see meterChip in audioview.js) — not
   draggable, so no grab cursor, and the palette's --warn to tell it from a
   section. The chip is just the signature; the rest is in its panel. */
.av-flag-meter { background: var(--warn); cursor: default; padding: 0; }
.av-meter-chip {
  font: inherit;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  background: none;
  border: 0;
  padding: 3px 7px;
  cursor: pointer;
  border-radius: 4px;
}
.av-meter-chip:hover, .av-flag-meter.open .av-meter-chip { background: rgba(255, 255, 255, 0.18); }
/* The Map is counting a different bar from the song's signature. */
.av-flag-unsure { box-shadow: 0 0 0 2px var(--paper), 0 0 0 3px var(--warn); }
/* A hold on the bar lines after a hand-edited beat: nobody's choice of
   meter, so a thin pin rather than a label. */
.av-flag-hold { padding: 0 1px; opacity: 0.55; }
.av-flag-hold:hover { opacity: 1; }
/* Above every other flag, whatever row it was dealt into. */
.av-flag-meter.open { z-index: 50 !important; }

.av-meter-pop {
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  width: 236px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 11px;
  background: var(--raised);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 3px);
  box-shadow: var(--shadow-2);
  white-space: normal;
  cursor: default;
}
.av-meter-pop-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.av-meter-pop-note { margin: 0; font-size: 12px; line-height: 1.35; color: var(--ink-soft); }
.av-meter-sigs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.av-meter-sigs .btn { min-width: 0; padding: 3px 0; font-variant-numeric: tabular-nums; }
.av-meter-beat { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.av-meter-pop-label { font-size: 12px; color: var(--ink-soft); width: 100%; }
.av-meter-remove { align-self: flex-start; }

/* Above section flags (z-index 2) — in Edit beats mode a beat under a flag
   should still be the thing a click lands on, not the flag sitting near it. */
.av-beat-handles { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.av-beat-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 10px;
  transform: translateX(-5px);
  cursor: grab;
  touch-action: none;
  pointer-events: auto;
}
.av-beat-handle::before {
  content: '';
  position: absolute;
  left: 4px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: #1f5fd0;
  opacity: 0.5;
}
.av-beat-handle::after {
  content: '';
  position: absolute;
  left: 1px;
  top: 2px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #1f5fd0;
}
.av-beat-handle:hover::before { opacity: 0.9; }
.av-beat-handle:active { cursor: grabbing; }
.av-beat-handle:active::before { opacity: 1; }

/* Downbeats warp the grid instead of moving one beat, so they get their own
   colour and a bigger grip — the thing worth reaching for first. */
.av-beat-handle.downbeat { z-index: 1; width: 14px; transform: translateX(-7px); }
.av-beat-handle.downbeat::before { left: 6px; width: 2.5px; background: #b3641e; opacity: 0.7; }
.av-beat-handle.downbeat::after { left: 2px; width: 11px; height: 11px; background: #b3641e; }
.av-beat-handle.downbeat:hover::before, .av-beat-handle.downbeat:active::before { opacity: 1; }

/* A tapped beat: full strength, and a bubble above it with what can be done
   to it (Delete; Freeze on a downbeat). The bubble is only there while a beat
   is selected, so it takes no room otherwise. */
.av-beat-handle.selected::before { opacity: 1; width: 3px; }
.av-beat-handle.selected::after { box-shadow: 0 0 0 3px var(--wash-firm); }
.av-beat-bubble {
  position: absolute;
  top: 16px;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  padding: 4px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 7px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  pointer-events: auto;
  z-index: 4;
  white-space: nowrap;
}

/* A frozen downbeat can't be dragged — click it to unfreeze rather than a
   separate control, so solid and dark (not faded) is what "click again to
   undo this" looks like here, matching the flags' own dark, definite fill,
   rather than reading as disabled or faded out. */
.av-beat-handle.downbeat.frozen { cursor: pointer; }
.av-beat-handle.downbeat.frozen::before { background: #14161a; opacity: 0.85; }
.av-beat-handle.downbeat.frozen::after {
  background: #14161a;
  opacity: 1;
  box-shadow: 0 0 0 3px rgba(20, 22, 26, 0.22);
}

/* The playhead's own grip, separate from the beat grid's (above) — a drag
   here always moves the playhead, whatever a plain drag on the stage itself
   currently means (pan on a touch, scrub for a mouse). Above the beat
   handles (z-index 4 vs 3) since it is the one you reach for most and a beat
   sitting right at the playhead should never steal the touch. The triangle
   flag at the top is the grabbable part; the hairline continues the red the
   canvas already draws the playhead in, so the two read as one mark. */
.av-playhead-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 22px;
  transform: translateX(-11px);
  cursor: grab;
  touch-action: none;
  pointer-events: auto;
  z-index: 4;
}
.av-playhead-handle::before {
  content: '';
  position: absolute;
  left: 10px;
  top: 0;
  bottom: 0;
  width: 2px;
  background: #b3261e;
  opacity: 0;
}
.av-playhead-handle::after {
  content: '';
  position: absolute;
  left: 3px;
  top: 0;
  width: 16px;
  height: 12px;
  background: #b3261e;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}
.av-playhead-handle:hover::before,
.av-playhead-handle:active::before { opacity: 0.6; }
.av-playhead-handle:active { cursor: grabbing; }

.av-markbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  flex: 0 0 auto;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 8px 10px;
}
.btn.big-mark { font-size: 15px; padding: 10px 16px; min-height: 44px; }
.av-queue { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.av-queue-item {
  font-size: 11px;
  background: var(--line-soft);
  color: var(--ink-soft);
  border-radius: 20px;
  padding: 3px 9px;
}
.av-queue-item.more { background: transparent; }
.av-follow { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-soft); }

@media (max-width: 700px) {
  .av-bar { gap: 6px; padding: 5px 8px; }
}

/* the marker palette */
.av-mark-label { font-size: 12px; color: var(--ink-soft); white-space: nowrap; }
.av-palette { display: flex; gap: 5px; flex-wrap: wrap; flex: 1 1 auto; }
.btn.mark-kind { font-size: 12px; padding: 6px 11px; min-height: 36px; }

/* the dock can be dragged taller */
/* The dock is a column so the bar — and the waveform inside it — grow when the
   dock is dragged taller. */
#audio-dock { display: flex; flex-direction: column; min-height: 0; }
#audio-dock .audio-bar { flex: 1 1 auto; align-items: stretch; min-height: 0; }
#audio-dock .audio-bar > .btn,
#audio-dock .audio-bar > .au-clock { align-self: center; }
#audio-dock .audio-panel { flex: 0 0 auto; }
.au-grip {
  flex: 0 0 auto;
  height: 9px;
  margin: -2px 0 2px;
  cursor: ns-resize;
  position: relative;
  touch-action: none;
}
.au-grip::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 3px;
  transform: translateX(-50%);
  width: 42px;
  height: 3px;
  border-radius: 3px;
  background: var(--line);
}
.au-grip:hover::before { background: var(--ink-faint); }
/* The waveform takes whatever height the dock has been dragged to. Note the
   bar must be allowed to grow — an earlier rule pinning it to flex:0 0 auto is
   what stopped the waveform following the drag. */
#audio-dock .au-wave { flex: 1 1 auto; height: auto; min-height: 40px; }

.av-markbar.naming { outline: 2px solid var(--accent); outline-offset: -2px; }
.btn.mark-kind.danger { color: var(--danger); border-color: var(--danger-line); }

/* Map laid out as the Mac's design C in the website's own look: on a touch
   screen since the iPad audit (19 Oct), on the website since Adam chose it
   blind there (21 Oct). The sections down the left from the start, and
   along the foot the beat grid as one slim row (Fix it by hand a second one
   only while open) and the marking as one row. The same cards, buttons and
   colours as the rest of the website. Never the Mac app (mac-map.css draws
   it there). */
html:not(.drumchart-mac) .av-mac-sidebar {
  flex: 0 0 200px;
  width: 200px;
  padding: 10px 0 12px;
}
html:not(.drumchart-mac) .av-mac-cap {
  padding: 0 14px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
html:not(.drumchart-mac) .av-mac-sidebar .av-section-list { gap: 2px; }
html:not(.drumchart-mac) .av-msection {
  margin: 0 6px;
  padding: 5px 10px;
  min-height: 46px;
  box-sizing: border-box;
  border-radius: 8px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
}
html:not(.drumchart-mac) .av-msection-text { display: flex; flex-direction: column; min-width: 0; }
html:not(.drumchart-mac) .av-msection-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
html:not(.drumchart-mac) :is(.av-msection-bars, .av-msection-time) {
  font-size: 12px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
html:not(.drumchart-mac) .av-msection.at { background: var(--accent-soft); }
html:not(.drumchart-mac) .av-msection.at :is(.av-msection-name, .av-msection-time) { color: var(--accent); }
html:not(.drumchart-mac) .av-msection .name-field { display: flex; align-items: center; min-width: 0; }
html:not(.drumchart-mac) .av-msection .av-section-name { font-size: 14px; background: var(--wash); margin-left: -3px; }
html:not(.drumchart-mac) .av-mac-hint { margin: 12px 14px 0; font-size: 12px; line-height: 1.45; color: var(--ink-faint); }
html:not(.drumchart-mac) .av-mac-sidebar .av-sections-empty { margin: 6px 14px 0; font-size: 14px; color: var(--ink-soft); }

html:not(.drumchart-mac) .av-mac-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
}
html:not(.drumchart-mac) .audio-view > :is(.av-mac-beat, .av-mac-beatrow) {
  flex: 0 0 auto;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 10px;
}
html:not(.drumchart-mac) .av-mac-beat { display: flex; flex-direction: column; }
html:not(.drumchart-mac) .av-mac-fixrow {
  gap: 18px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--line-soft);
}
html:not(.drumchart-mac) .av-mac-ico { display: inline-flex; align-items: center; color: var(--ink-soft); }
html:not(.drumchart-mac) .btn .av-mac-ico { color: inherit; margin-right: 5px; }
html:not(.drumchart-mac) .av-mac-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
html:not(.drumchart-mac) :is(.av-mac-state, .av-mac-label, .av-mac-note) { font-size: 12px; color: var(--ink-soft); }
html:not(.drumchart-mac) .av-mac-note { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
html:not(.drumchart-mac) .av-mac-sep { flex: 0 0 auto; width: 1px; height: 18px; background: var(--line); }
html:not(.drumchart-mac) .av-mac-row .spacer { flex: 1 1 auto; }
html:not(.drumchart-mac) .av-mac-seg {
  display: inline-flex;
  padding: 2px;
  border-radius: 8px;
  background: var(--line-soft);
}
html:not(.drumchart-mac) .av-mac-seg button {
  appearance: none;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  color: var(--ink-soft);
  min-height: 26px;
  padding: 0 12px;
  border-radius: 6px;
}
html:not(.drumchart-mac) .av-mac-seg button.on { background: var(--raised); color: var(--ink); box-shadow: var(--shadow-1); }
html:not(.drumchart-mac) .av-mac-disclose { align-self: center; color: var(--ink); gap: 4px; }
html:not(.drumchart-mac) .av-mac-chev { display: inline-flex; color: var(--ink-soft); transition: transform 0.15s ease; }
html:not(.drumchart-mac) .av-mac-disclose.open .av-mac-chev { transform: rotate(90deg); }

/* "Time 4/4 ▾" (macTimeButton): the song's signature, amber as its chip
   on the waveform, plainly a button with a menu, as on the Mac. */
html:not(.drumchart-mac) .av-mac-time { display: inline-flex; align-items: center; gap: 6px; }
html:not(.drumchart-mac) .av-mac-time-btn { display: inline-flex; align-items: center; gap: 3px; color: var(--warn); font-weight: 600; }
html:not(.drumchart-mac) .av-mac-time-btn.unsure { box-shadow: 0 0 0 1.5px var(--warn); }
html:not(.drumchart-mac) .av-markbar.av-mac-markrow { flex-wrap: nowrap; gap: 8px; padding: 6px 10px; }
html:not(.drumchart-mac) .av-mac-markrow .av-palette { flex: 0 1 auto; flex-wrap: nowrap; gap: 5px; min-width: 0; }
html:not(.drumchart-mac) .av-mac-markrow .av-mark-time { color: var(--ink); font-variant-numeric: tabular-nums; }
html:not(.drumchart-mac) :is(.av-mac-more, .av-mac-trim, .av-mac-timechange) { display: inline-flex; align-items: center; gap: 3px; }
html:not(.drumchart-mac) :is(.av-mac-popup, .av-mac-plus) { display: inline-flex; }
html:not(.drumchart-mac) .av-mac-plus { color: var(--warn); }
html:not(.drumchart-mac) .av-mac-timechange.primary .av-mac-plus { color: inherit; }
html:not(.drumchart-mac) .av-mac-sidebar-btn { padding: 0 8px; min-width: 36px; }
html:not(.drumchart-mac) .av-mac-sidebar-btn svg { display: block; }
html:not(.drumchart-mac) .av-mac-sidebar-btn.on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
/* The website's (audioview.js webRows): the same, sized for a
   pointer, with the website's own controls in the rows (its menus, its
   ☰ Sections, its + Time change), and More lit as a name is. */
html:not(.drumchart-mac) .av-web-sidebar { flex-basis: 220px; width: 220px; }
html:not(.drumchart-mac) .av-web-sidebar .av-msection { min-height: 38px; cursor: pointer; }
html:not(.drumchart-mac) .av-web-sidebar .av-msection:not(.at):hover { background: var(--wash); }
html:not(.drumchart-mac) .av-web-sidebar .av-msection-name { font-size: 13px; }
html:not(.drumchart-mac) .av-web-sidebar :is(.av-msection-bars, .av-msection-time) { font-size: 11.5px; }
html:not(.drumchart-mac) .av-web-sidebar .av-mac-hint { font-size: 11.5px; }
html:not(.drumchart-mac) .av-web-keys { margin-top: 8px; }
html:not(.drumchart-mac) .av-web-row :is(.av-trim-select, .av-timesig-select) { min-height: 30px; }
html:not(.drumchart-mac) .av-web-row .av-timesig-select { color: var(--warn); font-weight: 600; }
html:not(.drumchart-mac) .av-web-row .av-timesig-select.unsure { box-shadow: 0 0 0 1.5px var(--warn); }
html:not(.drumchart-mac) .av-web-row .av-more-select { width: 6.5em; border-style: solid; }
html:not(.drumchart-mac) .av-web-row .av-more-select.primary { background-color: var(--accent-fill); border-color: var(--accent-fill); color: var(--accent-ink); }
html:not(.drumchart-mac) .av-web-row .av-fix-toggle { align-self: center; }
html:not(.drumchart-mac) .av-web-row .av-bpm { font-size: 13px; }
/* A narrower window (an iPad's width held sideways, 1024): the marking row
   stays one row, as the Mac's does, by giving up padding, "Mark at" and the
   word Sections by its ☰; the sidebar a little narrower. */
@media (max-width: 1180px) {
  html:not(.drumchart-mac) .av-markbar.av-web-row { gap: 6px; }
  html:not(.drumchart-mac) .av-web-row .av-palette { gap: 4px; }
  html:not(.drumchart-mac) .av-web-row .btn.mark-kind { padding: 4px 7px; }
  html:not(.drumchart-mac) .av-web-row :is(.av-mark-at, .av-sections-label) { display: none; }
  html:not(.drumchart-mac) .av-web-row .av-more-select { width: 5.5em; }
  html:not(.drumchart-mac) .av-web-sidebar { flex-basis: 190px; width: 190px; }
}

/* What only a touch screen's Map has: the signature's chip drawn as the
   Mac's, the touch marking row, Follow playhead by the zoom, and More's and
   Trim's menus. */
@media (hover: none) and (pointer: coarse) {
  /* The signature's chip on the waveform, as the Mac's (Adam, 19 Oct: "a
     little bigger, and opaque"): solid amber, dark ink on it in dark mode,
     a pop-up arrow. */
  html:not(.drumchart-mac) .av-flag-meter { border-radius: 5px; box-shadow: 0 0.5px 1.5px rgba(0, 0, 0, 0.45); }
  html:not(.drumchart-mac) .av-meter-chip {
    color: var(--warn-ink, #fff);
    font-size: 12.5px;
    min-height: 22px;
    padding: 2px 8px;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border-radius: 5px;
  }
  html:not(.drumchart-mac) .av-meter-chip::after {
    content: '';
    width: 5px;
    height: 5px;
    border-right: 1.8px solid currentColor;
    border-bottom: 1.8px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
  }
  html:not(.drumchart-mac) :is(.av-meter-chip:active, .av-flag-meter.open .av-meter-chip) { background: rgba(0, 0, 0, 0.12); }
  /* The chart's own sections to mark (audioview.js touchMarkRow): one line
     that scrolls sideways when there are more than fit, kept 44 pt tall so
     the buttons' touch areas are inside it. */
  html:not(.drumchart-mac) .av-touch-markrow .av-mark-sections {
    flex: 0 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 4px 0;
    margin: -4px 0;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  html:not(.drumchart-mac) .av-touch-markrow .av-mark-sections::-webkit-scrollbar { display: none; }
  html:not(.drumchart-mac) .av-touch-markrow .mark-kind { flex: 0 0 auto; }
  html:not(.drumchart-mac) .av-touch-markrow .av-mark-new { flex: 0 0 auto; }
  html:not(.drumchart-mac) .av-touch-markrow .av-new-name { flex: 0 0 200px; width: 200px; min-height: 36px; font-size: 16px; }
  /* Follow playhead, by the waveform's zoom. */
  html:not(.drumchart-mac) .av-zoom-float .av-follow-btn { margin-left: 10px; border-radius: 999px; font-size: 12px; }
  html:not(.drumchart-mac) .av-zoom-float .av-follow-btn.on { background: color-mix(in srgb, var(--ink) 16%, transparent); color: var(--ink); border-color: var(--ink-faint); }

  /* More's and Trim's menus (macpopup.js), as the iPad's other menus. */
  html:not(.drumchart-mac) .mac-menu {
    position: fixed;
    z-index: 900;
    min-width: 220px;
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--raised);
    border: 1px solid var(--line);
    border-radius: calc(var(--radius) + 3px);
    box-shadow: var(--shadow-2);
    font-size: 15px;
    color: var(--ink);
  }
  html:not(.drumchart-mac) .mac-menu-head { font-size: 12px; color: var(--ink-soft); padding: 4px 12px 6px; }
  html:not(.drumchart-mac) .mac-menu-item {
    appearance: none;
    border: 0;
    background: transparent;
    font: inherit;
    color: inherit;
    text-align: left;
    min-height: 44px;
    padding: 0 12px 0 8px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
  }
  html:not(.drumchart-mac) .mac-menu-item:is(.hi, :active):not(:disabled) { background: var(--accent-soft); }
  html:not(.drumchart-mac) .mac-menu-item:disabled { color: var(--ink-faint); }
  html:not(.drumchart-mac) .mac-menu-item.danger { color: var(--danger); }
  html:not(.drumchart-mac) .mac-menu-key { margin-left: auto; padding-left: 22px; color: var(--ink-soft); }
  html:not(.drumchart-mac) .mac-menu-ico { width: 16px; display: inline-flex; justify-content: center; color: var(--accent); }
  html:not(.drumchart-mac) .mac-menu-sep { height: 1px; margin: 4px 8px; background: var(--line); }
}
/* Held upright (Adam's pick, 19 Oct: beside the sections the waveform was
   tall and narrow, height it cannot use and width it needs): the waveform
   across the whole width at the top, the beat grid and marking rows right
   under it (the waveform about a third of the screen), and the sections
   below them, two to a row, in the rest (a long list scrolls). Shut, the
   waveform has their room too. The sections' button draws the list
   where it now is, at the foot. Held sideways, the sidebar stays. */
@media (hover: none) and (pointer: coarse) and (orientation: portrait) {
  html:not(.drumchart-mac) .audio-view > .av-body { display: contents; }
  html:not(.drumchart-mac) .audio-view > .av-bar { order: 0; }
  html:not(.drumchart-mac) .audio-view .av-wave-col { order: 1; flex: 0 0 40vh; min-height: 0; }
  html:not(.drumchart-mac) .audio-view:not(:has(.av-mac-sidebar)) .av-wave-col { flex: 1 1 0; }
  html:not(.drumchart-mac) .audio-view > :is(.av-mac-beat, .av-mac-beatrow) { order: 2; }
  html:not(.drumchart-mac) .audio-view > .av-mac-markrow { order: 3; }
  html:not(.drumchart-mac) .audio-view .av-mac-sidebar {
    order: 4;
    flex: 1 1 0;
    min-height: 0;
    width: auto;
    padding: 8px 0 10px;
  }
  html:not(.drumchart-mac) .av-mac-sidebar .av-section-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 0; }
  html:not(.drumchart-mac) .av-mac-sidebar .av-mac-hint { margin-top: 8px; }
  html:not(.drumchart-mac) .av-mac-sidebar-btn svg { transform: rotate(-90deg); }
}

/* Held upright (820 wide) the marking row is one row still: the time stands
   without "Mark at", the names take less padding, and Trim is its scissors. */
@media (hover: none) and (pointer: coarse) and (max-width: 1000px) {
  html:not(.drumchart-mac) .av-markbar.av-mac-markrow { gap: 6px; }
  html:not(.drumchart-mac) .av-mac-markrow .av-mark-at { display: none; }
  html:not(.drumchart-mac) .av-mac-markrow .btn.mark-kind { padding: 6px 8px; }
  html:not(.drumchart-mac) .av-mac-trim-word { display: none; }
  /* The beat grid row, with "Time 4/4 ▾" in it: "found" goes (the tempo
     beside it says so) and Remove grid is Remove. */
  html:not(.drumchart-mac) .av-mac-beatrow { gap: 8px; }
  html:not(.drumchart-mac) .av-mac-beatrow > .av-mac-state, html:not(.drumchart-mac) .av-mac-grid-word { display: none; }
  html:not(.drumchart-mac) .av-mac-trim .av-mac-ico { margin-right: 1px; }
}

/* cued hits — the figure the band plays */
.ge-row.cues { margin-bottom: 1px; }
.ge-row.cues .ge-cells { border-color: var(--line); }
.ge-cell.cue { height: 14px; font-size: 9px; color: var(--ink-faint); }
.ge-cell.cue.on { color: var(--cell-on); background: var(--cell-cue-on); }
/* An accented hit: the same mark a drum's cell takes, a size down for the
   shorter row. */
.ge-cell.cue .accent-mark { font-size: 8px; right: 1px; }

/* What is sounding while a groove loops, or while the chart plays the phrase
   this editor shows (followChartStep in grooveeditor.js), lit the count's
   own way (Adam's pick, 23 Oct 2026, over a blue ring that "just flashes
   up"): while it plays the hits rest a shade dimmer, and the ones on the
   step come up to full ink on a soft wash of the same ink, at once, then
   fade back over a little longer than a sixteenth at 116, so a fast groove
   leaves a short trail as the count does rather than strobing. Only hits
   are lit; a cell with nothing in it never is. The wash is a layer under the
   glyph, since a cell's background and shadow already draw the beat and the
   lines between cells. */
.ge-cell { isolation: isolate; }
.ge-cell .glyph, .ge-cell .accent-mark { z-index: 1; }
.ge-cell .glyph { position: relative; }
.ge-cell::before {
  content: ''; position: absolute; inset: 1px 1px 1px 2px; border-radius: 4px; z-index: 0;
  pointer-events: none; opacity: 0; transition: opacity 190ms ease-out;
  background: var(--cell-on);
}
.ge-cell.sounding::before { opacity: 0.12; transition: none; }
.ge-cell.on, .ge-cell.on .accent-mark { transition: color 190ms ease-out; }
.groove-editor.ge-playing .ge-cell.on,
.groove-editor.ge-playing .ge-cell.on .accent-mark { color: var(--ink-faint); }
.groove-editor .ge-cell.on.sounding,
.groove-editor .ge-cell.on.sounding .accent-mark { color: var(--cell-on); transition: none; }
/* A note picked on the sheet over the grid, marked on the sheet only (its
   cell takes none), cleared by Delete or Escape (grooveeditor.js). */
.ge-preview g.ge-picked path, .ge-preview g.ge-picked text { fill: var(--accent); stroke: var(--accent); }
.ge-preview g.vf-notehead { cursor: pointer; }
.ge-preview { user-select: none; -webkit-user-select: none; }
/* The count under the step, so the place is kept through a step where
   nothing plays: the number brightens to the ink the under-hairpin counts
   already use, at once, and fades back to its grey, leaving a short trail
   that says which way it is going. No background, no ring (Adam, 20 Oct).
   Across a hairpin the count is that ink already, so there it is
   underlined instead. */
.ge-row.counts .ge-count { transition: color 150ms ease-out; }
.ge-row.counts .ge-count.passing { color: var(--cell-on); transition: none; }
.ge-row.counts .ge-count.under-hairpin.passing { text-decoration: underline; text-underline-offset: 2px; }

/* the beat grid controls */
.av-beat { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-soft); }
.av-bpm { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }
/* Map's notehead picker — the shared one out of chart.js, which keeps the
   engraved glyph out of the native option list (see .note-pick there). The
   hover lives on the wrapper now, since the control itself is invisible. */
.av-beat .note-pick { border-radius: 3px; padding: 1px 4px; margin: -1px -2px; }
.av-beat .note-pick:hover,
.av-beat .note-pick:focus-within { background: var(--wash); }
.av-beat .note-pick-face { font-weight: 600; color: var(--ink); }
/* One menu for the song's signature and "… from here" changes. Only as wide
   as a signature when closed; the longer "from here" options show once open. */
.av-timesig-select {
  width: 5em;
  border: 1px solid var(--line); background: var(--paper); font: inherit; color: inherit;
  padding: 3px 5px; border-radius: 5px; cursor: pointer; font-size: 12px;
}

/* In the Mark here row. Only as wide as its closed label, "Trim…" — the
   options are longer, but only show once it is open. */
.av-trim-select {
  width: 6em; min-height: 36px;
  border: 1px dashed var(--line); background: var(--paper); font: inherit; color: inherit;
  padding: 3px 8px; border-radius: var(--radius); cursor: pointer; font-size: 12px;
}

.av-click-controls { display: inline-flex; align-items: center; gap: 5px; }
.av-click-volume { width: 96px; }
.av-click-level { min-width: 30px; font-variant-numeric: tabular-nums; }
.av-nudge { display: inline-flex; align-items: center; gap: 3px; }
.av-nudge-value { min-width: 44px; text-align: center; font-variant-numeric: tabular-nums; }
.av-bpm-range { font-weight: 400; color: var(--ink-soft); }
.av-bpm-fix { margin-left: 5px; padding: 1px 6px; min-height: 20px; font-size: 11px; font-weight: 600; vertical-align: 1px; }

/* the overview box is a handle when zoomed in */
.av-viewport { pointer-events: auto; cursor: grab; }
.av-viewport:active { cursor: grabbing; background: var(--wash-firm); }

/* the bar sounding while a chart plays, where it is not a box on the chart
   (that has its own outline, chart.css: both made two boxes round the bar
   playing, Adam 30 Oct) */
.sounding-bar:not(.phrase) { box-shadow: inset 0 0 0 2px var(--accent); border-radius: 4px; }


.av-adjust { display: inline-flex; align-items: center; gap: 3px; }


/* importing an isolated drum track to settle the beat grid */
.av-drums { display: inline-flex; }


/* The settings menu's choices (settingsmenu.js). */
.mix-choices { display: inline-flex; gap: 4px; flex-wrap: wrap; }
/* A control whose options will not fit beside a label: the name goes above
   them and they take the width of the menu. "every other beat" is wider than
   what is left of a 268px row after a 52px label, so beside one it wrapped and
   left the label centred against two lines of buttons, lining up with
   neither. */
.mix-block { display: flex; flex-direction: column; gap: 5px; }
.mix-block-name { font-size: 12px; color: var(--ink-soft); }
.mix-choices.wide { display: flex; width: 100%; gap: 4px; }
/* Padded for reading rather than for a thumb: three of these have to sit side
   by side in a menu, and at the ordinary tiny-button padding the third wrapped
   onto a line of its own. */
.mix-choices.wide .btn {
  flex: 1 1 0;
  min-width: 0;
  padding: 4px 6px;
  font-size: 11.5px;
  white-space: nowrap;
}

/* A note in a line of text inside a control ("♩. = 73" in Map's Beat grid
   tray and its buttons). The chart's own marking (.tempo-note, chart.css) is
   set for print and sits a little large; in a button its stem ran out of the
   top. Smaller, and lowered so the ink (0.88em above its baseline to 0.14em
   below) centres on the text beside it. It must be inline in the same run
   as that text: as a flex item of its own, vertical-align does nothing. */
.note-inline {
  font-family: Bravura, serif;
  font-size: 1.25em;
  line-height: 0;
  vertical-align: -0.18em;
  margin-right: 0.12em;
  white-space: nowrap;
}
/* The augmentation dot, drawn apart and larger: at these sizes Bravura's
   metronome-mark dot is a speck tight against the note. No height of its own
   in the line, so enlarging it moves nothing. */
.note-dot {
  font-size: 1.5em;
  line-height: 0;
  margin-left: 0.08em;
}


/* Under 900px there is not room for three lanes, so both bars go back to being
   a row that wraps. Placed at the end of the file so it wins over .av-bar's own
   display, which is declared after the shared grid block. */
/* Under 900px there is not room to hold a lane open in the middle, so the
   switch goes back into the flow and the bar wraps as it needs to. */
@media (max-width: 899px) {
  .toolbar.chart-bar > .view-switch,
  .av-bar > .view-switch {
    position: static;
    transform: none;
  }
  .bar-gap { display: none; }
  .bar-group { max-width: none; }
  .toolbar.chart-bar,
  .av-bar { flex-wrap: wrap; }
  .bar-group { flex: 1 1 auto; }
  .bar-right { justify-content: flex-end; }
}

/* The phone screen (js/phone-gate.js): covers the app on a phone until
   "Open anyway". */
.phone-gate {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  padding: 24px max(24px, env(safe-area-inset-right)) 24px max(24px, env(safe-area-inset-left));
  background: var(--paper);
  color: var(--ink);
}
.phone-gate h1 { margin: 0; font-size: 1.5rem; line-height: 1.25; letter-spacing: -0.02em; }
.phone-gate p { margin: 0 0 12px; color: var(--ink-soft); font-size: 1.05rem; }
.phone-gate-send {
  font: inherit;
  font-weight: 600;
  padding: 14px 20px;
  border: 0;
  border-radius: 10px;
  background: var(--accent-fill);
  color: var(--accent-ink);
}
.phone-gate-anyway {
  font: inherit;
  padding: 10px;
  border: 0;
  background: none;
  color: var(--ink-soft);
  text-decoration: underline;
}

/* "A new version of DrumChart is ready" (js/update.js): small, out of the
   way at the foot of the screen, and only gone when answered. */
.update-note {
  position: fixed;
  left: 50%;
  bottom: max(16px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 900;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100% - 32px);
  padding: 8px 8px 8px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--raised);
  color: var(--ink);
  box-shadow: var(--shadow-2);
  font-size: 13px;
}

/* View mode (js/viewmode.js): the finished page on the stand, over the app.
   Dark round the page whatever the theme, because it is read on a stage, and
   nothing on it can be pressed but the bar: the page layer takes no touches,
   so no part of the chart can be selected from in here. */
.view-mode {
  position: fixed;
  inset: 0;
  z-index: 1100;
  overflow: hidden;
  background: #0b0c0e;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
/* The pages take the body's type, as Score's and Export set's do: a page
   drawn under any other line height is a different page. Only the controls
   are set here. */
.vm-head, .vm-side, .vm-toast, .vm-zones { font: 14px/1.25 system-ui, -apple-system, sans-serif; }
.vm-pages { position: absolute; inset: 0; pointer-events: none; }
.vm-host { position: absolute; left: 0; top: 0; transform-origin: 0 0; visibility: hidden; }
/* Spelled out past body.layout-view .chart-frame, which is still on the body
   when view mode is opened from the song list after Score, and put Score's
   page shadow and rounded corners on these pages. */
.view-mode .vm-host .chart-frame { box-shadow: none; border-radius: 0; }
.vm-host .page-no { display: none; }
.vm-failed {
  width: 810px; height: 1080px; box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  padding: 80px; background: #fff; color: #14161a; font-size: 26px; text-align: center;
}

.vm-zones {
  position: absolute; inset: 0;
  display: grid; grid-template-columns: 1fr 2fr 1fr;
  pointer-events: none; opacity: 0; transition: opacity 0.4s ease;
}
.vm-zones.show { opacity: 1; }
.vm-zone {
  display: flex; align-items: center; justify-content: center;
  background: rgba(31, 95, 208, 0.16);
  border-right: 1px dashed rgba(31, 95, 208, 0.6);
}
.vm-zone:last-child { border-right: 0; }
.vm-zone-mid { background: rgba(20, 22, 26, 0.1); }
/* What is picked in Practice (paintGrabs): one box a line, laid over the
   bars, in place of each bar's own outline; and the bar playing inside it
   has none of its own. */
.view-mode .phrase.selected { outline: none !important; box-shadow: none !important; }
.vm-pick {
  position: absolute; z-index: 2; box-sizing: border-box; pointer-events: none;
  border: 2px solid #1f5fd0; border-radius: 4px;
}
/* The pick's handles in Practice (paintGrabs): a blue dot on each end of the
   outline, in a 44pt reach, the one thing on the page that takes a finger. */
.vm-grab {
  position: absolute; top: 50%; z-index: 3; width: 44px; height: 44px; margin-top: -22px;
  pointer-events: auto; touch-action: none; cursor: ew-resize;
}
.vm-grab-start { left: -23px; }
.vm-grab-end { right: -23px; }
.vm-grab::after {
  content: ''; position: absolute; left: 50%; top: 50%; width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border-radius: 50%; background: #1f5fd0; border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  box-sizing: border-box;
}
.vm-zone span {
  font-weight: 600; font-size: 15px; color: #fff;
  background: rgba(20, 22, 26, 0.82); padding: 8px 14px; border-radius: 999px;
}

.vm-btn {
  appearance: none; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 40px; min-width: 40px; padding: 0 14px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(255, 255, 255, 0.06);
  color: inherit; font: inherit; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.vm-btn:active { background: rgba(255, 255, 255, 0.14); }
.vm-btn.on, .vm-btn[aria-expanded="true"] { background: #1f5fd0; border-color: #1f5fd0; }
.vm-icon-btn { padding: 0 12px; }
/* + New chart on the header, in the app (paintHead in js/viewmode.js): a
   small blue "+" beside Charts. */
.vm-new-btn { width: 40px; min-width: 40px; padding: 0; background: #1f5fd0; border-color: #1f5fd0; color: #fff; }
.vm-new-btn:active { background: #1a51b3; }
.vm-icon { display: inline-flex; }

/* The header a tap in the middle brings down (paintHead in js/viewmode.js):
   close and the songs at the left, where you are in the middle, Edit and the
   song that comes next at the right. Slid up out of sight when it is away,
   so bringing it down and putting it away again is one movement. */
.vm-head {
  position: absolute;
  left: 0; right: 0; top: 0;
  z-index: 3;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  padding: max(10px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right)) 10px max(12px, env(safe-area-inset-left));
  background: #14161a; color: #f2f3f5;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  transform: translateY(-110%);
  visibility: hidden;
  transition: transform 0.18s ease, visibility 0s linear 0.18s;
}
.vm-head.show { transform: none; visibility: visible; transition: transform 0.18s ease, visibility 0s; }
.vm-head-side { display: flex; align-items: center; gap: 8px; min-width: 0; }
.vm-head-side.right { justify-content: flex-end; }
.vm-where { min-width: 0; max-width: 46vw; text-align: center; }
.vm-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vm-pos { margin-top: 2px; font-size: 13px; color: #a9afba; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The next song, in the top right corner: a word over its title, a tap to
   go to it. */
.vm-next {
  appearance: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  column-gap: 8px;
  align-items: center;
  min-width: 0;
  max-width: 230px;
  min-height: 40px;
  padding: 3px 10px 3px 12px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(255, 255, 255, 0.06);
  color: inherit; font: inherit; text-align: right; cursor: pointer;
}
.vm-next:active { background: rgba(255, 255, 255, 0.14); }
.vm-next-word { grid-column: 1; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: #8fb4ff; }
.vm-next-title { grid-column: 1; font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vm-next-arrow { grid-column: 2; grid-row: 1 / span 2; font-size: 24px; line-height: 1; color: #a9afba; }
.vm-next-end { cursor: default; display: inline-flex; border-style: dashed; background: none; }
.vm-next-end .vm-next-word { color: #8a909b; }
/* An iPad upright, or a narrow window: with Layout beside Edit chart the
   right of the header ran into the chart's title (1 Nov 2026). The words
   that only label go, Charts to its icon, Edit chart to Edit, Next to the
   word without the next chart's title (said by its tooltip). Up to an iPad
   on its side, the buttons keep their size and the title has what is left
   between them, rather than a third of the width each. The website's only:
   the iPad app's header has its own buttons (View and Edit) and is left as
   it was sent to App Review. */
@media (max-width: 1180px) {
  html:not(.drumchart-app) .vm-head { grid-template-columns: auto minmax(0, 1fr) auto; }
  html:not(.drumchart-app) .vm-head .vm-where { max-width: none; }
  html:not(.drumchart-app) .vm-head-side > * { flex-shrink: 0; }
}
@media (max-width: 900px) {
  html:not(.drumchart-app) .vm-head .vm-btn-word, html:not(.drumchart-app) .vm-head .vm-wide-word,
  html:not(.drumchart-app) .vm-head .vm-next-title { display: none; }
}

/* The sidebar of songs (paintSide in js/viewmode.js), slid out from the left
   under the header. */
.vm-side {
  position: absolute;
  left: 0; bottom: 0;
  top: var(--vm-head-h, 64px);
  z-index: 2;
  width: min(340px, 86vw);
  display: flex;
  flex-direction: column;
  padding-left: env(safe-area-inset-left);
  background: #14161a; color: #f2f3f5;
  box-shadow: 6px 0 20px rgba(0, 0, 0, 0.35);
  transform: translateX(-105%);
  visibility: hidden;
  transition: transform 0.2s ease, visibility 0s linear 0.2s;
}
.vm-side.show { transform: none; visibility: visible; transition: transform 0.2s ease, visibility 0s; }
.vm-side-head {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.vm-side-back {
  appearance: none; flex: 0 0 auto;
  width: 36px; height: 36px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(255, 255, 255, 0.06);
  color: inherit; font: inherit; font-size: 24px; line-height: 1; cursor: pointer;
}
.vm-side-title { flex: 1 1 auto; min-width: 0; font-size: 16px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vm-side-action {
  appearance: none; flex: 0 0 auto;
  min-height: 36px; padding: 0 14px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(255, 255, 255, 0.06);
  color: inherit; font: inherit; font-weight: 600; cursor: pointer;
}
.vm-side-action.on { background: #1f5fd0; border-color: #1f5fd0; }
.vm-side-tools { display: flex; align-items: center; gap: 10px; padding: 8px 12px; }
.vm-side-note { font-size: 12px; color: #a9afba; }
.vm-sort {
  display: inline-flex; gap: 2px; padding: 2px;
  border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 9px;
}
.vm-sort-choice {
  appearance: none; min-height: 32px; padding: 0 14px; border: 0; border-radius: 7px;
  background: none; color: #c3c8d1; font: inherit; font-size: 14px; cursor: pointer;
}
.vm-sort-choice.on { background: rgba(31, 95, 208, 0.55); color: #fff; font-weight: 600; }
.view-mode input { -webkit-user-select: text; user-select: text; }
.vm-side-search {
  flex: 1 1 auto; min-width: 0; min-height: 38px;
  padding: 6px 10px; border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.06);
  color: inherit; font: inherit;
}
.vm-side-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  /* The page under it takes no touches (see .view-mode); this list scrolls. */
  touch-action: pan-y;
  padding: 4px 6px 16px;
  -webkit-overflow-scrolling: touch;
}
.vm-side-group {
  margin: 12px 10px 4px; font-size: 11px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; color: #8a909b;
}
.vm-side-song {
  display: flex; align-items: center; gap: 4px;
  border-radius: 8px;
  transition: transform 0.12s ease;
}
.vm-side-song.current { background: rgba(31, 95, 208, 0.35); }
.vm-side-song.dragging { position: relative; z-index: 5; transition: none; background: #2a2f38; box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); }
.vm-side-pick {
  appearance: none; flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  min-height: 48px; padding: 6px 10px;
  border: 0; border-radius: 8px; background: none;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.vm-side-pick:disabled { cursor: default; color: inherit; opacity: 1; }
.vm-side-pick:not(:disabled):active { background: rgba(255, 255, 255, 0.1); }
.vm-side-no { min-width: 1.4em; text-align: right; color: #8a909b; font-variant-numeric: tabular-nums; }
.vm-side-plus { min-width: 1.4em; text-align: center; color: #8fb4ff; font-weight: 700; font-size: 18px; }
.vm-side-name { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.vm-side-song-title { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vm-side-artist { font-size: 12px; color: #a9afba; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vm-side-count { color: #8a909b; font-size: 13px; font-variant-numeric: tabular-nums; }
.vm-grip {
  appearance: none; flex: 0 0 auto;
  width: 36px; height: 44px; border: 0; background: none;
  color: #a9afba; font-size: 18px; cursor: grab;
}
.vm-side-remove {
  appearance: none; flex: 0 0 auto;
  width: 34px; height: 34px; margin-right: 4px; border-radius: 50%;
  border: 0; background: #b3261e; color: #fff;
  font: inherit; font-size: 20px; font-weight: 700; line-height: 1; cursor: pointer;
}
/* A touch screen's (sideSong): iPadOS's ⊖, 22 pt of red with a white bar,
   in a 44 pt target, at the start of the row. */
.vm-side-remove.ipad {
  width: 44px; height: 44px; margin: 0; border-radius: 0; background: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.vm-side-remove-dot { position: relative; width: 22px; height: 22px; border-radius: 50%; background: #ff453a; }
.vm-side-remove-dot::after {
  content: ''; position: absolute; left: 5px; right: 5px; top: 50%; height: 2.5px; margin-top: -1.25px;
  border-radius: 1.25px; background: #fff;
}
.vm-side-remove.ipad:active .vm-side-remove-dot { background: #d63a31; }
.vm-grip .vm-icon { display: inline-flex; line-height: 0; }
.vm-side-add {
  appearance: none; display: block; width: calc(100% - 8px); margin: 8px 4px 0;
  min-height: 44px; border-radius: 10px;
  border: 1px dashed rgba(255, 255, 255, 0.3); background: none;
  color: #8fb4ff; font: inherit; font-weight: 600; cursor: pointer;
}
.vm-side-empty { margin: 16px 12px; color: #a9afba; }

/* The iPad app's home is view mode (js/home.js): its ≡ menu where ✕ is
   elsewhere, a new chart, a chart's ⋯ and Export in the sidebar. Only the
   app draws any of these. */
.vm-menu { display: inline-flex; }
.vm-menu .settings-btn {
  min-height: 40px; min-width: 40px; padding: 0 12px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(255, 255, 255, 0.06); color: inherit;
}
.vm-menu .settings-btn.open { background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.3); }
/* Opens to the right, from the left edge of the header, over the sidebar. */
.vm-menu .sound-menu { left: 0; right: auto; }
.vm-head:has(.settings-menu) { z-index: 8; }
.vm-side-actions { display: inline-flex; gap: 6px; }
.vm-side-more {
  appearance: none; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-right: 2px; border-radius: 10px;
  border: 0; background: none; color: #c3c8d1; cursor: pointer;
}
.vm-side-more:active { background: rgba(255, 255, 255, 0.1); }
.vm-side-item, .vm-side-way {
  appearance: none; display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 48px; padding: 6px 12px; border: 0; border-radius: 8px; background: none;
  color: inherit; font: inherit; font-size: 15px; text-align: left; cursor: pointer;
}
.vm-side-way { flex-direction: column; align-items: flex-start; justify-content: center; gap: 2px; min-height: 60px; }
.vm-side-way .vm-side-artist { white-space: normal; }
.vm-side-item:active, .vm-side-way:active { background: rgba(255, 255, 255, 0.1); }
.vm-side-item.danger { color: #ff7b72; }
.vm-side-tick-mark { width: 1.2em; color: #8fb4ff; font-weight: 700; text-align: center; }
.vm-side-into { margin: 4px 14px 0; }

/* With no charts, the app shows the blank space with + New chart in the
   middle and only the menu above it (renderLibrary in js/library.js). */
.first-run-bar .settings-cluster { margin-left: auto; }
.library.first-run { display: flex; align-items: center; justify-content: center; min-height: 70vh; }

/* The Mac app (native.js): the page runs up under the window's title bar,
   which draws no title (Mac.swift), and every bar's top padding is the
   title bar's height (env(safe-area-inset-top)), so the close, minimise and
   full-screen buttons sit over the bar's own colour, above its buttons. */

/* In the app, what opens from view mode's sidebar and menu (Export,
   feedback, Dropbox, a word on the status line, progress) shows over it. */
:is(.drumchart-app, .drumchart-macapp) body.view-mode-open .feedback-overlay,
:is(.drumchart-app, .drumchart-macapp) body.view-mode-open .progress-overlay,
:is(.drumchart-app, .drumchart-macapp) body.view-mode-open .status { z-index: 1200; }
/* A set made from here (makeSet in js/viewmode.js): its name in the head, to
   type over, and a word on how the songs go in until the first has. */
.vm-side-set-name {
  box-sizing: border-box; width: 100%; min-height: 36px;
  padding: 4px 10px; border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.06);
  color: inherit; font: inherit; font-weight: 700;
}
.vm-side-set-name:focus { outline: none; border-color: #6fa0f2; }
.vm-side-hint { margin: 10px 10px 2px; font-size: 13px; color: #a9afba; }

.vm-toast {
  position: absolute; left: 50%; bottom: max(18px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  max-width: calc(100% - 32px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 9px 16px; border-radius: 999px;
  background: rgba(20, 22, 26, 0.86); color: #f2f3f5; font-weight: 600;
  pointer-events: none; opacity: 0; transition: opacity 0.3s ease;
}
.vm-toast.show { opacity: 1; }

/* Practice (js/viewpractice.js): the strip of play controls along the foot,
   always there while Practice is on, and the page drawn in the room between
   it and the header. Design A (Adam, 2026-10-13): Play exactly in the middle
   of the screen, Speed and Loop to its left, Click and Mix to its right, and
   where the music is at the far right. Two equal outer columns keep Play in
   the middle whatever either side holds. Dark whatever the theme, as the
   header is. Every control has a fixed size, at least 44px for a finger, so
   nothing moves when one is pressed. */
.vm-strip {
  --vm-side: max(16px, env(safe-area-inset-left), env(safe-area-inset-right));
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 3;
  display: none;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  padding: 10px var(--vm-side) max(10px, env(safe-area-inset-bottom));
  background: #14161a; color: #eceef2;
  border-top: 1px solid #23262c;
  box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.35);
  font: 14px/1.25 system-ui, -apple-system, sans-serif;
}
.vm-strip.show { display: grid; }
/* Above the strip, and above the mixer or the click's settings while one is
   up (practicemix.js), so a word said meanwhile is never under it. */
.view-mode.practising .vm-toast { bottom: var(--vm-pop-clear, calc(var(--vm-strip-h, 64px) + var(--vm-track-h, 0px) + 12px)); }
.view-mode.practising .vm-side { bottom: var(--vm-strip-h, 64px); }
/* View and Edit's Layout: a popover hanging from the header's right, over
   the page's margin rather than beside it, the page left where it is. It
   takes touches and selection as Score's panel does. */
.vm-layout {
  position: absolute; z-index: 2;
  top: calc(var(--vm-head-h, 64px) + 8px); right: max(12px, env(safe-area-inset-right));
  width: min(300px, calc(100vw - 24px)); max-height: calc(100% - var(--vm-head-h, 64px) - 20px);
  overflow-y: auto; border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  touch-action: auto; -webkit-user-select: auto; user-select: auto;
}
.vm-layout .layout-panel { position: static; width: auto; z-index: auto; border-left: 0; border-radius: 12px; box-shadow: none; padding: 12px 14px 14px; }
/* The Layout popover's own panel (layoutpop.js). A chosen answer and a
   ticked box are a neutral tint: blue is for Play, the way into Map and what
   is playing (Adam, 25 Oct). */
.lpop .btn.chosen {
  background: color-mix(in srgb, var(--ink) 14%, transparent);
  border-color: color-mix(in srgb, var(--ink) 34%, transparent);
  color: var(--ink); font-weight: 600;
}
.lpop .lp-toggle.on .lp-tick { background: color-mix(in srgb, var(--ink) 70%, transparent); border-color: transparent; color: var(--paper); }
.lpop .lp-row { grid-template-columns: 84px minmax(0, 1fr); }
.lpop .lp-row-stacked { grid-template-columns: minmax(0, 1fr); }
/* One line of answers, however narrow Score's panel is. */
.lpop .lp-choices { flex-wrap: nowrap; }
.lpop .lp-choices .btn { flex: 1 1 0; min-width: 0; padding: 3px 2px; white-space: nowrap; }
.lpop-step-ctl { display: flex; align-items: center; gap: 4px; justify-content: flex-end; }
.lpop-step-ctl .btn { min-width: 34px; }
.lpop-step-value { min-width: 46px; text-align: center; font-size: 13px; font-variant-numeric: tabular-nums; }
.lpop-fit { margin-left: 6px; }
.lpop-rows { display: flex; flex-direction: column; margin-top: 4px; }
.lpop-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) 12px; align-items: center; gap: 8px;
  min-height: 38px; padding: 0 2px; font: inherit; font-size: 13px; color: var(--ink);
  background: none; border: 0; border-bottom: 1px solid var(--line-soft); text-align: left; cursor: pointer;
}
.lpop-row:last-child { border-bottom: 0; }
.lpop-row:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); }
.lpop-row-value { color: var(--ink-soft); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lpop-chev { color: var(--ink-faint); font-size: 16px; }
.lpop-drill-head { display: flex; align-items: center; gap: 8px; padding-bottom: 6px; border-bottom: 1px solid var(--line-soft); }
.lpop-back { font: inherit; font-size: 13px; color: var(--ink-soft); background: none; border: 0; padding: 4px 6px 4px 0; cursor: pointer; }
.lpop-back:hover { color: var(--ink); }
.lpop-drill-title { font-weight: 600; font-size: 13px; }
.lpop-list { display: flex; flex-direction: column; }
.lpop-pick { display: flex; align-items: center; gap: 8px; min-height: 34px; font: inherit; font-size: 13px; color: var(--ink); background: none; border: 0; border-radius: 6px; text-align: left; cursor: pointer; }
.lpop-pick:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); }
.lpop-tick { width: 14px; color: var(--ink-soft); }
@media (pointer: coarse) {
  .lpop-row, .lpop-pick { min-height: 44px; }
}
/* View and Edit: full screen is the page filling the window, and this
   is the way back, faint in the corner until the pointer or a tap finds it. */
.vm-shrink {
  position: absolute; z-index: 4;
  top: max(10px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right));
  width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(20, 22, 26, 0.85); color: #f2f3f5;
  cursor: pointer; opacity: 0.2; transition: opacity 0.2s ease;
}
.vm-shrink:hover, .vm-shrink:focus-visible, .vm-shrink.show { opacity: 1; }
/* Zoomed into the page (pinch or ⌘-scroll, as in Score): a drag moves
   around it, and this puts the whole page back. Neutral, above where
   Practice's strip goes. The pointer stays as it was (Adam, 25 Oct). */
.vm-fit {
  position: absolute; z-index: 4;
  right: max(12px, env(safe-area-inset-right)); bottom: calc(84px + env(safe-area-inset-bottom));
  height: 36px; padding: 0 14px; border-radius: 18px;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(20, 22, 26, 0.88); color: #f2f3f5;
  font: 600 13px/1 -apple-system, system-ui, sans-serif; cursor: pointer;
}
.vm-fit:hover, .vm-fit:focus-visible { background: rgba(40, 43, 50, 0.95); }
/* View and Edit: full screen is the page alone, Practice's strip put away
   with the header (Space still plays). */
.view-mode.enlarged .vm-strip, .view-mode.enlarged .vm-pops, .view-mode.enlarged .vm-track { display: none !important; }
.view-mode.enlarged .vm-fit { bottom: calc(16px + env(safe-area-inset-bottom)); }
/* Turning the page on screen (paintTurns in viewmode.js): an arrow either
   side of the page, white on the dark desk, never blue (Adam picked these,
   25 Oct). */
.vm-turns { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
.vm-turn {
  position: absolute; width: 48px; height: 48px; border-radius: 24px;
  pointer-events: auto; display: grid; place-items: center; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(20, 22, 26, 0.85); color: #f2f3f5;
  opacity: 0.7; transition: opacity 0.15s ease, background 0.15s ease;
}
.vm-turn:hover:not(:disabled), .vm-turn:focus-visible { opacity: 1; background: rgba(48, 52, 60, 0.95); }
.vm-turn:disabled { opacity: 0.15; cursor: default; }
.view-mode .vm-turns .vm-icon { display: inline-flex; line-height: 0; }
/* Export, Settings and Feedback open over View too: View is a layer of its
   own above the app, and Export from its header opened behind it (Adam,
   25 Oct: "export seems kind of broken"). */
body.view-mode-open .feedback-overlay { z-index: 1200; }
/* View and Edit: the header is part of the page, there from the first frame,
   not dropped in: coming from Edit it slid down and looked wrong (Adam,
   25 Oct). Full screen puts it away and back the same way, at once. */
body.view-edit .vm-head, body.view-edit .vm-head.show { transition: none; }
/* Follow playhead on: a grey pressed look and its tick, on every screen. */
.av-zoom-float .av-follow-btn.on { background: color-mix(in srgb, var(--ink) 16%, transparent); color: var(--ink); border-color: var(--ink-faint); }
/* View and Edit: Map is the strip expanded, laid over Build from under
   its bar, which stays put and live; Map's own bar is not drawn. It is always
   laid over rather than in the page's column, and the strip keeps its room
   under it, so opening and closing Map moves nothing but Map: an earlier
   version that switched layout as the slide ended jumped (Adam, 25 Oct:
   "a bit glitchy"). Dragged, it follows the pointer down (--map-drag) and
   settles back or closes. */
body.map-in-build .audio-view {
  position: fixed; left: 0; right: 0; bottom: 0; top: var(--bar-h, 56px); z-index: 60;
  padding-top: 0; gap: 6px; border-top: 1px solid var(--line);
  box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.18);
  will-change: transform;
  backface-visibility: hidden;
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0, 1);
}
body.map-in-build .audio-view .av-bar { display: none; }
/* Build's bar stays above Map, which starts under it, so what drops from
   the bar (the ≡ menu) opens over Map, not behind it (Adam, 25 Oct). */
body.map-in-build #app > .toolbar.chart-bar { position: relative; z-index: 70; }
body.map-dragging .audio-view { transform: translateY(var(--map-drag, 0px)); transition: none; }

/* Its header row (mapHeadRow in audio.js), the same on the strip and on Map
   expanded: Map and its arrow, the clock, the section playing; W on hover.
   The whole row is the control. */
.map-handle { flex: 0 0 auto; cursor: pointer; touch-action: none; user-select: none; -webkit-user-select: none; }
.au-grip.map-handle { height: auto; margin: -2px 0 4px; cursor: pointer; }
.au-grip.map-handle::before { display: none; }
.audio-view > .map-handle { margin-top: 6px; }
.map-head-row { position: relative; display: flex; align-items: center; gap: 10px; min-height: 30px; padding: 4px 2px 0; }
.map-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 13px/1 system-ui, -apple-system, sans-serif; color: var(--ink);
  padding: 5px 10px 6px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper);
}
.map-handle:hover .map-toggle { border-color: var(--ink-faint); }
.map-chev { font-size: 12px; color: var(--ink-soft); }
.map-clock { font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums; white-space: nowrap; min-width: 0; }
.map-now { font-size: 12px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Map open on a chart with nothing written: where to go next, in grey. */
.map-next { font-size: 12px; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.map-toggle.to-build .map-mark { font-size: 13px; line-height: 1; }
.map-key {
  margin-left: auto; font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--ink-soft);
  border: 1px solid var(--line); border-radius: 4px; padding: 3px 6px; opacity: 0; transition: opacity 0.15s ease;
}
.map-handle:hover .map-key, .map-handle:focus-within .map-key { opacity: 1; }
/* The strip: room for the sections' bands along its foot. */
body.view-edit .au-wave { height: 58px; }
/* Map's overview reads as the strip does, its sections banded along the foot
   (Adam, 25 Oct), and the box showing what the big waveform shows is a faint
   outline, not a blue one. */
body.view-edit .av-overview-wrap { flex-basis: 58px; }
body.view-edit .av-viewport {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}
body.view-edit .av-viewport:active { background: color-mix(in srgb, var(--ink) 10%, transparent); }

/* The way into Map: an accent button with a waveform mark, the one blue on
   the strip (Adam picked it, 25 Oct). */
.map-mark { display: inline-flex; }
.map-head-row .map-toggle { background: var(--accent-fill); color: var(--accent-ink); border-color: transparent; }
.map-head-row .map-chev { color: inherit; opacity: 0.85; }
.map-handle:hover .map-head-row .map-toggle { filter: brightness(1.08); border-color: transparent; }

/* Map tidied (Adam, 25 Oct): blue only for Play, the
   way into Map and what is playing. Open, the Map button is a plain pressed
   button in the same place: you are in Map, and it is the way out. */
.audio-view .map-head-row .map-toggle { background: color-mix(in srgb, var(--ink) 12%, transparent); color: var(--ink); border-color: var(--line); }
.audio-view .map-head-row .map-chev { color: var(--ink-soft); opacity: 1; }
.audio-view .map-handle:hover .map-head-row .map-toggle { filter: none; border-color: var(--ink-faint); }
/* The sections list, always open: the list scrolls, adding sits under it. */
html:not(.drumchart-mac) .av-home-sidebar { display: flex; flex-direction: column; min-height: 0; padding-bottom: 0; }
.av-home-sidebar .av-section-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.av-home-sidebar .av-msection.at { background: color-mix(in srgb, var(--ink) 9%, transparent); box-shadow: inset 2px 0 0 var(--accent); }
.av-home-sidebar .av-msection.at :is(.av-msection-name, .av-msection-time) { color: var(--ink); }
.av-home-add { flex: 0 0 auto; border-top: 1px solid var(--line); padding: 10px 12px 12px; display: grid; gap: 8px; }
.av-home-add-head { font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.av-home-add-head .av-mark-time { font-variant-numeric: tabular-nums; letter-spacing: 0; }
.av-home-add.naming .av-home-add-head { color: var(--ink); text-transform: none; letter-spacing: 0; font-size: 12px; }
.av-home-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.av-home-chips .btn.small, .av-home-chips select { font-size: 11px; line-height: 1.3; padding: 2px 7px; min-height: 0; height: auto; }
.av-mac-beatrow .btn.small.on { background: color-mix(in srgb, var(--ink) 12%, transparent); border-color: var(--ink-faint); }
/* The regrouped beat grid row: Snap beside 'Add at'; Remove quiet until pointed at. */
.av-home-add-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.av-home-snap .av-follow { font-size: 11px; text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--ink-soft); gap: 4px; }
.av-home-snap select { font-size: 11px; padding: 1px 4px; }
.av-home-beat .av-mac-beatrow { flex-wrap: wrap; row-gap: 6px; overflow: visible; }
.av-home-remove { color: var(--ink-soft); }
.av-home-remove:hover { color: var(--danger, #d9534f); border-color: currentColor; }
.av-home-add-head > span:first-child { white-space: nowrap; }

/* Sliding up over the strip from the foot of the window, and back down off
   it, revealing the strip that was there all along (from wherever a drag
   left it, --sink-from), on the easing iOS sheets use. Only the transform
   moves, on its own layer, so the slide does not wait on anything else. */
body.map-rising .audio-view { animation: map-rise 0.32s cubic-bezier(0.32, 0.72, 0, 1) both; transition: none; }
body.map-sinking .audio-view { animation: map-sink 0.26s cubic-bezier(0.32, 0.72, 0, 1) both; transition: none; }
@keyframes map-rise { from { transform: translateY(100%); } to { transform: none; } }
@keyframes map-sink { from { transform: translateY(var(--sink-from, 0px)); } to { transform: translateY(100%); } }
@media (prefers-reduced-motion: reduce) {
  body.map-rising .audio-view, body.map-sinking .audio-view { animation: none; }
  body.map-in-build .audio-view { transition: none; }
}
.vm-btn:disabled { opacity: 0.4; cursor: default; }
.vm-strip-side { display: flex; align-items: center; gap: 8px; min-width: 0; }
.vm-strip-left { justify-self: end; margin-right: 14px; }
.vm-strip-right { justify-self: start; margin-left: 14px; }
/* What every button on the strip, in its popovers and in the track view
   starts from, under anything said of it by its own class. */
.vm-strip .vm-icon, .vm-pop .vm-icon, .pc-bar .vm-icon { display: inline-flex; line-height: 0; }
:where(.vm-strip, .vm-pop, .vm-track, .pc-bar) svg { display: block; flex: none; }
:where(.vm-strip, .vm-pop, .vm-track, .pc-bar) button {
  appearance: none; -webkit-appearance: none; margin: 0; font: inherit; color: inherit;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation; cursor: pointer;
}
:where(.vm-strip, .vm-pop, .vm-track, .pc-bar) button:disabled { cursor: default; }
:where(.vm-strip, .vm-pop, .vm-track, .pc-bar) button:focus-visible { outline: 2px solid #6fa0f2; outline-offset: 2px; }

/* Play, a circle in the middle. */
.vm-play {
  width: 48px; height: 48px; padding: 0; border: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: #2f62c9; color: #fff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 3px 10px rgba(47, 98, 201, 0.35);
}
.vm-play:active { background: #2856b3; }

/* Loop and Click: split buttons of one shape. The big part is the one tap;
   the small part opens what belongs to it. */
.vm-split { display: inline-flex; gap: 2px; flex: 0 0 auto; }
.vm-split > button, .vm-mix-btn, .vm-ctl {
  height: 44px; box-sizing: border-box; padding: 0;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  border: 1px solid #343943; background: #262a31; color: #eceef2;
  font-size: 15px; font-weight: 600; white-space: nowrap;
}
.vm-split-main { width: 92px; border-radius: 11px 4px 4px 11px; }
.vm-split-door { width: 44px; border-radius: 4px 11px 11px 4px; color: #b9bec8; }
.vm-split.solo .vm-split-main { border-radius: 11px; }
.vm-split > button:active:not(:disabled), .vm-mix-btn:active, .vm-ctl:active:not(:disabled) { background: #30353d; }
.vm-split-main.on { background: #2f62c9; border-color: #3b6fda; color: #fff; }
.vm-split-main.on:active { background: #2958b8; }
.vm-split-door.open, .vm-mix-btn.open { background: #1c2943; border-color: #3d5f9f; color: #9fc0f7; }
.vm-split > button:disabled, .vm-ctl:disabled { opacity: 0.4; }

/* Mix: a faders icon, and a dot on it when the mix is not the default. */
.vm-mix-btn { position: relative; width: 52px; border-radius: 11px; }
.vm-mix-dot {
  position: absolute; top: 7px; right: 8px; width: 8px; height: 8px; border-radius: 50%;
  background: #6fa0f2; box-shadow: 0 0 0 2px #262a31; visibility: hidden;
}
.vm-mix-btn.changed .vm-mix-dot { visibility: visible; }
.vm-mix-btn.open .vm-mix-dot { box-shadow: 0 0 0 2px #1c2943; }

/* While the chart is not played at its own tempo, Click carries the rate
   on its corner (playcontrols.js), laid over it so nothing moves. */
.pc-click { position: relative; }
.pc-tempo-tag {
  position: absolute; top: -7px; right: -5px; z-index: 1;
  height: 16px; padding: 0 5px; border-radius: 8px;
  background: #f0b44c; color: #22180a; box-shadow: 0 0 0 2px #16181c;
  font-size: 11px; font-weight: 700; line-height: 16px; font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* The tempo row at the top of the click's settings (practicemix.js): the
   figure and Reset, and under them a slider between − and +. */
.vm-prow.vm-tempo-row { flex-direction: column; align-items: stretch; justify-content: flex-start; gap: 6px; padding: 8px 12px 12px 18px; }
.vm-tempo-top { display: flex; align-items: center; gap: 12px; min-height: 36px; }
.vm-tempo-top .vm-prow-name { flex: 1; }
.vm-tempo-figure { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vm-tempo-reset { height: 36px; }
.vm-tempo-ctl { display: flex; align-items: center; gap: 10px; }
.vm-step {
  width: 44px; height: 44px; flex: none; padding: 0; box-sizing: border-box;
  display: grid; place-items: center;
  border: 1px solid #343943; border-radius: 10px; background: #262a31; color: #eceef2;
}
.vm-step:active:not(:disabled) { background: #30353d; }
.vm-step:disabled { color: #4c525c; }
.vm-tslider { position: relative; flex: 1; height: 44px; touch-action: none; cursor: pointer; outline: none; }
.vm-tslider-track { position: absolute; left: 0; right: 0; top: 50%; height: 6px; margin-top: -3px; border-radius: 3px; background: #2b2f37; overflow: hidden; }
.vm-tslider-fill { position: absolute; left: 0; top: 0; bottom: 0; background: #6fa0f2; }
.vm-tslider-thumb {
  position: absolute; top: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px; border-radius: 50%;
  background: linear-gradient(#eceef2, #c8ccd3); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55);
}
.vm-tslider.dragging { cursor: grabbing; }
.vm-tempo-tap { height: 36px; min-width: 52px; border: 1px solid #343943; border-radius: 9px; color: #eceef2; }
.vm-tempo-tap:active { background: #30353d; }
/* A tempo typed: the chart's one tempo as the figure, and each marking in
   the list where there are several. */
.vm-tempo-type {
  width: 64px; height: 36px; box-sizing: border-box; padding: 0 8px;
  border: 1px solid #343943; border-radius: 9px; background: #14161a; color: #eceef2;
  font: 600 15px system-ui, -apple-system, sans-serif; font-variant-numeric: tabular-nums; text-align: right;
}
.vm-tempo-type:focus { outline: none; border-color: #6fa0f2; }
/* No spinner of the browser's own: − and + are beside it. */
.vm-tempo-type { -moz-appearance: textfield; appearance: textfield; }
.vm-tempo-type::-webkit-inner-spin-button, .vm-tempo-type::-webkit-outer-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
.vm-tempo-own { display: inline-flex; align-items: center; gap: 6px; }
/* Several markings: every one, the one playing lit. */
.vm-tempo-list-head { margin-top: 6px; font-size: 12px; font-weight: 600; color: #9aa1ad; }
.vm-tempo-list { display: flex; flex-direction: column; gap: 4px; }
.vm-tempo-mark { display: flex; align-items: center; gap: 8px; padding: 2px 2px 2px 8px; border-radius: 10px; }
.vm-tempo-mark.here { background: #1c2943; box-shadow: inset 0 0 0 1px #3d5f9f; }
.vm-tempo-place {
  flex: 1; min-width: 0; height: 44px; padding: 0; border: 0; background: none; color: #d4d7dd;
  display: flex; flex-direction: column; justify-content: center; align-items: flex-start; text-align: left;
}
.vm-tempo-place-name { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.vm-tempo-plays { font-size: 12px; color: #f0b44c; font-variant-numeric: tabular-nums; }
.vm-tslider:focus-visible .vm-tslider-thumb { box-shadow: 0 0 0 3px #6fa0f2, 0 2px 6px rgba(0, 0, 0, 0.55); }

/* A recording that is not here: where Speed would be, in the left half. */
.vm-strip-note { max-width: 200px; font-size: 13px; line-height: 1.3; color: #9aa1ad; text-align: right; }
/* Before the song is open: in the middle, where Play will be. */
.vm-strip-waiting { grid-column: 2; text-align: center; max-width: none; }
/* A long size wraps it onto a second line rather than past its half of the
   strip, where it would run under Play. */
.vm-ctl.vm-get { max-width: 220px; padding: 0 14px; border-radius: 11px; font-size: 14px; line-height: 1.15; white-space: normal; text-align: left; }

/* Where the music is, at the far right: the bar, and a dot a beat. */
.vm-readout {
  position: absolute; right: var(--vm-side); top: 10px; height: 48px;
  display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 6px;
  pointer-events: none;
}
.vm-readout-text { font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.vm-readout-of { color: #9aa1ad; font-weight: 500; }
.vm-readout-dots { display: flex; gap: 5px; }
.vm-readout-dots:empty { display: none; }
.vm-readout-dots i { width: 6px; height: 6px; border-radius: 50%; background: #363b45; }
.vm-readout-dots i.on { background: #6fa0f2; }

/* The track view (js/practicetrack.js): docked above the strip, so Play,
   Loop and Speed stay where they are; the whole song along the top, a
   stretch of it on the beat grid under that with the loop's A and B, and
   under that the two ends in words, to move a beat at a time. It covers the
   foot of the page while it is open. */
.vm-track {
  position: absolute; left: 0; right: 0; bottom: var(--vm-strip-h, 64px); z-index: 4;
  height: 304px; box-sizing: border-box;
  display: flex; flex-direction: column;
  background: #16181c; color: #eceef2;
  border-top: 1px solid #30343c; box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.45);
  font: 14px/1.25 system-ui, -apple-system, sans-serif;
  touch-action: none;
}
.vm-track-head {
  flex: none; height: 52px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 0 12px 0 max(18px, env(safe-area-inset-left));
}
.vm-track-title { display: flex; align-items: baseline; gap: 10px; min-width: 0; white-space: nowrap; }
.vm-track-name { font-size: 15px; font-weight: 600; }
.vm-track-sub { font-size: 13px; font-weight: 500; color: #9aa1ad; overflow: hidden; text-overflow: ellipsis; }
.vm-track-acts { display: flex; align-items: center; gap: 8px; flex: none; }
.vm-track-btn {
  height: 44px; padding: 0 14px; box-sizing: border-box;
  border: 1px solid #343943; border-radius: 10px; background: #262a31;
  font-size: 14px; font-weight: 600;
}
.vm-track-btn:active { background: #30353d; }
.vm-track-close {
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: 50%; background: none;
  display: grid; place-items: center; color: #c3c8d0;
}
.vm-track-close svg { padding: 10px; border-radius: 50%; background: #2a2d34; box-sizing: content-box; }
.vm-track-over { position: relative; flex: none; height: 30px; margin: 0 16px; border-radius: 4px; overflow: hidden; touch-action: none; cursor: pointer; }
.vm-track-zoom { position: relative; flex: none; height: 156px; margin: 8px 16px 0; touch-action: none; cursor: grab; }
.vm-track-canvas { position: absolute; left: 0; top: 0; width: 100%; height: 100%; display: block; }
.vm-track-ph, .vm-track-oph { position: absolute; left: 0; width: 2px; margin-left: -1px; background: #fff; pointer-events: none; }
.vm-track-ph { top: 14px; bottom: 0; z-index: 1; }
.vm-track-ph::before {
  content: ""; position: absolute; left: -4px; top: -4px;
  border: 5px solid transparent; border-top: 6px solid #fff; border-bottom: 0;
}
.vm-track-oph { top: 0; bottom: 0; width: 1.5px; }
/* The playhead's head, to take hold of: the ruler's height, a finger wide. */
.vm-ph-grab { position: absolute; left: -21px; top: -14px; width: 44px; height: 30px; pointer-events: auto; touch-action: none; cursor: ew-resize; }
.vm-track-ph[hidden], .vm-track-oph[hidden], .vm-hdl[hidden], .vm-track-empty[hidden] { display: none; }
/* A and B: a line on the beat each is on, and a tab to take hold of. */
/* Below the ruler, which is the playhead's to take hold of. */
.vm-hdl { position: absolute; top: 18px; bottom: 0; width: 44px; margin-left: -22px; z-index: 2; touch-action: none; cursor: ew-resize; }
.vm-hdl-line { position: absolute; left: 21px; top: 0; bottom: 0; width: 2px; background: #6fa0f2; }
.vm-hdl-tab {
  position: absolute; left: 8px; bottom: -1px; width: 28px; height: 26px; border-radius: 7px;
  background: #2f62c9; color: #fff; font-size: 13px; font-weight: 700; line-height: 26px; text-align: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
}
.vm-hdl.held .vm-hdl-tab { background: #4479e0; }
.vm-track-empty {
  position: absolute; left: 0; right: 0; top: 18px; bottom: 0; z-index: 3;
  display: grid; place-items: center; pointer-events: none;
  font-size: 14px; font-weight: 500;
}
.vm-track-empty span { padding: 9px 14px; border: 1px solid #343943; border-radius: 10px; background: rgba(21, 23, 27, 0.92); }
.vm-track-foot {
  flex: 1; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 0 max(12px, env(safe-area-inset-right)) 0 max(12px, env(safe-area-inset-left));
}
.vm-track-edge { display: flex; align-items: center; gap: 6px; }
.vm-nudge {
  width: 44px; height: 44px; padding: 0; box-sizing: border-box;
  display: grid; place-items: center;
  border: 1px solid #343943; border-radius: 10px; background: #262a31;
}
.vm-nudge:active { background: #30353d; }
.vm-track-words {
  display: flex; align-items: center; justify-content: center; gap: 8px; min-width: 196px;
  font-size: 14px; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.vm-abk { display: inline-grid; place-items: center; width: 22px; height: 20px; border-radius: 5px; background: #2f62c9; color: #fff; font-size: 12px; font-weight: 700; }
.vm-track-reset { visibility: hidden; font-size: 15px; }

/* A loop moved off its boxes: the part of a box it takes in, shaded. */
.vm-reach {
  position: absolute; top: 0; bottom: 0; pointer-events: none;
  background: rgba(47, 98, 201, 0.16); box-shadow: inset 0 -3px 0 #2f62c9;
}

/* The strip's popovers (js/practicemix.js): the mixer, from Mix, and the
   click's settings, from the arrow beside Click. Over the page, just above
   the strip, pointing at the button that opened them. */
.vm-pop {
  position: absolute; z-index: 6; box-sizing: border-box;
  background: #1b1e23; color: #eceef2;
  border: 1px solid #343943; border-radius: 16px;
  box-shadow: 0 22px 54px rgba(0, 0, 0, 0.6), 0 2px 10px rgba(0, 0, 0, 0.35);
  font: 14px/1.25 system-ui, -apple-system, sans-serif;
  touch-action: none;
}
.vm-pop-arrow {
  position: absolute; bottom: -7px; width: 14px; height: 14px;
  background: #1b1e23; border-right: 1px solid #343943; border-bottom: 1px solid #343943;
  border-bottom-right-radius: 3px; transform: rotate(45deg);
}
.vm-pop-head {
  display: flex; align-items: center; justify-content: space-between;
  height: 48px; padding: 0 8px 0 18px; border-bottom: 1px solid #2a2e35;
}
.vm-pop-title { font-size: 15px; font-weight: 600; }
.vm-pop-text { height: 44px; padding: 0 10px; border: 0; background: none; color: #6fa0f2; font-size: 15px; font-weight: 500; }
.vm-pop-text:disabled { color: #565c67; }

/* The mixer: a channel each, its fader, and under the fader its own mute and
   solo as icons. A channel not heard is greyed; its buttons stay bright,
   since they are the way back. */
.vm-mix-row { display: flex; align-items: flex-end; padding: 12px 10px 14px; }
.vm-mix-sep { align-self: stretch; width: 1px; margin: 4px 5px 0; background: #2a2e35; }
.vm-mix-group { display: flex; flex-direction: column; }
.vm-mix-cap { display: flex; align-items: center; gap: 8px; height: 14px; margin: 0 12px 8px; font-size: 11px; font-weight: 600; color: #9aa1ad; }
.vm-mix-cap::before, .vm-mix-cap::after { content: ""; flex: 1; height: 1px; background: #30343c; }
.vm-mix-chs { display: flex; }
.vm-ch { flex: 0 0 auto; width: 86px; display: flex; flex-direction: column; align-items: center; }
.vm-ch-name {
  width: 84px; height: 18px; font-size: 13px; font-weight: 600; line-height: 18px;
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vm-ch-name.busy { color: #6fa0f2; font-weight: 500; }
.vm-fader { position: relative; width: 60px; height: 156px; margin: 6px 0 10px; touch-action: none; cursor: grab; }
.vm-fader.dragging { cursor: grabbing; }
.vm-fader-track { position: absolute; left: 50%; top: 12px; bottom: 12px; width: 6px; margin-left: -3px; border-radius: 3px; background: #2b2f37; }
.vm-fader-fill { position: absolute; left: 0; right: 0; bottom: 0; border-radius: 3px; background: #6fa0f2; }
.vm-fader-cap {
  position: absolute; left: 50%; width: 42px; height: 24px; margin-left: -21px; border-radius: 6px;
  background: linear-gradient(#eceef2, #c8ccd3); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55);
}
.vm-fader-cap::after { content: ""; position: absolute; left: 8px; right: 8px; top: 11px; height: 2px; border-radius: 1px; background: #5c626d; }
/* The dB scale beside a fader (Adam, 23 Oct 2026), quiet: +6, 0 a little
   brighter, −12 and −∞, in the room left of the cap, each with a short tick
   by the track. Over the track's own height, so a mark is where the cap's
   middle stands at that level. The level itself is said in place of the
   channel's name while the fader is dragged. */
.vm-fader-scale { position: absolute; top: 12px; bottom: 12px; left: -13px; width: 20px; pointer-events: none; }
.vm-fader-mark {
  position: absolute; right: 0; transform: translateY(-50%);
  font-size: 9px; line-height: 10px; font-weight: 500; font-variant-numeric: tabular-nums; white-space: nowrap;
  color: #69707b;
}
.vm-fader-mark::after { content: ""; position: absolute; left: calc(100% + 14px); top: 50%; width: 4px; height: 1px; background: #474c56; }
.vm-fader-mark.zero { color: #a3aab5; font-weight: 600; }
.vm-fader-mark.zero::after { background: #7c838e; }
.vm-ch-name.level { font-weight: 500; font-variant-numeric: tabular-nums; color: #9fc0f7; }
.vm-ch.silent .vm-ch-name, .vm-ch.silent .vm-fader { opacity: 0.38; }
.vm-ch.silent .vm-ch-name.busy { opacity: 1; }
.vm-ch-keys { display: flex; flex-direction: column; gap: 6px; }
.vm-key-space { width: 44px; height: 44px; }
.vm-key {
  width: 44px; height: 44px; box-sizing: border-box; padding: 0;
  display: grid; place-items: center;
  border: 1px solid #343943; border-radius: 10px; background: #262a31; color: #b9bec8;
}
.vm-key:active:not(:disabled) { background: #30353d; }
.vm-key.vm-mute.on { background: #21407e; border-color: #4479e0; color: #a9c8ff; }
.vm-key.vm-solo.on { background: #f0b44c; border-color: #f0b44c; color: #22180a; }
.vm-key:disabled { opacity: 0.4; }

/* The click's settings: the pulse as notes, the accent, the count-in. */
.vm-clickpop { width: 384px; }
.vm-prow {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 60px; padding: 0 12px 0 18px; border-bottom: 1px solid #2a2e35;
}
.vm-prow:nth-last-child(2) { border-bottom: 0; }
.vm-prow-name { font-size: 15px; font-weight: 500; white-space: nowrap; }
.vm-seg2 { display: inline-flex; gap: 2px; padding: 2px; border-radius: 11px; background: #2a2d34; }
.vm-seg2-choice {
  min-width: 64px; height: 44px; padding: 0 10px; border: 0; border-radius: 9px;
  display: grid; place-items: center;
  background: none; color: #d4d7dd; font-size: 14px; font-weight: 600; white-space: nowrap;
}
.vm-seg2-choice.vm-pulse { min-width: 50px; padding: 0 6px; }
.vm-seg2-choice.on { background: #575d69; color: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35); }
.vm-seg2-choice:disabled { opacity: 0.4; }
/* The switch is 44px tall to touch; what is drawn is iOS's 51 by 31. */
.vm-switch { flex: none; width: 55px; height: 44px; padding: 0; border: 0; background: none; display: grid; place-items: center; }
.vm-switch-track { position: relative; width: 51px; height: 31px; border-radius: 16px; background: #3a3f48; transition: background 0.2s; }
.vm-switch i { position: absolute; top: 2px; left: 2px; width: 27px; height: 27px; border-radius: 50%; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); transition: transform 0.2s; }
.vm-switch.on .vm-switch-track { background: #2f62c9; }
.vm-switch.on i { transform: translateX(20px); }
@media (prefers-reduced-motion: reduce) { .vm-switch-track, .vm-switch i { transition: none; } }

/* The popovers opened from the bar in Build, Map and Score (playcluster.js):
   the same mixer and click settings, put over everything, below the bar,
   their arrow pointing up at the button. */
.pc-pops { position: fixed; inset: 0; z-index: 800; pointer-events: none; }
.pc-pops > .vm-pop { pointer-events: auto; }
.vm-pop.below .vm-pop-arrow { bottom: auto; top: -7px; transform: rotate(225deg); }

/* The bar's play controls (playcluster.js): Practice's Speed, Loop, Play,
   Click and Mix, the same shapes, sized for the bar (40px, as Play was
   there; 44px on a touch screen) and in the bar's own colours, light or
   dark. */
.play-cluster.pc-bar { --pc-h: 40px; display: inline-flex; align-items: center; gap: 6px; }
@media (hover: none) and (pointer: coarse) { .play-cluster.pc-bar { --pc-h: 44px; } }
.pc-bar .vm-split > button, .pc-bar .vm-mix-btn, .pc-bar .pc-loop { height: var(--pc-h); }
.pc-bar .vm-split > button, .pc-bar .vm-mix-btn, .pc-bar .pc-loop {
  border-color: var(--line); background: var(--paper); color: var(--ink); font-size: 14px;
}
.pc-bar .pc-loop {
  box-sizing: border-box; padding: 0 14px 0 12px; border: 1px solid var(--line); border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-weight: 600; white-space: nowrap;
}
.pc-bar .vm-split-main { width: 84px; }
.pc-bar .vm-split-door { width: 36px; color: var(--ink-soft); }
.pc-bar .vm-mix-btn { width: 46px; }
.pc-bar .vm-mix-btn .vm-mix-dot { top: 6px; right: 7px; }
.pc-bar .vm-split > button:hover:not(:disabled), .pc-bar .vm-mix-btn:hover, .pc-bar .pc-loop:hover { background: var(--line-soft); }
.pc-bar .vm-split > button:active:not(:disabled), .pc-bar .vm-mix-btn:active, .pc-bar .pc-loop:active { background: var(--line-soft); }
/* On wins over the pointer resting on it or pressing it (those rules are
   one class stronger with their :not(:disabled)): the click lit only once
   the pointer left it, and in the Mac app, where a click leaves the pointer's
   state on the button, only after a click somewhere else (Adam, 15 Oct). */
.pc-bar .vm-split > .vm-split-main.on, .pc-bar .pc-loop.on,
.pc-bar .vm-split > .vm-split-main.on:hover:not(:disabled), .pc-bar .pc-loop.on:hover,
.pc-bar .vm-split > .vm-split-main.on:active:not(:disabled), .pc-bar .pc-loop.on:active { background: var(--accent-fill); border-color: var(--accent-fill); color: var(--accent-ink); }
.pc-bar .vm-split > .vm-split-door.open, .pc-bar .vm-mix-btn.open,
.pc-bar .vm-split > .vm-split-door.open:hover:not(:disabled), .pc-bar .vm-mix-btn.open:hover,
.pc-bar .vm-split > .vm-split-door.open:active:not(:disabled), .pc-bar .vm-mix-btn.open:active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.pc-bar .vm-mix-dot { background: var(--accent); box-shadow: 0 0 0 2px var(--paper); }
.pc-bar .vm-mix-btn.open .vm-mix-dot { box-shadow: none; }
.pc-bar .pc-tempo-tag { box-shadow: 0 0 0 2px var(--paper); }
.pc-bar .vm-play { width: var(--pc-h); height: var(--pc-h); flex: none; }
.pc-bar .vm-play svg { width: 20px; height: 20px; }
/* The bar's half of the window is not the strip's whole width: where the
   window is narrower than a desktop's (an iPad's 1180), Loop and Click are
   their icons, so nothing on the bar reaches the view switch. */
@media (max-width: 1299px) {
  .pc-bar .pc-label { display: none; }
  .pc-bar .pc-loop { width: var(--pc-h); padding: 0; }
  .pc-bar .vm-split-main { width: var(--pc-h); }
}

/* A box's right-click menu (phraseMenuPanel in editor.js): the Export menu's
   panel, put where the click was. */
.phrase-menu {
  position: fixed;
  right: auto;
  bottom: auto;
  transform: none;
  min-width: 170px;
  gap: 2px;
  padding: 6px;
  z-index: 60;
}
.phrase-menu .settings-item { justify-content: flex-start; }
.phrase-menu .settings-item.danger { color: var(--danger); }
.phrase-menu-rule { height: 1px; margin: 3px 4px; background: var(--line); }
.phrase-menu-times { display: flex; align-items: center; gap: 6px; padding: 4px 8px; }
.phrase-menu-times > span { flex: 1; color: var(--ink-soft); }
.phrase-menu-times strong { min-width: 2.2em; text-align: center; font-variant-numeric: tabular-nums; }
.phrase-menu-tempo { display: flex; flex-direction: column; gap: 6px; padding: 6px 8px 4px; }
.phrase-menu-tempo-name { color: var(--ink-soft); }
.phrase-menu-tempo-set { display: flex; align-items: center; gap: 6px; }
.phrase-menu-tempo-input { width: 4.5em; font: inherit; font-variant-numeric: tabular-nums; }
.phrase-menu-tempo .settings-item { margin: 0 -8px; }

/* The Mac app (WKWebView through Mac Catalyst) treats any text a person
   could select as text: the pointer turns into the typing bar over buttons
   and menu items, and a press on them can start a selection instead of a
   click, which is how the ≡ menu's Light/Dark and the pickup's beats could
   be pressed and do nothing (Adam, 15 Oct). Nothing in the app is text to
   select except what is typed into, so only the boxes keep it; everything
   that can be pressed shows the pointing hand. */
.drumchart-mac body { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.drumchart-mac input, .drumchart-mac textarea, .drumchart-mac [contenteditable="true"] {
  -webkit-user-select: text; user-select: text;
}
.drumchart-mac input[type="checkbox"], .drumchart-mac input[type="radio"], .drumchart-mac input[type="range"],
.drumchart-mac button, .drumchart-mac select, .drumchart-mac a, .drumchart-mac label,
.drumchart-mac [role="button"], .drumchart-mac [role="menuitem"] { cursor: pointer; }
.drumchart-mac button:disabled { cursor: default; }

/* The Mac app's menu bar does the work of the bars' own menus and buttons
   (macmenu.js, Mac.swift): no ≡, no undo and redo, no name or Feedback over
   the chart list, and "+ New chart" leads the list instead (Adam, 15 Oct). */
.drumchart-mac .settings-cluster,
.drumchart-mac .undo-btn, .drumchart-mac .redo-btn,
.drumchart-mac .toolbar .app-title, .drumchart-mac .toolbar .feedback-btn,
.drumchart-mac .toolbar > .new-song { display: none !important; }

/* --- design C's layout on the website ---------------------------------------
   Build and the chart list laid out as the Mac look's design C, in the
   website's own colours, type and controls: Adam's blind pick on the website
   (21 Oct 2026), with Map's. Only the layout moved: what each thing is made
   of stays the website's. Score kept the website's own on the same trial. On
   a screen with a pointer only (native.js macLayout): a touch screen and the
   iPad app keep their own, and the Mac app has mac-build.css and
   mac-home.css. */
@media not ((hover: none) and (pointer: coarse)) {
  /* Build: the chart as the document on the left, the editor down the right,
     and under the grid, on one line, everything about the selected box that
     is not its music (This box, editor.js thisBox), so selecting a box adds
     nothing to the chart. */
  /* Beside the chart only where it is beside it: under 900px wide the
     editor stacks above the chart, as before. */
  @media (min-width: 901px) {
    html:not(.drumchart-mac):not(.drumchart-app) .split { flex-direction: row-reverse; }
    html:not(.drumchart-mac):not(.drumchart-app) .editor-pane { border-right: 0; border-left: 1px solid var(--line); }
  }
  html:not(.drumchart-mac):not(.drumchart-app) .pep-head .pc-summary strong { color: var(--ink); font-weight: 600; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-this-box {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--line);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-part { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-labelled { flex: 1 1 180px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-label { color: var(--ink-soft); font-size: 12px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-note { color: var(--ink-soft); font-size: 12px; white-space: nowrap; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-stepper { display: inline-flex; align-items: center; gap: 4px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-times { display: inline-flex; align-items: center; gap: 2px; color: var(--ink-soft); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-times { width: 48px; text-align: center; font-variant-numeric: tabular-nums; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-label-input { flex: 1 1 auto; min-width: 70px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-seg-icons { display: inline-flex; flex: none; gap: 2px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-seg-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 5px;
    background: var(--paper);
    color: var(--ink-soft);
    cursor: pointer;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-seg-icon.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-actions { margin-left: auto; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-box-ico { display: none; }
  /* A narrower window (an iPad's width, 1024): the panel is 470px, and the
     line stays one line by leaving "= N bars" to the heading, which says it,
     and Duplicate and Delete to their icons. */
  @media (max-width: 1180px) {
    html:not(.drumchart-mac):not(.drumchart-app) .mac-this-box { column-gap: 12px; }
    html:not(.drumchart-mac):not(.drumchart-app) .mac-box-note,
    html:not(.drumchart-mac):not(.drumchart-app) .mac-box-word { display: none; }
    html:not(.drumchart-mac):not(.drumchart-app) .mac-box-ico { display: inline-flex; }
    html:not(.drumchart-mac):not(.drumchart-app) .mac-box-actions .btn { min-width: 32px; padding: 0 8px; justify-content: center; }
  }
  /* The number on the box takes a click to This box (goToPlayed in chart.js). */
  html:not(.drumchart-mac):not(.drumchart-app) .phrase .ph-bars { cursor: pointer; }

  /* The chart list: the whole window, a source list down the left (All charts
     and the setlists, + New setlist at its foot), the charts as a table sorted
     by its headings with Tempo and Length, a status line along the foot, and
     the list's name, its tools, + New chart and the search in the toolbar. */
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-bar { flex-wrap: nowrap; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-bar .app-title { margin-right: 6px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-doc { display: inline-flex; flex-direction: column; justify-content: center; min-width: 0; line-height: 1.2; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-doc { flex: 0 1 auto; max-width: 280px; padding-left: 12px; border-left: 1px solid var(--line); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-doc-title { font-weight: 600; font-size: 14px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-doc-sub { font-size: 12px; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-doc input.mac-doc-title {
    width: 260px;
    max-width: 100%;
    margin: 0 0 0 -5px;
    padding: 1px 4px;
    border: 1px solid transparent;
    border-radius: 5px;
    background: transparent;
    font: inherit;
    font-weight: 600;
    font-size: 14px;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-doc input.mac-doc-title:hover { border-color: var(--line); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-space { flex: 1 1 auto; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-tools { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-bar .btn.mac-tool.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-ico { display: inline-flex; line-height: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .btn .mac-ico + * { margin-left: 4px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-play { color: var(--accent); margin-right: 4px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-chev { display: inline-flex; line-height: 0; margin-left: 4px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-bar-sep { width: 1px; height: 22px; background: var(--line); margin: 0 2px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home-bar .set-picker { top: calc(100% + 8px); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-search { position: relative; display: inline-flex; align-items: center; flex: 0 1 220px; min-width: 120px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-search-ico { position: absolute; left: 9px; display: inline-flex; color: var(--ink-soft); pointer-events: none; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-search input { width: 100%; padding-left: 28px; }

  html:not(.drumchart-mac):not(.drumchart-app) .library.mac-home { padding: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-home > .library-body { max-width: none; margin: 0; gap: 0; height: 100%; }
  html:not(.drumchart-mac):not(.drumchart-app) .set-nav.mac-side {
    flex: 0 0 210px;
    align-self: stretch;
    max-height: none;
    padding: 10px 8px 0;
    background: var(--paper);
    border-right: 1px solid var(--line);
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-side .set-nav-head:first-child { margin-top: 4px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-side .set-nav-ico { display: inline-flex; width: 16px; justify-content: center; color: var(--accent); line-height: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-side-none { margin: 2px 10px 6px; font-size: 12px; line-height: 1.4; color: var(--ink-faint); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-side .set-nav-new {
    position: sticky;
    bottom: 0;
    margin: auto -8px 0;
    width: calc(100% + 16px);
    padding: 0 18px;
    background: var(--paper);
    border-top: 1px solid var(--line);
    border-radius: 0;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-main > .backup-note { width: auto; max-width: none; margin: 12px 14px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-list { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-list > .select-bar { margin: 0; padding: 6px 14px; border-bottom: 1px solid var(--line); }

  html:not(.drumchart-mac):not(.drumchart-app) .song-table.mac-table { flex: 1 1 auto; max-height: none; border: 0; border-top: 1px solid var(--line); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .song-row.mac-row {
    --pos-w: 10px;
    /* Tempo and Length wide enough for their heading with the sort arrow:
       at 70px, sorted by Length, it read "ENGTH" (Adam, 25 Oct). */
    /* The tools: View, Edit and ⋯ (Edit beside View since 25 Oct). */
    grid-template-columns: var(--pos-w) 28px minmax(140px, 1.3fr) minmax(100px, 1fr) 84px 84px 190px;
    column-gap: 0;
    min-height: 40px;
    font-size: 14px;
    color: var(--ink);
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table.manual .song-row.mac-row { --pos-w: 34px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table.set-table .song-row.mac-row { --pos-w: 32px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table.set-table.manual .song-row.mac-row { --pos-w: 60px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .mac-row > * { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 10px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .mac-row > :is(.mac-col-pos, .mac-col-star) { padding: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .num { text-align: right; font-variant-numeric: tabular-nums; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table :is(.mac-col-artist, .mac-col-tempo, .mac-col-length) { color: var(--ink-soft); font-size: 13px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-title { font-weight: 600; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .song-table-head.mac-row { min-height: 30px; font-size: 11px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-head-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    width: 100%;
    height: 30px;
    padding: 0 10px;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-faint);
    text-align: left;
  }
  html:not(.drumchart-mac):not(.drumchart-app) button.mac-head-btn { cursor: pointer; }
  html:not(.drumchart-mac):not(.drumchart-app) .song-table-head .mac-row > * { padding: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .song-table-head > :is(.mac-col-title, .mac-col-artist, .mac-col-tempo, .mac-col-length) { padding: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .num > .mac-head-btn { justify-content: flex-end; }
  html:not(.drumchart-mac):not(.drumchart-app) button.mac-head-btn:hover { color: var(--ink); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-head-btn.on { color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-head-arrow { display: inline-flex; line-height: 0; }
  html:not(.drumchart-mac):not(.drumchart-app) .set-table .song-table-head .mac-col-pos .mac-head-btn { justify-content: flex-end; padding: 0 6px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .song-table-body > .mac-row { cursor: default; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .song-table-body > .mac-row.selected,
  html:not(.drumchart-mac):not(.drumchart-app) .mac-table .song-table-body > .mac-row.pick-row.picked { background: var(--accent-soft); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row.selected .mac-title { color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-col-pos { display: flex; align-items: center; justify-content: flex-end; gap: 2px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row .set-pos { min-width: 22px; padding-right: 6px; text-align: right; font-size: 12px; color: var(--ink-soft); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-col-star { display: flex; align-items: center; justify-content: center; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-col-tools { display: flex; justify-content: flex-end; padding-right: 10px !important; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row-tools { display: inline-flex; gap: 6px; visibility: hidden; }
  html:not(.drumchart-mac):not(.drumchart-app) :is(.mac-row:hover .mac-row-tools, .mac-row.selected .mac-row-tools, .mac-row-tools:focus-within) { visibility: visible; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-height: 28px;
    padding: 0 9px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--paper);
    color: var(--ink);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row-btn:hover { border-color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row-btn .mac-ico { color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row-btn.more-btn { padding: 0 6px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-row-btn.more-btn .mac-ico { color: inherit; }

  html:not(.drumchart-mac):not(.drumchart-app) .mac-foot {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 30px;
    padding: 0 14px;
    border-top: 1px solid var(--line);
    background: var(--paper);
    font-size: 12px;
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-foot-hint { margin-left: auto; color: var(--ink-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-empty {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 40px 20px 80px;
    text-align: center;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-empty h2 { margin: 0; font-size: 16px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-empty p { margin: 0; max-width: 380px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-empty-mark { display: inline-flex; color: var(--ink-faint); margin-bottom: 6px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-empty-mark .app-mark { width: 52px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-empty-actions { display: flex; gap: 8px; margin-top: 10px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-link { margin-top: 6px; border: 0; background: none; font: inherit; font-size: 13px; color: var(--accent); cursor: pointer; }

  /* The list's menus (+ New chart, Sort, a chart's ⋯ and right-click;
     macpopup.js) in the website's own: as its ≡ menu is drawn. */
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu {
    position: fixed;
    z-index: 900;
    min-width: 220px;
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 1px;
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    font-size: 13px;
    color: var(--ink);
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-head { font-size: 11px; font-weight: 600; color: var(--ink-soft); padding: 4px 10px 4px; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-item {
    appearance: none;
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 30px;
    padding: 0 10px 0 8px;
    border: 0;
    border-radius: 4px;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
  }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-item:is(:hover, :focus-visible, .hi):not(:disabled) { background: var(--accent-soft); outline: none; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-item:disabled { color: var(--ink-faint); cursor: default; }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-item.danger { color: var(--danger); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-key { margin-left: auto; padding-left: 22px; color: var(--ink-faint); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-ico { width: 16px; display: inline-flex; justify-content: center; color: var(--accent); }
  html:not(.drumchart-mac):not(.drumchart-app) .mac-menu-sep { height: 1px; margin: 3px 4px; background: var(--line); }

  /* A narrow window: the numbers give way to the names. */
  @media (max-width: 860px) {
    html:not(.drumchart-mac):not(.drumchart-app) .mac-table .song-row.mac-row { grid-template-columns: var(--pos-w) 28px minmax(120px, 1.3fr) minmax(80px, 1fr) 0 0 180px; }
    html:not(.drumchart-mac):not(.drumchart-app) .mac-table :is(.mac-col-tempo, .mac-col-length) { visibility: hidden; padding: 0; }
    html:not(.drumchart-mac):not(.drumchart-app) .set-nav.mac-side { flex-basis: 170px; }
  }
}

/* The label typed on a box, over its sheet in the groove editor (with boxes
   joined, joins.js), as the box draws it on the chart: the chart's type for a
   label (.ph-groove-label in chart.css, with the chart's own grey, blue for a
   lyric, and its font), over the top of the drawing from the start of the
   staff, or its middle or end (.ph-label-line.align-*), the way the finished
   page lays one over its staff's frame. Laid over it, so the window keeps its
   height and the grid under it does not move. */
.ge-preview {
  position: relative;
  --chart-markings: 1;
  --chart-faint: #7b818c;
  --chart-lyric: #2456a6;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.ge-preview > .notation { position: relative; }
.ge-preview-label {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin: 0;
  line-height: 1;
  white-space: nowrap;
  pointer-events: none;
}

/* Boxes joined: the bar while boxes are being picked on a touch screen
   (select mode, editor.js), at the top of the chart under the app's bar: at
   the foot it lay over the groove editor docked under the chart. */
.join-select-bar {
  position: fixed;
  left: 50%;
  top: calc(var(--bar-h, 56px) + 10px);
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100vw - 32px);
  padding: 8px 10px;
  border-radius: 12px;
  background: var(--raised, #2a2e36);
  color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
  font-size: 14px;
}
.join-select-bar .btn { min-height: 40px; min-width: 44px; }
.join-select-bar .jsb-count { font-weight: 700; white-space: nowrap; }
.join-select-bar .jsb-times { display: inline-flex; align-items: center; gap: 6px; }
.join-select-bar .jsb-times strong { min-width: 2.5ch; text-align: center; font-variant-numeric: tabular-nums; }
.join-select-bar .jsb-hint { font-size: 12px; max-width: 18em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.editor-pane-empty.join-pane { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.editor-pane-empty.join-pane p { margin: 0; }
/* What the box plays, in the editor's heading in design C's Build (with
   boxes joined): the heading says what the box is, and This box under the
   grid has no room left on its one line. */
.pep-head .groove-pick {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 12em;
  height: 24px;
  padding: 0 4px;
  border-radius: 5px;
  border: 0;
  box-shadow: inset 0 0 0 1px var(--line);
  background: var(--chrome);
  color: var(--ink);
  font-size: 13px;
}
.phrase-menu .settings-item:disabled { opacity: 0.45; cursor: default; }

/* --- the sign-in (account.js) ------------------------------------------- */
/* Before the app, in look A of the mockups (Adam, 26 Oct 2026): a card in the
   middle of the dark room, the mark and name above it. Over everything, so
   the app behind it never shows through or takes a key. */
.door { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: center; justify-content: center;
  padding: max(24px, env(safe-area-inset-top)) 20px max(24px, env(safe-area-inset-bottom)); background: var(--chrome); overflow-y: auto; }
.door-col { width: 100%; max-width: 380px; display: flex; flex-direction: column; align-items: stretch; gap: 22px; margin: auto; }
.door-brand { display: flex; align-items: center; justify-content: center; gap: 10px; font-weight: 700; font-size: 22px; letter-spacing: -0.01em; }
.door-mark { display: contents; }
.door-brand .app-mark { width: 34px; }
.door-card { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: 26px 24px; box-shadow: var(--shadow-2); }
.door-form { display: flex; flex-direction: column; gap: 12px; }
.door-form h2 { font-size: 22px; margin: 0; letter-spacing: -0.01em; }
.door-form p { margin: 0; color: var(--ink-soft); }
.door-label { font-size: 13px; color: var(--ink-soft); display: flex; flex-direction: column; gap: 6px; }
.door-field { font-size: 17px; padding: 11px 12px; min-height: 46px; border-radius: 8px; width: 100%; box-sizing: border-box; }
.door-go { min-height: 46px; font-size: 16px; font-weight: 600; border-radius: 8px; }
.door-go:disabled { opacity: 0.45; }
.door-check { font-size: 13.5px; color: var(--ink-soft); display: flex; align-items: center; gap: 8px; cursor: pointer; }
.door-check input { margin: 0; width: 16px; height: 16px; flex: none; }
.door-trial { font-size: 13.5px; }
.door-trial strong { color: var(--ink); font-weight: 600; }
.door-error { color: var(--danger); font-size: 13px; }
.door-error:empty { display: none; }
.door-links { display: flex; gap: 16px; flex-wrap: wrap; font-size: 13.5px; }
.door-link, .door-later { background: none; border: 0; padding: 0; min-height: 0; color: var(--accent); font: inherit; cursor: pointer; }
.door-link:disabled { color: var(--ink-faint); cursor: default; }
.door-later { align-self: center; color: var(--ink-faint); font-size: 13px; }
/* The code: one field (so the browser can offer it from Mail), drawn as six. */
.door-codewrap { position: relative; }
.door-code { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; font-size: 20px; border: 0; min-height: 0; padding: 0; }
.door-boxes { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; pointer-events: none; }
.door-boxes span { height: 54px; border-radius: 8px; border: 1px solid var(--line); background: var(--chrome);
  display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: 600; font-variant-numeric: tabular-nums; }
.door-boxes span.next { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent); }

/* --- the free trial (trial.js) --------------------------------------------- */
/* The days left, a small pill beside the ≡ menu: never on the chart itself. */
.trial-pill { min-height: 0; padding: 3px 10px; border-radius: 999px; font-size: 12.5px; font-weight: 600; white-space: nowrap;
  background: var(--accent-soft); border-color: transparent; color: var(--accent); }
.trial-pill.ended { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }
/* The Buy card: the sign-in's card, over the app rather than in place of it. */
.door.buy-door { background: color-mix(in srgb, var(--chrome) 82%, transparent); z-index: 2900; }
.buy-test { font-size: 12.5px; color: var(--ink-faint); border-top: 1px solid var(--line); padding-top: 10px; align-items: baseline; }
/* Teacher sharing (sharebox.js, shared.js): the Share box and Shared with me. */
.share-door .door-card { max-width: 460px; }
.share-link { font-size: 13.5px; font-family: var(--mono, ui-monospace, monospace); }
.share-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.share-actions .btn { min-height: 40px; }
.share-head { font-size: 14px; margin: 6px 0 0; }
.share-students { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; max-height: 180px; overflow: auto; }
.share-students li { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; }
.share-email { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-soft); }
.door-link.danger { color: var(--danger); }
.shared-nav-item { display: flex; align-items: center; }
.shared-nav-item > .set-nav-item { flex: 1; min-width: 0; }
.shared-nav-more { background: none; border: 0; color: var(--ink-faint); font-size: 18px; padding: 0 8px; min-height: 0; cursor: pointer; }
.shared-nav-more:hover { color: var(--ink); }
.shared-note, .vm-side-note { font-size: 12.5px; color: var(--ink-faint); margin: 6px 10px; line-height: 1.35; }
