/* Light by default — dark is opt-in via the toggle, stored in localStorage. */
:root{
  --bg:#f7f8fa; --panel:#fff; --panel2:#f1f3f7; --line:#e2e5ec; --line2:#eef0f5;
  --fg:#1a1c23; --muted:#6b7085; --accent:#3563e9; --accent-fg:#fff;
  --bad:#b42318; --r:7px;
  --p1bg:#fdeaea; --p1fg:#b42318; --p2bg:#fdf1e3; --p2fg:#b54708;
  --p3bg:#e9effd; --p3fg:#1849a9; --p4bg:#e3f8ec; --p4fg:#067647;
  --p5bg:#d3f5e2; --p5fg:#05603a; --pxbg:#f2f3f7; --pxfg:#8b90a3;
  /* Random mode. Accent-tinted on purpose: it is a mode, not a rating, so it
     must not land anywhere on the p1–p5 red-to-green ramp. */
  --rndbg:#e6ecfd; --rndfg:#1849a9; --rndline:#a9bef6;
  /* Goals calendar: solid, high-contrast fills rather than the pastel p1–p5
     ramp, and two different SHAPES (a fill vs. a dot) for practice vs.
     recording rather than two colours on the same cell — legible without
     asking anyone to tell two hues apart. */
  --ghit:#15803d; --ghitfg:#fff; --grec:#7c3aed; --gsongrec:#d97706;
  --side:238px;
  /* --head is the design minimum for the header. --headh is what it actually
     measures, kept up to date by a ResizeObserver, because the header is
     allowed to WRAP — that is what stops it pushing its own controls off the
     right edge — and everything anchored below it has to follow it down rather
     than assume one row. */
  --head:46px; --headh:46px;
  /* Everything in this sheet is sized in rem, so the root font size is the one
     dial that scales the whole app — type, padding and tap targets together.
     --root is the per-breakpoint default; --tscale is the reader's override,
     set from Settings ▸ Text size and applied before first paint. */
  --root:14px; --tscale:1;
  /* The tab strip is a real row in the layout, so every full-height pane has to
     subtract it. On a phone it moves to the bottom of the screen and this is
     the height reserved for it there instead. */
  --navh:36px; --safeb:0px;
}
html[data-theme="dark"]{
  --bg:#14161d; --panel:#1b1e27; --panel2:#232733; --line:#2e3240; --line2:#262a36;
  --fg:#e7e9f0; --muted:#98a0b8; --accent:#6f95ff; --accent-fg:#0b0e16;
  --bad:#ff6b6b;
  --p1bg:#3a2530; --p1fg:#ffa8b4; --p2bg:#3d3320; --p2fg:#ffcf94;
  --p3bg:#26364f; --p3fg:#a8c8ff; --p4bg:#1f4438; --p4fg:#8ee2b6;
  --p5bg:#1c5140; --p5fg:#7dffc2; --pxbg:#2a2e3b; --pxfg:#868da5;
  --rndbg:#232e4a; --rndfg:#a8c8ff; --rndline:#41547e;
  --ghit:#22c55e; --ghitfg:#052e16; --grec:#c4b5fd; --gsongrec:#fbbf24;
}
*{box-sizing:border-box}
html{font-size:calc(var(--root) * var(--tscale))}
html,body{margin:0;height:100%;background:var(--bg);color:var(--fg);
  font-family:system-ui,-apple-system,"Segoe UI",sans-serif;line-height:1.4;
  -webkit-text-size-adjust:100%}
/* Nothing in this app is a horizontally scrolling document. Every screen was,
   on a phone — the Practice tab laid out 914px wide inside a 390px viewport and
   had to be dragged sideways to reach its own buttons. */
body{overflow-x:hidden}
/* This is a native-feeling app, not a document — double-tap-to-zoom has no
   job here and was firing on a mistapped small button (the reader FAB) as a
   stray second tap, zooming the whole page in. manipulation drops just that
   gesture while leaving normal panning alone; .readermaterials goes further
   (pan-y) to also take over pinch-zoom for its own font-size gesture. */
body{touch-action:manipulation}
button,input,select,textarea{font:inherit;color:inherit}
button{cursor:pointer}
a{color:var(--accent)}

/* ── header ─────────────────────────────────────────────────────────── */
/* flex-wrap is a floor, not a layout: it is what guarantees the header can
   never again push its own controls past the right edge of the screen. */
/* z-index 38, not some lower value: header is a positioned ancestor, so it
   establishes its own stacking context — a position:fixed descendant (the
   quick-actions .pop.readerquickpop, z-index 37) can set its z-index as high
   as it likes, but that only orders it against OTHER header descendants; the
   header's own box still paints as a whole at ITS z-index among root-level
   siblings like .tuner/.playalong (36). Below 37 here, the popover was
   silently trapped underneath any docked panel it visually overlaps, even
   though 37 > 36 reads like it should win — a real bug this exact comment
   used to not warn about. 38 clears both the popover (37) and the docked
   panels (36) it's meant to render over; keep it above whichever of those
   is highest if either ever changes. */
header{min-height:var(--head);display:flex;flex-wrap:wrap;align-items:center;
  gap:.6rem;padding:0 .8rem;background:var(--panel);
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:38}
header h1{font-size:.98rem;margin:0;font-weight:650;letter-spacing:-.01em;
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Same flex slot as h1 (immediately follows it, same flex:1/min-width:0) so
   it fills exactly the space the title otherwise would. */
.catsearchinput{flex:1;min-width:0}
/* header.searching (render()/setCatSearchOpen in app.js): the title, count
   and add-a-song controls make room for #catsearchinput instead of
   competing with it for the same header row. */
header.searching h1,header.searching #count,
header.searching #addbtn{display:none}
.spacer{margin-left:auto}
.count{color:var(--muted);font-size:.82rem;white-space:nowrap}
.icon{background:none;border:1px solid var(--line);border-radius:var(--r);
  padding:.25rem .45rem;color:var(--muted);flex:none}
.icon:hover{border-color:var(--accent);color:var(--fg)}
.icon.on{border-color:var(--accent);color:var(--fg)}
/* Recording continues after the camera menu auto-closes (see recStart in
   app.js) — this dot is the only thing on-screen saying so once it's shut. */
.readerquickmenu.recording>summary.icon{position:relative;border-color:var(--bad)}
.readerquickmenu.recording>summary.icon::after{content:"";position:absolute;
  top:-.15rem;right:-.15rem;width:.5rem;height:.5rem;border-radius:50%;
  background:var(--bad);animation:recpulse 1.4s ease-in-out infinite}
/* Live to the band (BAND_SHARE_PLAN.md §2b): the Band button itself reads
   "Live · N" in red for as long as tablets can follow — never off-screen. */
.readerquickmenu.bandlive>summary.icon{position:relative;border:2px solid var(--bad);color:var(--bad)}
.readerquickmenu.bandlive>summary.icon .readercommandlabel{color:var(--bad);font-weight:750}
.readerquickmenu.bandlive>summary.icon::after{content:"";position:absolute;
  top:-.15rem;right:-.15rem;width:.5rem;height:.5rem;border-radius:50%;
  background:var(--bad);animation:recpulse 1.4s ease-in-out infinite}
.readerlivebox{display:flex;flex-direction:column;gap:.45rem}
.readerlivebox .dim{margin:0;font-size:.82rem;line-height:1.35}
.readerlivehead{font-weight:700;color:var(--bad)}
.readerlivewho{font-size:.85rem}
/* Catalog's filter drawer, the song count and "+ Add" have no use while
   reading a chart — Back and Quick Actions take their place instead (see
   mountReaderHeader() in app.js). Both live permanently in the header so
   nothing has to be built or torn down there on every reader re-render. */
#readerback,#readerqa{display:none}
#brandicon{width:26px;height:26px;border-radius:7px;flex:none}
@media (max-width:860px){#brandicon{display:none}}
header.searching #brandicon{display:none}
html.reading #drawer,html.reading #count,html.reading #addbtn,html.reading #brandicon{display:none!important}
html.reading #readerback,html.insettings #readerback{display:inline-flex;align-items:center;justify-content:center}
html.insettings #drawer,html.insettings #count,html.insettings #addbtn,html.insettings #brandicon{display:none!important}
html.reading #readerqa{display:inline-flex;margin-left:auto;gap:.35rem}
html.reading #tab-set,html.reading #searchtoggle{display:none!important}
/* #main scrolls itself (overflow-y:auto, below) everywhere except a phone,
   where an inner scroller is known to fight iOS's address bar and momentum
   scrolling — see the ≤860px override further down. The reader's FAB is
   position:fixed and should never move relative to the page at all, but an
   inner-scrolling ancestor is exactly the setup that class of iOS bug lives
   in, and the reader never needs #main's own scrollbar (only the Playlists
   split view does) — so it gets the document-scroll treatment unconditionally,
   not just under 860px. .shell's height has to come along too: it is what
   actually stretches #main to fill the viewport via CSS Grid, so #main's
   overflow:visible alone still left it clipped to that same fixed height
   with content spilling invisibly past the bottom — .shell needs to grow to
   fit before the document has anything to scroll. scrollHost() (app.js)
   picks between #main and document.scrollingElement based on exactly this:
   whichever one's scrollHeight actually exceeds its clientHeight. */
html.reading #main{overflow:visible}
/* The document scrolls here, and a sticky header only sticks within its
   parent's box — body's height:100% (above) left that box one screen tall,
   so the header scrolled away after the first screenful, taking Exit with it. */
html.reading body{height:auto;min-height:100%}
/* Taking aside out of grid flow (below) leaves #main as the grid's only
   item, auto-placed into column 1 — the "0" column .shell.wide meant for
   the now-absent aside — instead of the "1fr" one, which squeezed every
   reader down to a sliver a few characters wide (one word per line) on
   desktop. grid-template-columns:1fr makes that single remaining item's
   column the full-width one regardless of which explicit track it would
   otherwise have inherited. */
html.reading .shell,html.reading .shell.wide{height:auto;grid-template-columns:1fr}
/* .shell is a grid with align-items:stretch, so once its own height is
   auto (above) the row height comes from the TALLEST item in it — and
   aside, though already zero-width via .shell.wide, was still a normal
   grid item with its full, un-collapsed filter-panel content height. That
   content (not #reader's) was what #main was actually growing to match,
   which is why even a one-line chart ended up many screens tall. Reading
   never needs aside at all (there is no drawer toggle in this space), so
   it comes out of grid flow entirely instead of just losing its width. */
html.reading aside{display:none}

/* ── tabs ────────────────────────────────────────────────────────────
   A row of its own under the header on a desktop; a fixed bar along the bottom
   of the screen on a phone. See index.html for why they left the header. */
nav#tabs{display:flex;align-items:stretch;gap:.15rem;height:var(--navh);
  padding:0 .7rem;background:var(--panel);border-bottom:1px solid var(--line);
  position:sticky;top:var(--headh);z-index:29}
.tab{display:inline-flex;align-items:center;gap:.35rem;
  background:none;border:0;border-bottom:2px solid transparent;color:var(--muted);
  padding:0 .6rem;font-weight:500;white-space:nowrap}
.tab.on{color:var(--fg);border-color:var(--accent)}
.tab:hover{color:var(--fg)}
.navicon{width:15px;height:15px;flex:none}

/* ── the iOS app (IOS_PLAN N8) ─────────────────────────────────────────
   html.native is set only by the bundled app (native-config.js). Capacitor
   draws the page edge to edge (contentInset: never), so the app keeps its own
   header out from under the status bar, its bottom edge above the home
   indicator at every width (phones already did; iPad's wide layout didn't),
   and its content clear of a landscape notch. The web is untouched. */
html.native{--safeb:env(safe-area-inset-bottom,0px)}
/* .reading too: the reader's own header padding shorthand (same
   specificity, later in the file) otherwise resets this and hides Back
   under the status bar. */
html.native header,html.native.reading header{padding-top:max(env(safe-area-inset-top),.2rem)}
html.native body{padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right)}
@media (min-width:861px){
  html.native .shell{height:calc(100vh - var(--headh) - var(--navh) - var(--safeb))}
}

/* ── shell: sidebar + list ──────────────────────────────────────────── */
.shell{display:grid;grid-template-columns:var(--side) 1fr;
  height:calc(100vh - var(--headh) - var(--navh))}
.shell.wide{grid-template-columns:0 1fr}
aside{border-right:1px solid var(--line);background:var(--panel);
  overflow-y:auto;padding:.6rem .55rem 3rem;min-width:0}
/* The drawer's own heading/"Show N songs" only makes sense once the panel
   is a dismissable overlay rather than always-visible desktop chrome. */
.filterheading,#filterbackdrop{display:none}
.mobilelisttools{display:none}
.mobilelinklabel{display:none}
/* Collapse by zeroing the column, never display:none — removing the aside from
   the grid flow makes #main the first child and squeezes it into the 0 column,
   which reads as "the whole app vanished". */
.shell.wide aside{overflow:hidden;padding:0;border-right:0;visibility:hidden}
#main{overflow-y:auto;overflow-x:hidden;padding:0 0 4rem;min-width:0}

/* ── sidebar controls ───────────────────────────────────────────────── */
.sec{border-bottom:1px solid var(--line2);padding:.45rem 0}
.sec>summary{list-style:none;cursor:pointer;font-size:.7rem;font-weight:700;
  letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  display:flex;align-items:center;gap:.3rem;padding:.15rem .1rem}
.sec>summary::-webkit-details-marker{display:none}
.sec>summary::before{content:"▸";font-size:.65rem;transition:transform .12s}
.sec[open]>summary::before{transform:rotate(90deg)}
.sec>summary .n{margin-left:auto;font-weight:500;letter-spacing:0}
.opts{display:flex;flex-wrap:wrap;gap:.22rem;padding:.35rem .1rem .15rem}
.chip{padding:.16rem .45rem;border-radius:999px;border:1px solid var(--line);
  background:var(--panel);color:var(--muted);font-size:.76rem;white-space:nowrap;
  user-select:none}
.chip:hover{border-color:var(--accent)}
.chip.on{background:var(--accent);border-color:var(--accent);color:var(--accent-fg);
  font-weight:600}
.chip.off{text-decoration:line-through;border-color:var(--bad);color:var(--bad)}
/* Still a real, clickable button — tapping it goes to the editor to claim a
   part — so this is a dim look, not :disabled's inert one (same reasoning as
   .readchip.empty). */
.chip.ghost{opacity:.55}
.chip .c{opacity:.6;margin-left:.2rem;font-variant-numeric:tabular-nums}
.search{width:100%;padding:.38rem .55rem;border-radius:var(--r);
  border:1px solid var(--line);background:var(--panel)}
.clear{background:none;border:0;color:var(--accent);font-size:.78rem;padding:.2rem}
.active{display:flex;flex-wrap:wrap;gap:.22rem;padding:.3rem .1rem}

/* ── table ──────────────────────────────────────────────────────────── */
table{width:100%;border-collapse:collapse;table-layout:fixed}
/* Redundant with the sortable column headers on a wide screen (thead is
   hidden below 860px, so it's the ONLY way to sort there) — kept visible at
   every width anyway rather than added and removed at the breakpoint, so
   the control a keyboard/screen-reader user already found doesn't vanish
   out from under them on a resize. */
.catalogsort{display:flex;gap:.4rem;padding:.5rem .7rem;align-items:center}
.catalogsort select{max-width:12rem}
thead th{position:sticky;top:0;background:var(--bg);text-align:left;
  font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);
  padding:.4rem .5rem;border-bottom:1px solid var(--line);white-space:nowrap;z-index:10}
thead th.s{cursor:pointer}
thead th.s:hover{color:var(--fg)}
tbody tr{border-bottom:1px solid var(--line2);cursor:pointer}
tbody tr:hover{background:var(--panel2)}
tbody tr.open{background:var(--panel2)}
td{padding:.24rem .5rem;vertical-align:middle;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
td.t{font-weight:500}
.lyrichit{display:block;color:var(--muted);font-weight:400;font-size:.85em;font-style:italic;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:32rem}
td.a{color:var(--muted)}
.dim{color:var(--muted)}
.selbox{accent-color:var(--accent);vertical-align:-1px;margin-right:.35rem}
/* Last played: the staleness, then the tick that resets it. Dimmed rather than
   hidden until hover — 221 rows should not read as 221 competing buttons, but a
   tick that only appears on hover is invisible-and-still-tappable on an iPad. */
td.lastp{text-align:right}
.when{font-variant-numeric:tabular-nums}
.playbox{display:inline-flex;align-items:center;gap:.35rem;white-space:nowrap}
td.lastp .tick{opacity:.4;transition:opacity .1s}
tbody tr:hover td.lastp .tick,tbody tr.open td.lastp .tick,
td.lastp .tick:focus-visible{opacity:1}

/* proficiency badges: two chars on desktop, expanded in the editor */
.profs{display:inline-flex;gap:.18rem}
.pb{font-size:.7rem;padding:.04rem .3rem;border-radius:4px;white-space:nowrap;
  font-variant-numeric:tabular-nums;background:var(--pxbg);color:var(--pxfg);border:0}
.pb.p1{background:var(--p1bg);color:var(--p1fg)}
.pb.p2{background:var(--p2bg);color:var(--p2fg)}
.pb.p3{background:var(--p3bg);color:var(--p3fg)}
.pb.p4{background:var(--p4bg);color:var(--p4fg)}
.pb.p5{background:var(--p5bg);color:var(--p5fg);font-weight:650}
.pb.px{border:1px dashed var(--line);background:transparent}
/* Random mode's bucket. Deliberately outside the p1–p5 rating ramp: it is not a
   proficiency, and borrowing p1's red would read as "you are bad at these". */

/* ── the front-of-row indicators ─────────────────────────────────────
   Recorded. Ghosted when off so the row reads as
   "an empty box" rather than as clutter, solid when on. Never display:none
   — a missing indicator has to be visibly missing, or the whole point (glance
   at a list, see what is left to record) is lost.

   These are STATE, not actions, and they are deliberately smaller and quieter
   than the Played / Skip buttons on the Practice screen. They used to be the
   same size and the same glyphs: "log that I played it" was a ○, which is
   exactly what `recorded` draws for "no video of this yet". */
/* Off is an outline, on is a fill. Never opacity alone: a 40%-opacity control
   reads as DISABLED on iOS, not as "off", which is why every unset indicator on
   the phone looked greyed out rather than open. */
/* Three toggles on 221 rows is a lot of chrome however it is drawn, so the OFF
   state is deliberately not a button: no border, no fill, just a small quiet
   word that still says which indicator is missing. Switching one on lights it
   up, which is what makes "scroll the list, see what is left to record" work.
   The hit area is the same in both states. */
.badges{display:inline-flex;gap:.3rem;margin-right:.45rem;vertical-align:1px}
.bdg{border:1px solid transparent;background:none;border-radius:4px;
  padding:.02rem .28rem;font-size:.63rem;line-height:1.6;cursor:pointer;
  color:var(--muted);letter-spacing:.05em;text-transform:uppercase;
  font-weight:600;white-space:nowrap;opacity:.6}
.bdg:hover{opacity:1;border-color:var(--line);color:var(--fg)}
.bdg.on{opacity:1;background:var(--p4bg);color:var(--p4fg);border-color:var(--p4bg)}
/* Red for "filmed" — the one state worth spotting from across a list of 223
   rows, and a red word carries further than a red dot did. */
.bdg.b-recorded.on{background:#b3261e;border-color:#b3261e;color:#fff}
/* "orig" instead of a ★ next to the title: ★ means a pinned list, everywhere. */
.tagpill.orig{margin-left:.35rem;text-transform:uppercase;font-size:.62rem;
  letter-spacing:.05em}

/* A playlist is a table now, same as the Catalog, so sorting is one habit
   rather than two. `.tnum` carries the position on a phone, where the # column
   is gone; exactly one of the two ever renders. */
.pltab td.pos{color:var(--muted);text-align:right;
  font-variant-numeric:tabular-nums;padding-right:.15rem}
.pltab td.plact{white-space:nowrap;text-align:right}
.pltab td.plact .chip{padding:.05rem .3rem}
/* Title is "auto" width, every other column a fixed rem/%, so on an ordinary
   laptop-width window Title was the one thing forced to give — down to
   nothing, since the badges alone could already fill its share. A safety net
   regardless of width: truncate with an ellipsis instead of just overflowing
   invisibly under the next column's background. */
.pltab td.t .linkish{display:inline-block;max-width:100%;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
/* And don't wait for the phone breakpoint to free up the room Title actually
   needs: Proficiency/Links/Key/Last played give way well before Title would
   otherwise get crushed by them. Title and Artist are the two that stay. */
@media (max-width:1200px){ .pltab .hidemid{display:none} }
.tnum{display:none;color:var(--muted);font-variant-numeric:tabular-nums}
.srow.dragging{opacity:.4}
.srow .grip{cursor:grab;padding:0 .2rem;color:var(--muted);touch-action:none}
/* Edit-mode move controls sit at the row's left edge, beside the title. */
.movectl .chip{padding:.05rem .4rem;min-width:1.9rem;justify-content:center}
div.movectl{flex:none;display:flex;gap:.2rem;align-items:center;margin-right:.15rem}
td.movectl{white-space:nowrap}
td.movectl .chip+.chip{margin-left:.2rem}
/* Whole-row drag: a long-press mustn't start text selection or iOS's callout. */
.drag{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}

.linkbar{display:inline-flex;gap:.12rem}
.lnk{padding:.05rem .28rem;border-radius:4px;text-decoration:none;font-size:.82rem;
  border:1px solid transparent;line-height:1.3}
.lnk:hover{border-color:var(--accent);background:var(--panel)}
.lnk.ghost{opacity:.32}
.tagpill{display:inline-flex;align-items:center;gap:.2rem;font-size:.7rem;
  background:var(--panel2);color:var(--muted);border-radius:4px;
  padding:.04rem .3rem;margin-right:.15rem;border:1px solid var(--line2)}
.tagpill .x{background:none;border:0;color:var(--muted);padding:0 .05rem;font-size:.85rem}
.tagpill .x:hover{color:var(--bad)}
button.tagpill.capobtn,button.tagpill.loadoutbtn,button.tagpill.keybtn,button.tagpill.readerflow{cursor:pointer;font-family:inherit;color:var(--fg)}
button.tagpill.capobtn.ghost,button.tagpill.loadoutbtn.ghost,button.tagpill.keybtn.ghost,button.tagpill.readerflow.ghost{color:var(--muted);border-style:dashed}
.seg.capochoices{flex-wrap:wrap;gap:.3rem}
.seg.capochoices button{min-width:2.6rem;min-height:2.4rem}
.capopick select{font:inherit;padding:.3rem .4rem;border-radius:var(--r);border:1px solid var(--line);background:var(--panel2);color:var(--fg)}

/* ── editor ─────────────────────────────────────────────────────────── */
/* Lives inside a .sheet now (opened via openSongEditor), not a table row —
   .sheet already supplies the outer padding/border/background, so .editor
   itself is just a plain vertical stack. */
.editor{display:grid;gap:.6rem;white-space:normal}
.erow{display:flex;gap:.4rem;align-items:flex-start;flex-wrap:wrap}
.erow>.lbl{color:var(--muted);font-size:.68rem;min-width:5.2rem;padding-top:.3rem;
  text-transform:uppercase;letter-spacing:.06em}
/* The always-visible top block: title/artist, the three Catalog-filter
   flags, the starred part's rating, Played, the accepted-focus banner. */
.editorhead{display:grid;gap:.55rem;padding-bottom:.65rem;margin-bottom:.1rem;
  border-bottom:1px solid var(--line2)}
/* Song editor cards (editorCard): always open, two columns once there's room.
   The head carries the card's one "+ Add" action. */
.editorgrid{display:grid;gap:.6rem;align-items:start}
.editorcol{display:grid;gap:.6rem;min-width:0}
@media(min-width:760px){.editorgrid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}}
.ecard{border:1px solid var(--line);border-radius:10px;background:var(--panel);min-width:0;
  padding:.6rem .75rem .75rem;display:flex;flex-direction:column;gap:.55rem}
.ecardhead{display:flex;align-items:center;gap:.5rem;min-height:2rem}
.ecardhead h4{margin:0;font-size:.95rem;font-weight:650}
.ecardhead>:not(h4){margin-left:auto}
.ecardadd[aria-expanded="true"]{background:var(--panel2)}
.editornames{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:.5rem}
.etags{gap:.4rem}
.musicalfields{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.5rem}
.musicalfields .kin{width:100%;min-width:0}
.durationrow{align-items:center;flex-wrap:nowrap}
.durationrow input[type=range]{flex:1;min-width:3rem}
.durationrow .durationin{width:4.6rem;flex:none}
.durationrow .chip{flex:none}
.presetrow{align-items:center;flex-wrap:nowrap}
.presetrow .tin{flex:1;min-width:0;width:auto}
.elinks{display:flex;flex-wrap:wrap;gap:.4rem}
.elink{display:inline-flex;align-items:center;border:1px solid var(--line);border-radius:var(--r);
  background:var(--panel);min-height:2.5rem;color:var(--accent);text-decoration:none;font-size:.9rem}
.elink>a,a.elink{display:flex;align-items:center;align-self:stretch;padding:0 .65rem;color:inherit;text-decoration:none}
.elink>.x{align-self:stretch;border:0;border-left:1px solid var(--line2);background:none;
  color:var(--muted);padding:0 .65rem;font-size:1.05rem}
.elink>.x:hover{color:var(--bad)}
a.elink.ghost{color:var(--muted);border-style:dashed}
.elinkadd .search{flex:none;width:auto}
.elinkadd .tin{flex:1;min-width:9rem;width:auto}
.ehistory>summary{cursor:pointer;color:var(--muted);font-size:.85rem;padding:.25rem 0}
.ehistory[open]>summary{margin-bottom:.3rem}
.addpart{max-width:12rem}
.enjoyrow{border-top:1px solid var(--line2);padding-top:.5rem}
.plpicker[hidden],.elinkadd[hidden]{display:none}
.enjoylabel{min-width:6rem;font-size:.85rem}
/* One column: the columns dissolve so the chart can sit right under proficiency. */
@media(max-width:759px){
  .editorcol{display:contents}
  .ecard[data-key="prof"]{order:1}.ecard[data-key="ref"]{order:2}.ecard[data-key="links"]{order:3}
  .ecard[data-key="key"]{order:4}.ecard[data-key="pl"]{order:5}.ecard[data-key="notes"]{order:6}.ecard[data-key="rec"]{order:7}
  .ecard[data-key="vocalref"]{order:7}.ecard[data-key="sections"]{order:8}
}
/* Touch screens (iPad included — it gets the wide layout) need real targets. */
@media(pointer:coarse){
  .songeditorsheet .chip,.songeditorsheet .seg button,.songeditorsheet select{min-height:40px}
  .songeditorsheet .chip{padding:.3rem .75rem;font-size:.92rem}
  .songeditorsheet .tin,.songeditorsheet .kin{min-height:44px}
}
.dangerzone{display:flex;align-items:center;justify-content:space-between;gap:.6rem;
  flex-wrap:wrap;padding:.55rem .75rem;border:1px solid var(--bad);border-radius:var(--r)}
.seg{display:flex;gap:.15rem}
.seg button{padding:.2rem .5rem;border-radius:var(--r);border:1px solid var(--line);
  background:var(--panel);min-width:2rem;font-variant-numeric:tabular-nums}
.seg button.on{background:var(--accent);border-color:var(--accent);
  color:var(--accent-fg);font-weight:650}
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr));gap:.22rem}
.prow1{display:flex;gap:.35rem;align-items:center}
.prow1.unset{opacity:.42}
.pname{min-width:6rem;text-align:left;background:none;border:1px dashed var(--line);
  border-radius:var(--r);padding:.16rem .45rem;color:var(--muted);font-size:.8rem}
.prow1:not(.unset) .pname{border-style:solid;color:var(--fg)}
.pname:hover{border-color:var(--accent)}
.tin,.kin{padding:.24rem .45rem;border-radius:var(--r);border:1px solid var(--line);
  background:var(--panel);max-width:100%}
.kin{width:4.2rem}
.notes{width:100%;min-height:2.6rem;padding:.4rem;border-radius:var(--r);
  border:1px solid var(--line);background:var(--panel);resize:vertical}
.danger{background:none;border:1px solid var(--bad);color:var(--bad);
  border-radius:var(--r);padding:.22rem .6rem;justify-self:start;font-size:.8rem}

/* ── bulk bar ───────────────────────────────────────────────────────── */
/* Floating toolbar: one row of compact controls, Delete set apart at the end. */
.bulk{position:fixed;z-index:20;display:none;left:50%;transform:translateX(-50%);
  bottom:calc(var(--safeb) + 1rem);width:max-content;max-width:calc(100vw - 2rem);
  gap:.5rem;align-items:center;flex-wrap:wrap;padding:.45rem .5rem .45rem .45rem;
  background:var(--panel);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 8px 28px rgba(0,0,0,.16),0 1px 3px rgba(0,0,0,.08)}
.bulk.show{display:flex}
html.hassel #catalog{padding-bottom:5.5rem}
.bulkhead{display:flex;align-items:center;gap:.35rem;padding-right:.55rem;border-right:1px solid var(--line);white-space:nowrap}
.bulkhead b{font-variant-numeric:tabular-nums}
.bulkx{width:1.9rem;height:1.9rem;border-radius:50%;border:0;background:var(--panel2);color:var(--muted);
  display:inline-flex;align-items:center;justify-content:center;font-size:.85rem;cursor:pointer}
.bulkx:hover{color:var(--fg);background:var(--line)}
.bulkacts{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap}
.bulksel,.bulkbtn{font:inherit;font-size:.82rem;color:var(--fg);background:var(--panel);
  border:1px solid var(--line);border-radius:999px;padding:.32rem .7rem;cursor:pointer;white-space:nowrap}
.bulksel{appearance:none;-webkit-appearance:none;padding-right:1.6rem;field-sizing:content;max-width:10rem;
  background:var(--panel) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a90a2' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat right .6rem center}
.bulksel:hover,.bulkbtn:hover{background-color:var(--panel2)}
.bulk .seg button{font-size:.82rem;padding:.25rem .55rem}
.bulkdel{font:inherit;font-size:.82rem;font-weight:600;color:var(--bad);background:none;
  border:1px solid color-mix(in srgb,var(--bad) 45%,transparent);border-radius:999px;padding:.32rem .8rem;cursor:pointer;margin-left:.15rem}
.bulkdel:hover{background:var(--bad);color:#fff;border-color:var(--bad)}
tbody tr.sel,tbody tr.sel:hover{background:color-mix(in srgb,var(--accent) 10%,var(--panel))}
tbody tr.sel td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
#head .selbox{margin-right:.45rem}
.bulkhead .clear{display:none}
.selbox{cursor:pointer}
/* table-layout:fixed hands the Title column (checkbox included) whatever the
   fixed columns leave over, which between phone and wide desktop was nothing.
   Sized off #catalog, not the viewport, since the filter sidebar collapses. */
#catalog{container:cat/inline-size}
@media (min-width:861px){
  @container cat (max-width:1060px){ #catalog .plcol{display:none} }
  @container cat (max-width:880px){ #catalog .profcol{display:none} }
}

/* ── playlists page ─────────────────────────────────────────────────── */
#pr,#set,#org,#goals,#stats,#reader{padding:.7rem .8rem;max-width:60rem}
.plh{margin:.9rem 0 .3rem;font-size:.7rem;text-transform:uppercase;
  letter-spacing:.06em;color:var(--muted)}
.plitem{display:flex;gap:.45rem;align-items:center;padding:.24rem .1rem;
  border-top:1px solid var(--line2);min-width:0}
.plitem .t{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plitem .hwhen{flex:none;font-size:.76rem;font-variant-numeric:tabular-nums}

/* ── setlist builder ────────────────────────────────────────────────── */
.setlist{margin-top:.3rem}
.setrow{align-items:flex-start!important;padding:.5rem .1rem}
.setpos{flex:none;width:1.7rem;text-align:right;color:var(--muted);
  font-variant-numeric:tabular-nums;padding-top:.15rem;font-size:.85rem}
.setbody{flex:1;min-width:0;display:flex;flex-direction:column;gap:.15rem}
.setline1{display:flex;gap:.5rem;align-items:baseline;flex-wrap:wrap}
.setmeta{font-size:.88rem;color:var(--muted)}
.setdur,.setline1 .durlookup{margin-left:auto;flex:none}
.setdur{font-size:.82rem;font-variant-numeric:tabular-nums}
.setnote{font-size:.83rem;color:var(--muted);font-style:italic;line-height:1.35}
.setacts{flex:none;display:flex;gap:.25rem;align-items:center;padding-top:.1rem}
/* A row missing a fact that matters for rehearsing it (setlistNeedsWork) —
   left-accent rather than a filled background, so a whole "needs-work
   first" list doesn't read as a wall of warning color. */
.setrow.setneedswork{border-left:3px solid var(--p2fg);padding-left:.35rem}
.setworkflag{font-size:.76rem;color:var(--p2fg);white-space:nowrap}

/* Band sync, Phase 3: guests' own notes/proficiency on this song, rolled up
   from the setlist's active share (bandNotesForSong in app.js). */
.setbandnotes{display:flex;flex-direction:column;gap:.15rem;margin-top:.15rem}
.setbandnote{font-size:.82rem;color:var(--muted);display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.setbandnote .pb{font-size:.72rem;padding:.05rem .4rem}
/* Band votes and flags (BAND_SHARE_PLAN.md §4) — same avatar as share.css. */
.bandavatar{display:inline-flex;align-items:center;justify-content:center;min-width:1.5rem;height:1.5rem;
  padding:0 .25rem;box-sizing:border-box;border-radius:999px;color:#fff;font-size:.66rem;font-weight:750;line-height:1;white-space:nowrap}
.setbandvotes{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:.2rem}
.setbandvote{display:inline-flex;align-items:center;gap:.2rem;font-size:.82rem}
.bandfbsheet{display:flex;flex-direction:column;gap:.2rem}
.bandfbrow{display:flex;flex-direction:column;gap:.35rem;padding:.7rem 0;border-bottom:1px solid var(--line2)}
.bandfbhead{display:flex;align-items:center;gap:.5rem}
.bandfbtitle{flex:1;font-weight:650}
.bandfbwork{font-size:.8rem;font-weight:700;color:var(--bad)}
.bandfbnote{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;font-size:.85rem}
.bandfbacts{display:flex;gap:.4rem;flex-wrap:wrap}
.bandfbacts .chip{min-height:40px}
.bandfbacts .chip.on{border-color:var(--accent);background:var(--rndbg);color:var(--rndfg)}
.bandfblevel{min-height:40px;border-radius:999px;border:1px dashed var(--line);background:transparent;
  color:var(--muted);font:inherit;font-size:.85rem;font-weight:600;padding:0 .7rem}
.bandfblevel.p1{background:var(--p1bg);color:var(--p1fg);border-style:solid}
.bandfblevel.p2{background:var(--p2bg);color:var(--p2fg);border-style:solid}
.bandfblevel.p3{background:var(--p3bg);color:var(--p3fg);border-style:solid}
.bandfblevel.p4{background:var(--p4bg);color:var(--p4fg);border-style:solid}
.bandfblevel.p5{background:var(--p5bg);color:var(--p5fg);border-style:solid}
.bandfbfirst{align-self:flex-start;margin-bottom:.4rem}
.clearflagslist{margin:.2rem 0 .8rem 1.1rem;padding:0;font-size:.9rem;line-height:1.6}
.listenrow{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin:.35rem 0}
.listenchip{display:inline-flex;align-items:center;border:1px solid var(--line);border-radius:999px;background:var(--panel)}
.listenchip a{padding:.3rem .2rem .3rem .7rem;font-size:.82rem;font-weight:600;color:var(--fg);text-decoration:none}
.listenrm{border:0;background:none;color:var(--muted);min-width:32px;min-height:32px;cursor:pointer}

/* ── set readiness panel (renderPlDetail / plReadinessPanel) ──────────── */
.plready{margin:.7rem 0;padding:.55rem .9rem;border:1px solid var(--line);border-radius:var(--r)}
.plready>summary{cursor:pointer;list-style:none;min-height:32px;margin:0}
.plready>summary::-webkit-details-marker{display:none}
.plready>summary::after{content:"⌄";color:var(--muted);margin-left:.2rem}
.plready[open]>summary{margin-bottom:.6rem}
.plready[open]>summary::after{transform:rotate(180deg)}
.plreadysum{margin-right:auto;font-size:.78rem;color:var(--muted);font-variant-numeric:tabular-nums}
.plreadyhead{display:flex;align-items:center;justify-content:space-between;gap:.6rem;margin-bottom:.6rem}
.plreadytitle{font-size:.85rem;font-weight:700}
.plreadytoggle{display:flex;align-items:center;gap:.35rem;font-size:.78rem;color:var(--muted);cursor:pointer}
.plreadytoggle input{margin:0}
.plreadysub{font-size:.76rem;color:var(--muted);margin-bottom:.3rem}
.plreadybar{display:flex;gap:2px;height:1.15rem;border-radius:6px;overflow:hidden;background:var(--pxbg)}
.plreadyseg.pb{height:100%;padding:0;border-radius:0;display:flex;align-items:center;justify-content:center;
  font-size:.7rem;font-weight:700;min-width:1.1rem;overflow:hidden}
.plreadypart{font-size:.76rem;padding:.1rem .3rem;margin-left:.15rem}
.plreadylegend{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:.4rem;font-size:.72rem;color:var(--muted)}
.plreadylegenditem{display:inline-flex;align-items:center;gap:.25rem}
.plreadymeters{display:flex;flex-wrap:wrap;gap:1.1rem;margin-top:.9rem}
.plreadymeter{flex:1;min-width:9.5rem}
.plreadymeterhead{display:flex;justify-content:space-between;font-size:.78rem;margin-bottom:.25rem}
.plreadytrack{height:.45rem;border-radius:999px;background:var(--line);overflow:hidden}
.plreadyfill{height:100%;background:var(--accent)}

/* ── song materials ─────────────────────────────────────────────────── */
.matbox,.notesbox{flex:1 1 100%;display:flex;flex-direction:column;gap:.5rem;min-width:14rem}
/* materialCard's own head ("Lyrics / chart" + the raw kind) rode at the
   browser's unstyled <b> size, which reads oversized and heavier than
   everything around it in the editor's tighter Reference section. */
.pcard>.plbar>b{font-size:.9rem;font-weight:650}
/* A tiny shade so the chart content reads as its own block rather than
   blending into the surrounding panel it sits on. */
.materialtext{white-space:pre-wrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:.88rem;line-height:1.5;margin-top:.4rem;max-height:24rem;overflow-y:auto;
  background:var(--panel2);border-radius:var(--r);padding:.5rem .6rem}
.chordpro{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88rem;
  line-height:1.5;margin-top:.4rem;max-height:24rem;overflow-y:auto;
  background:var(--panel2);border-radius:var(--r);padding:.5rem .6rem}
.cpdir{color:var(--muted);font-style:italic;margin:.35rem 0}
.cpsection{color:var(--fg);font-weight:700;font-size:1.08em;letter-spacing:-.01em;
  margin:1.1rem 0 .3rem;padding-top:.5rem;border-top:1px solid var(--line2)}
.cpsection:first-child{margin-top:0;padding-top:0;border-top:0}
.cpline{position:relative;padding-top:1.2em;white-space:pre-wrap}
.cpline.cplyric{padding-top:0}
.cpchord{position:relative}
.cpchordgap{padding-right:var(--cpgap)}
.cpchord::before{content:attr(data-chord);position:absolute;top:-1.2em;left:var(--cpshift,0);
  color:var(--accent);font-weight:700;font-size:.82em;white-space:nowrap;
  transition:background-color .15s,color .15s,padding .15s,border-radius .15s}
/* An instrumental line ("[Bm][Bm][Bm][Bm]", no lyric text) renders its chords
   as real inline text instead (see renderChordPro in app.js) — this line has
   nothing for the floating ::before label to sit above, and with no lyric
   text between tokens to space them apart, every ::before landed at the same
   x and stacked illegibly. Kill the float here and give the real text its
   own spacing/weight instead; no reserved top padding either, since nothing
   floats above this line. */
/* ChordPro grid ({start_of_grid}): fixed-width cells so beats line up bar
   over bar across rows of the same shape; wraps rather than scrolls on a phone. */
.cpgrid{display:flex;align-items:baseline;margin:.15em 0;font-variant-numeric:tabular-nums;max-width:44rem}
.cpgmeasure{flex:1 1 0;display:flex;min-width:0}
.cpgcell{flex:1 1 0;padding:0 .15em;color:var(--accent);font-weight:700;white-space:nowrap}
.cpgbeat,.cpgrep,.cpgsep{color:var(--muted);font-weight:400}
.cpgsep{margin:0 .15em}
.cpgbar{flex:none;width:1.6em;text-align:center;color:var(--muted);font-weight:400;white-space:nowrap}
.cpgmargin{flex:none;width:3.5em;color:var(--muted);font-style:italic;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cpgnote{flex:none;width:2.5em;color:var(--muted);font-style:italic;padding-left:.3em;white-space:nowrap}
.cpgridedge{height:0}
.cpline.cpchordonly{padding-top:0}
.cpchordonly .cpchord::before{content:none}
.cpchordonly .cpchord{color:var(--accent);font-weight:700;margin-right:1.1em}
/* .tagged: has a real-video timestamp (Tag tool) — a small dot so tagging
   progress through a chart is visible at a glance. .nowplaying: which one
   auto-scroll's interpolation (currentChordIndex in app.js) says should be
   played right now — filled like an active chip so it reads at a glance,
   not just a color change subtle enough to miss mid-song. */
.cpchord.tagged::after{content:"";position:absolute;top:-.3em;left:-.15em;
  width:.3em;height:.3em;border-radius:999px;background:var(--accent);opacity:.5}
.cpchord.nowplaying::before{background:var(--accent);color:var(--accent-fg);
  border-radius:.3em;padding:0 .35em;margin-left:-.35em}
.cpblank{height:.7em}
.cpchorus{border-left:3px solid var(--accent);padding-left:.6rem}
.cprecall>.cpsection{margin-top:1.1rem}
.cpflow{color:var(--muted);font-family:system-ui,sans-serif;font-size:.85em;font-weight:600;margin:0 0 .4rem}
.cpflowmiss{color:var(--bad,#c33)}
.cptab{white-space:pre;overflow-x:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;line-height:1.25}
/* A pause mark ({pause: N}) — its own line in the chart flow, same column as
   the lyrics/chords around it, not a margin badge, so it reads the way a
   section title or a chord-only line already does. .active is only ever
   applied by autoscrollStart's tick loop while paced auto-scroll is
   actually sitting on this mark. */
.timerline{display:flex;align-items:center;gap:.5rem;margin:.35rem 0}
.timerchip{display:inline-flex;align-items:center;gap:.35rem;background:var(--panel);
  border:1.5px solid var(--accent);color:var(--accent);border-radius:999px;
  padding:.2rem .65rem;font-size:.78rem;font-weight:700;font-family:system-ui,sans-serif}
.timerchip.active{background:color-mix(in srgb, var(--accent) 14%, var(--panel));cursor:pointer}
.timerchip .track{width:3.6rem;height:.35rem;border-radius:999px;flex:none;
  background:color-mix(in srgb, var(--accent) 18%, transparent);overflow:hidden}
.timerchip .track .fill{height:100%;background:var(--accent);border-radius:999px}
.timerrule{flex:1;border-top:1.5px dashed var(--line)}
.skimline{display:flex;align-items:center;gap:.5rem;margin:.35rem 0}
.skimchip{display:inline-flex;align-items:center;background:var(--panel);border:1px solid var(--line);
  border-radius:999px;padding:.1rem .5rem;font-size:.75rem;color:var(--muted);white-space:nowrap}
/* A skimmed section stays on screen (no jump mid-song) but reads as "not
   this part": dimmed, so the eye slides past it while auto-scroll hurries. */
.chordpro .cpskimmed{opacity:.38}
.chordpro .cpsection.cpskiphead::after{content:" · skip";font-weight:400;color:var(--muted)}
.readerhint{background:color-mix(in srgb, var(--accent) 12%, var(--panel));color:var(--accent);
  font-size:.8rem;font-weight:600;padding:.4rem .7rem;border-radius:var(--r);
  margin-bottom:.5rem;text-align:center}

/* ── reader & rehearsal ─────────────────────────────────────────────── */
:root{--readerfont:1}
/* Wider than a typical .pop now that recording and notes live here too —
   min(…, 92vw) keeps it clear of the edge on a narrow phone instead of
   forcing every recorder control and the notes textarea to wrap at 18rem. */
/* position:fixed, anchored to the VIEWPORT's own right edge — not .pop's
   default position:absolute right:0, which anchors to .readerquickmenu
   itself. That trigger sits left of the gear icon, not
   flush against the screen edge, so a wide popover anchored to it (right:0
   of a box that's already ~80px in from the edge) ran off the left side of
   the viewport entirely on a real phone — the settings/recording/notes
   controls it holds are what item 7 called unusable there. Anchoring to the
   viewport directly is correct regardless of how much header chrome sits to
   the trigger's right. */
/* .pop.readerquickpop, not .readerquickpop alone: the base .pop rule (later
   in this file, same single-class specificity) sets position/right too, and
   a same-specificity rule wins by SOURCE ORDER, not by which one sounds more
   specific — .pop was winning here regardless of what this rule said. The
   compound selector outranks it for real. */
/* z-index 37, above .readerfab's 35 (and .performctl's 36) — this popover
   runs from just under the header down to nearly the bottom of the screen,
   genuinely overlapping the FAB cluster's bottom-right corner. The base
   .pop's z-index:25 left the FAB floating on top of an open recording/notes
   panel with no reason to — an open panel is the thing you're looking at. */
.pop.readerquickpop{position:fixed;right:.6rem;top:calc(var(--headh) + .4rem);
  width:min(24rem, calc(100vw - 1.2rem));max-height:calc(100vh - var(--headh) - .8rem);
  display:flex;flex-direction:column;overflow:hidden;z-index:37}
.readerpophead{display:none}
.readerquickpop .readerrecordings,.readerquickpop .readernotes{margin-left:.1rem;margin-right:.1rem}
.readerquickpop .erow{padding:.4rem .5rem;align-items:center}
.readerquickpop button{min-height:2.2rem}
/* .pop button normally goes full-width/block (menu-item style) — every chip
   in this popover (transpose, scroll-speed, the duration slider's Done) is
   meant to stay small and inline instead, unlike the plain-button menu rows. */
.readerquickpop .chip{display:inline-flex;align-items:center;width:auto}
.readerquickmenu>summary.icon{display:inline-flex;align-items:center;justify-content:center;
  gap:.35rem;min-height:2.5rem;padding:.35rem .65rem}
.readercommandlabel{font-size:.78rem;font-weight:650;color:var(--fg)}
.readercommandhead{display:flex;align-items:center;justify-content:space-between;gap:.75rem;
  position:sticky;top:0;z-index:2;background:var(--panel);padding:.65rem .55rem;
  border-bottom:1px solid var(--line)}
.readercommandhead strong{font-size:1rem}
.readercommandhead .chip{flex:none;min-height:2.5rem;padding:.4rem .85rem;border:1px solid var(--line);
  background:var(--panel2);border-radius:9px;justify-content:center}
.readercommandsection{padding:.7rem .55rem;border-bottom:1px solid var(--line2)}
.readercommandsection:last-child{border-bottom:0}
.readercommandsection h3{margin:0 0 .5rem;font-size:.75rem;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted)}
.readercommandsection>p{margin:.15rem 0 .7rem;line-height:1.45}
.readercommandprimary{width:100%;min-height:3rem;border:1px solid var(--accent);
  border-radius:var(--r);background:var(--accent);color:var(--accent-fg);
  font-weight:700;font-size:.95rem}
.readercommandprimary.on{background:var(--bad);border-color:var(--bad);color:#fff}
.readercommandsection .readerlinks{padding:.2rem 0}
.readercommandsection .readerscrollsettings{margin-bottom:0}
.cuepresets{display:flex;flex-wrap:wrap;gap:.4rem;padding:.3rem .5rem .5rem}
.cuecustomrow{display:flex;gap:.4rem;padding:0 .5rem .5rem}
.cuecustomrow input{flex:1;min-width:0;border:1px solid var(--line);border-radius:var(--r);
  padding:.4rem .55rem;font-size:.85rem;background:var(--panel);color:var(--fg)}
.readerquickpop .cuecustomrow button{flex:none;width:auto;min-height:2.2rem;padding:0 .8rem}
/* Inline on the page now (readerScrollSettings), not inside a popover, so
   its buttons need their own visible chip styling — .pop button's reset
   (transparent, full-width, left-aligned) doesn't reach them here. */
.readerscrollsettings{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;margin:.3rem 0 .5rem}
.readerscrollsettings .erow{width:100%}
.readerdurationslider{display:flex;align-items:center;gap:.5rem;padding:.4rem .5rem .6rem}
.readerdurationslider input[type=range]{flex:1;min-width:0}
.readerdurationactions{display:flex;flex-wrap:wrap;gap:.4rem;padding:0 .5rem .4rem}
.readerdurationedit .durresults{padding:0 .5rem .4rem}
.durationin{width:4.2rem;text-align:right;font-variant-numeric:tabular-nums}
.durresults{display:flex;flex-direction:column;gap:.3rem;margin:.2rem 0 .4rem}
.durresults:empty{display:none}
.durresults .sbtn{text-align:left;white-space:normal}
.durationsliderval{font-variant-numeric:tabular-nums;font-weight:650;min-width:2.6rem;text-align:right}
.transposeval{font-variant-numeric:tabular-nums;font-weight:650;min-width:1.6rem;text-align:center}
/* Bottom-right floating control cluster: play/pause, zoom, pen — the
   controls worth reaching for constantly mid-song, always on top of the
   chart regardless of scroll position. Translucent so it never fully hides
   the last line or two of lyrics underneath it. The fixed positioning lives
   on the WRAP, not .readerfab itself, so the Draw/Chord/Wait toolseg (a
   sibling, shown only while the pen is on) can stack above it as plain flow
   — the column grows upward from the same bottom-right anchor regardless of
   how many rows the FAB grid itself has. */
.readerfabwrap{position:fixed;right:1rem;bottom:calc(1rem + var(--safeb));z-index:35;
  display:flex;flex-direction:column;align-items:flex-end;gap:.55rem}
/* --playah/--playaw are measured from the strip itself (playAlongMeasure)
   rather than hardcoded: its size changes when the video is collapsed to
   audio-only, when the control rows wrap on a narrow phone, and on rotation.
   Both are always kept current; which one the chart/FAB actually reserve
   space for depends on the breakpoint below. */
:root{--playah:0px;--playaw:0px}
html.playingalong .readerfabwrap{bottom:calc(var(--playah) + .7rem + var(--safeb))}
/* Without this the last lines of a chart sit permanently under the strip and
   can't be scrolled into view at all — the chart's own scroll bottoms out. */
html.playingalong #reader{padding-bottom:calc(var(--playah) + 1rem)}
/* z-index 36: above the FAB (35), below the quick-action popovers (37) —
   an open menu is the thing being looked at, same reasoning as .readerquickpop. */
.playalong{position:fixed;left:0;right:0;bottom:0;z-index:36;
  background:var(--panel);border-top:1px solid var(--line);
  padding:0 .6rem calc(.5rem + var(--safeb));
  box-shadow:0 -6px 20px rgba(0,0,0,.16)}
/* The iframe stays mounted and playing when the video is hidden — collapsing
   to height 0 rather than display:none, which would stop the audio too, and
   unmounting it entirely would reload the track from the start. */
/* Sized from its HEIGHT, not its width: a 16:9 box given the full 34rem of
   column is ~300px tall, which on a phone swallows a third of the chart —
   the thing you are supposed to still be reading. width:fit-content is what
   makes aspect-ratio derive the width from that height rather than stretching
   to the column like a normal block. Desktop below sizes this the opposite
   way (from width), because there the strip's own box is narrow, not short. */
.playavid{aspect-ratio:16/9;height:min(30vh,14rem);width:fit-content;max-width:100%;
  margin:0 auto;overflow:hidden}
.playalong.novideo .playavid{aspect-ratio:auto;height:0;width:100%}
.playavid iframe,.playavid>div{width:100%;height:100%;border:0;display:block}
/* Spotify's embed is a fixed-height strip, not a 16:9 picture — 152px is its
   compact layout with transport controls. :not(.novideo) so hiding it still
   collapses to 0 (same keep-it-mounted reason as above), and specific enough
   to beat the desktop rail's .playavid rule below. */
.playalong.spotify:not(.novideo) .playavid{aspect-ratio:auto;width:100%;height:152px}
/* An uploaded audio file has nothing to show, just controls. */
.playalong.audio .playavid{display:none}
.playapitch button{min-width:2.3rem;min-height:2.3rem;padding:0 .55rem;border:0;background:none;border-radius:999px;
  color:var(--fg);font:inherit;font-weight:600;cursor:pointer}
.playapitch button:hover:not(:disabled){background:var(--panel2)}
.playapitch button:disabled{opacity:.45;cursor:default}
.playapitch .playapitchval{font-weight:500;font-size:.9rem;font-variant-numeric:tabular-nums;white-space:nowrap}
.playapitch .playapitchval:disabled{opacity:1}
.audiotrackup{margin-top:1rem;padding-top:.8rem;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:.5rem;align-items:flex-start}
.audiotrackup p{margin:0}
.audiotrackup .goalprogress{margin:0;max-width:20rem}
.playactl{display:flex;flex-direction:column;gap:.4rem;padding:.45rem 0 .1rem;
  max-width:34rem;margin:0 auto}
.playarow{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
/* Hide-video and close belong at the far edge, away from the transport —
   they end the session rather than adjust it, and a mis-tap on ✕ while
   reaching for play costs you your place in the track. */
.playarow .push{margin-left:auto}
.playarow .readerfabbtn{width:2.5rem;height:2.5rem}
.playatime{font-variant-numeric:tabular-nums;font-size:.8rem;color:var(--muted);
  min-width:5.5rem}
.playastatus{color:var(--muted);font-size:.78rem;line-height:1.35;padding:.2rem 0}
.playastatus a{color:inherit}
.readerlinks{padding:.2rem .4rem}
/* Speed/loop/tag are set-once-and-forget, folded behind a disclosure so the
   dock's default height is just transport + seek — see renderPlayAlong(). */
.playaoptions>summary{cursor:pointer;color:var(--muted);font-size:.85rem;padding:.4rem .1rem;
  list-style:none}
.playaoptions>summary::-webkit-details-marker{display:none}
.playaoptions>summary::before{content:"▸ "}
.playaoptions[open]>summary::before{content:"▾ "}
.playaoptions>.playarow{margin-top:.4rem}
.playaseek{display:flex;flex-direction:column;gap:.15rem;color:var(--muted);
  font-size:.8rem;max-width:34rem;margin:0 auto}
.playaseek input{width:100%;min-width:0;accent-color:var(--accent);margin:0}

/* 861px is the same "phone card layout vs. desktop table layout" line the
   rest of the app already draws (see .cardline above) — reused here rather
   than invented fresh, so "desktop" means the same width everywhere. Below
   it the strip stays a bottom-docked bar (the block above, untouched); at
   or above it a docked-bottom strip would eat a third of the visible chart
   height where there's plenty of spare WIDTH instead — so it becomes a
   right-side rail, full height, video over controls in the same column
   order, just turned 90 degrees. */
@media (min-width:861px){
  html.playingalong .readerfabwrap{bottom:calc(1rem + var(--safeb));
    right:calc(var(--playaw) + 1.2rem)}
  /* #reader is left-anchored at max-width:60rem everywhere else (see the
     #pr,#set,#org,#goals,#reader rule) — fine when nothing else shares the
     row. With the rail docked on the right, leaving that anchor alone left
     a wide empty gap between the capped-width chart and the rail, reading
     as a spurious third panel. margin-left:auto shoves the (still ≤60rem)
     box flush against the rail instead of pinning it to the left edge. */
  html.playingalong #reader{padding-bottom:0;margin-left:auto;
    margin-right:calc(var(--playaw) + 1.2rem)}
  .playalong{left:auto;right:0;top:var(--headh);bottom:0;
    width:min(24rem,34vw);
    border-top:0;border-left:1px solid var(--line);
    padding:.7rem .8rem calc(.7rem + var(--safeb));
    box-shadow:-6px 0 20px rgba(0,0,0,.16);
    display:flex;flex-direction:column;gap:.3rem;overflow-y:auto}
  /* Sized from its WIDTH now (the rail's, not the viewport's) — height:auto
     lets aspect-ratio derive from that instead of the mobile rule's
     height:min(30vh,14rem), which would otherwise cap a wide rail's video
     at a letterboxed sliver. */
  .playavid{width:100%;height:auto;max-width:none;margin:0}
  .playalong.novideo .playavid{height:0}
  .playactl{max-width:none;margin:0}
}

/* Tuner: same docked-panel technique as play-along above (--tunerh/--tunerw
   measured off the panel itself, html.tunerActive gates the reservation,
   same 861px breakpoint flips bottom bar -> right rail) — the two are
   mutually exclusive at the JS level (tunerStart()/playAlongMount() each
   unmount the other), so these rules never have to account for both being
   open at once. */
:root{--tunerh:0px;--tunerw:0px}
html.tunerActive .readerfabwrap{bottom:calc(var(--tunerh) + .7rem + var(--safeb))}
html.tunerActive #reader{padding-bottom:calc(var(--tunerh) + 1rem)}
.tuner{position:fixed;left:0;right:0;bottom:0;z-index:36;
  background:var(--panel);border-top:1px solid var(--line);
  padding:.6rem .9rem calc(.6rem + var(--safeb));
  box-shadow:0 -6px 20px rgba(0,0,0,.16);
  display:flex;flex-direction:column;gap:.5rem}
.tunerhead,.metrohead{display:flex;align-items:center;justify-content:space-between}
.tunerhead .chip,.metrohead .chip{width:2.2rem;height:2.2rem;min-height:0;padding:0;justify-content:center;font-size:.95rem}
.tunertitle{font-weight:650;font-size:.85rem}
.tunerbody{display:flex;flex-direction:row;align-items:center;gap:1rem}
.tunerleft{display:flex;flex-direction:column;align-items:center;gap:.3rem;min-width:3.6rem}
.tunernoterow{display:flex;align-items:flex-end;gap:.2rem}
.tunernote{font-size:2.2rem;font-weight:700;line-height:1;color:var(--muted)}
.tunernote.tuner-good{color:var(--p4fg)}
.tunernote.tuner-close{color:var(--p2fg)}
.tunernote.tuner-off{color:var(--p1fg)}
.tuneroct{font-size:.85rem;font-weight:600;color:var(--muted);margin-bottom:.25rem}
.tunercents{font-size:.72rem;font-weight:700;color:var(--muted);white-space:nowrap}
.tunercents.tuner-good{color:var(--p4fg)}
.tunercents.tuner-close{color:var(--p2fg)}
.tunercents.tuner-off{color:var(--p1fg)}
.tunerright{display:flex;flex-direction:column;gap:.4rem;flex:1;min-width:0}
.tunermeter{position:relative;height:.5rem;border-radius:999px;background:var(--line2)}
.tunerzone{position:absolute;left:45%;width:10%;height:100%;border-radius:999px;background:var(--p4bg)}
.tunerind{position:absolute;top:-.35rem;left:50%;width:.25rem;height:1.2rem;border-radius:.15rem;
  background:var(--muted);transform:translateX(-50%);transition:left .1s linear}
.tunerind.tuner-good{background:var(--p4fg)}
.tunerind.tuner-close{background:var(--p2fg)}
.tunerind.tuner-off{background:var(--p1fg)}
.tunerticks{display:none}
.tunerfreq{font-size:.72rem;color:var(--muted)}
.tunerfoot{display:flex;align-items:center;justify-content:space-between;font-size:.72rem;color:var(--muted)}
.tunerfoot .tunerref{color:var(--fg);font-weight:600}

/* Desktop: a compact card at the chart's bottom-left, not a full-height
   column — the Practice rail stays usable beside it. */
@media (min-width:861px){
  html.tunerActive .readerfabwrap{bottom:calc(1rem + var(--safeb))}
  html.tunerActive #reader{padding-bottom:calc(var(--tunerh) + 2rem)}
  .tuner{left:1rem;right:auto;bottom:1rem;width:20rem;border:1px solid var(--line);border-radius:14px;
    padding:.7rem .9rem .8rem;box-shadow:0 10px 30px rgba(0,0,0,.18)}
  .tunernote{font-size:2.6rem}
}

/* Metronome: identical docked-panel technique to the tuner above
   (--metroh/--metrow, html.metroActive, same 861px breakpoint) — mutually
   exclusive with both the tuner and play-along at the JS level
   (metroMount()/tunerStart()/playAlongMount() each unmount the other two). */
:root{--metroh:0px;--metrow:0px}
html.metroActive .readerfabwrap{bottom:calc(var(--metroh) + .7rem + var(--safeb))}
html.metroActive #reader{padding-bottom:calc(var(--metroh) + 1rem)}
.metro{position:fixed;left:0;right:0;bottom:0;z-index:36;
  background:var(--panel);border-top:1px solid var(--line);
  padding:.6rem .9rem calc(.6rem + var(--safeb));
  box-shadow:0 -6px 20px rgba(0,0,0,.16);
  display:flex;flex-direction:column;gap:.5rem}
.metrohead{display:flex;align-items:center;justify-content:space-between}
.metrotitle{font-weight:650;font-size:.85rem}
.metrobody{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:1rem}
.metroleft{display:flex;flex-direction:row;align-items:center;gap:.45rem;flex:1 1 100%;min-width:0}
.metroleft>.chip{margin-left:auto}
.metrobpmrow .chip{width:2.2rem;height:2.2rem;min-height:0;padding:0;justify-content:center;font-size:1.05rem}
.metrobpmrow{display:flex;align-items:center;gap:.5rem}
.metrobpm{font-size:1.8rem;font-weight:700;font-variant-numeric:tabular-nums;min-width:2.8rem;text-align:center}
.metrobpmlabel{font-size:.68rem;color:var(--muted);letter-spacing:.04em;text-transform:uppercase}
/* flex-basis:100% forces the beat-selector/dots/toggle group onto its own
   full-width row on mobile whenever it can't sit beside the BPM controls
   without crowding the toggle button off the edge of a narrow phone screen
   (the toggle is a fixed 2.9rem circle, not shrinkable) — flex-wrap alone
   would only wrap it once other content pushed it past a browser's own
   min-content threshold, which was too late to prevent the cutoff. */
.metroright{display:flex;flex-direction:row;align-items:center;gap:.7rem;flex:1 1 100%;min-width:0}
.metrodots{display:flex;gap:.35rem;flex:1;justify-content:center}
.metrodot{width:.6rem;height:.6rem;border-radius:999px;background:var(--line2);flex:none}
.metrodot.down{width:.75rem;height:.75rem}
.metrodot.on{background:var(--accent)}
.metrotoggle{flex:none;width:2.9rem;height:2.9rem;border-radius:999px;border:0;
  background:var(--accent);color:var(--accent-fg);display:flex;align-items:center;justify-content:center}

@media (min-width:861px){
  html.metroActive .readerfabwrap{bottom:calc(1rem + var(--safeb))}
  html.metroActive #reader{padding-bottom:calc(var(--metroh) + 2rem)}
  .metro{left:1rem;right:auto;bottom:1rem;width:21rem;border:1px solid var(--line);border-radius:14px;
    padding:.7rem .9rem .8rem;box-shadow:0 10px 30px rgba(0,0,0,.18)}
}
.readerfab{display:grid;grid-template-columns:repeat(2,1fr);gap:.45rem;justify-items:center;
  background:color-mix(in srgb, var(--panel) 45%, transparent);
  border:1px solid var(--line);border-radius:1.3rem;padding:.5rem .4rem;
  box-shadow:0 4px 18px rgba(0,0,0,.18)}
/* Spans both of .readerfab's grid columns so Prev/Next always land on their
   own row, next to each other — see readerFab()'s comment on why they can't
   just be two more plain grid items. */
.readerfabnav{grid-column:1 / -1;display:flex;justify-content:center;gap:.45rem}
.toolseg{display:flex;background:var(--panel);border:1px solid var(--line);border-radius:999px;
  padding:.2rem;box-shadow:0 4px 14px rgba(0,0,0,.16);gap:.15rem}
.toolseg button{border:0;background:none;border-radius:999px;padding:.4rem .65rem;
  font-size:.8rem;font-weight:600;color:var(--muted);white-space:nowrap}
.toolseg button.on{background:var(--accent);color:var(--accent-fg)}
.toolseg button:disabled{opacity:.4;cursor:not-allowed}
/* Pen, Highlight, Note, Eraser, Undo, Clear: wraps to two rows on a phone
   rather than running off both edges. */
.toolseg.drawctl{flex-wrap:wrap;justify-content:center;max-width:calc(100vw - 2rem);border-radius:1.4rem}
.toolseg.againseg{flex-wrap:wrap;width:fit-content;max-width:100%}
/* --line is too close to --panel2 to read as a border against the FAB's own
   translucent backdrop — the resting button looked borderless next to the
   solid-accent "on" state, which doesn't have that contrast problem. A
   shadow gives the resting state the same definition without depending on a
   border color that has to work over an arbitrary chart behind it. Each
   button's own fill is translucent too, not just the cluster's backdrop —
   "I'll need to be able to read through them" — so the chart line a button
   happens to sit over is never fully hidden, just dimmed. No backdrop-filter
   blur here (tried it first) — blur made the text behind unreadable, which
   is the opposite of the point; plain alpha transparency stays sharp. */
.readerfabbtn{flex:none;width:2.9rem;height:2.9rem;border-radius:50%;padding:0;
  display:flex;align-items:center;justify-content:center;font-size:1.1rem;
  background:color-mix(in srgb, var(--panel2) 40%, transparent);border:1px solid var(--line);
  box-shadow:0 1px 3px rgba(0,0,0,.22)}
.readerfabbtn.on{background:color-mix(in srgb, var(--accent) 62%, transparent);
  color:var(--accent-fg);border-color:var(--accent)}
.readerfabbtn.small{width:2.35rem;height:2.35rem;font-size:.82rem}
/* A control that doesn't apply right now (draw with no chart yet, transpose
   with no chords) reads as "not right now", not "gone" — see readerFab and
   readerMusicMenu. Buttons keep their spot in the grid/list either way,
   so nothing else shifts around depending on what a given song has. Disabled
   dims the icon a bit further than a resting button's already-translucent
   fill, floored so it doesn't disappear entirely against a busy chart. */
.readerfabbtn:disabled{opacity:.55;cursor:not-allowed;box-shadow:none}
.readerquickpop button:disabled,.readerquickpop .erow.disabled{opacity:.4;cursor:not-allowed}
.readermeta{margin:.3rem 0 .6rem}
/* Room under the chart's last line, so auto-scroll ends with it up off the
   bottom edge (READER_OVERSHOOT, a % of the screen). */
#reader::after{content:"";display:block;height:calc(var(--overshoot,25) * 1vh)}
#reader:has(.cppaged)::after{display:none}
.readerchips{display:flex;flex-wrap:wrap;gap:.3rem;margin-bottom:.25rem}
/* A set capo is the one setup step that's easy to miss until the first chord. */
.readermeta button.tagpill.capobtn:not(.ghost){font-weight:700;color:var(--accent);
  border-color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,var(--panel))}
@media (min-width:861px){
  .readermeta .readerchips .tagpill{font-size:.9rem;padding:.16rem .5rem}
}
.rigcue{display:inline-block;margin:.15rem 0 .3rem;padding:.3rem .65rem;border-radius:var(--r);
  font-weight:700;font-size:.95rem;background:var(--p2bg);color:var(--p2fg);
  border:1.5px solid currentColor}
/* pan-y (not the default "auto") is what actually disables the browser's own
   pinch-zoom and double-tap-zoom gestures here, so a two-finger touch reaches
   the app's pointer handlers instead — see wireReaderPinchZoom() in app.js.
   Vertical scrolling is unaffected; pan-y explicitly keeps it. */
.readermaterials{margin-top:.5rem;touch-action:pan-y}
.readermattabs{display:flex;flex-wrap:wrap;gap:.3rem;margin-bottom:.5rem}
.readertext{white-space:pre-wrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  line-height:1.6;font-size:calc(.95rem * var(--readerfont));padding-bottom:calc(var(--fabh,4rem) + 1rem)}
.readermaterials .chordpro{font-size:calc(.95rem * var(--readerfont));
  line-height:1.7;max-height:none;padding-bottom:calc(var(--fabh,4rem) + 1rem)}
.chordpro.cpcols{column-gap:2.5em;column-rule:1px solid var(--line2)}
.chordpro.cpcols>*{break-inside:avoid}
.chordpro.cpcols>.cpsection{break-after:avoid}
.readerpdf{position:relative}
.readerpdf .timerline{position:absolute;left:.4rem;right:.4rem;margin:0}
.readerpdf .timerchip{box-shadow:0 1px 4px rgb(0 0 0 / .25)}
.readerpdf .timerrule{border-top-color:var(--accent)}
.readerpdfpages{display:flex;flex-direction:column;gap:.5rem}
.readerpdfpage{display:block;width:100%;height:auto;background:#fff;border:1px solid var(--line);border-radius:var(--r)}
.readermatcontent{position:relative}
.readerannotwrap{position:absolute;inset:0;pointer-events:none}
/* Chords on top: each progression once, pinned under the header while the
   lyrics scroll; capped so it can never take over a phone screen. */
.chordsummary{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:calc(.9rem * var(--readerfont));
  line-height:1.45;background:var(--panel2);border:1px solid var(--line2);border-radius:var(--r);
  padding:.35rem .6rem;margin-bottom:.4rem;display:grid;grid-template-columns:max-content 1fr;column-gap:.8em}
.chordsummary.pinned{position:sticky;top:var(--headh);z-index:5;max-height:35vh;overflow-y:auto;
  box-shadow:0 2px 6px rgb(0 0 0 / .08)}
.chordsumrow{display:contents}
.chordsumlabel{font-weight:700;color:var(--fg);white-space:nowrap}
.chordsumlabel::after{content:":"}
.chordsumprog{color:var(--accent);font-weight:700;word-spacing:.35em}
.chordsumtimes{color:var(--muted);font-size:.85em;margin-left:.6em;word-spacing:normal}
/* Fold repeats: a word-for-word repeat keeps only its heading. */
.cpfolded{display:none !important}
.cpfoldhead{cursor:pointer}
.cpfoldhead::after{content:" ↻ repeat";color:var(--muted);font-weight:500;font-size:.8em}
.cpfoldhead.cpunfolded::after{content:" ▾"}
/* Cliff notes: sized by fitCliff() (app.js) to fit one screen. */
.cliffnotes{font-size:calc(.95rem * var(--readerfont));line-height:1.45;column-gap:2em;column-rule:1px solid var(--line2)}
.cliffnotes>*{break-inside:avoid}
.cliffhead{display:flex;align-items:center;gap:.4rem;margin-bottom:.4rem;font-size:max(.8rem, .85em);column-span:all}
.cliffhead b{margin-right:auto}
.cliffnotes .chordsummary{font-size:1em;margin-bottom:.5em}
.cliffnotes .chordpro{font-size:1em;padding:.4em .6em;margin-top:0;line-height:1.5}
.clifform{font-weight:700;margin:.2em 0 .5em;color:var(--fg)}
.cliffcues{display:grid;grid-template-columns:max-content minmax(0,1fr);column-gap:.8em;row-gap:.15em}
.cliffcue{display:contents}
.cliffcuetext{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--muted)}
.cliffmode .readerannotwrap{display:none}
.charteditor{width:100%;min-height:55vh;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9rem;line-height:1.5}
.cliffaipreview .chordpro{max-height:40vh}
.readerannot{width:100%;height:100%;display:block;touch-action:pan-y}
.readerannot.active{touch-action:none;cursor:crosshair}
/* The tab bar has nothing to do mid-chart and can visibly float over
   reader content while scrolling on iOS — see the render() comment in
   app.js. There is no separate full-screen "performance" reader any more
   (Prev/Next now live in the FAB instead, see readerFab) — this is the only
   session reader view, so no html.perfmode chrome-hiding is needed either. */
html.reading nav#tabs { display: none; }

/* The chart reader is the practice command center on a phone. Keep the song
   title readable, then give the three jobs one labeled row of their own.
   Each opens the same bottom sheet instead of a different icon-only page. */
@media (max-width:860px){
  html.reading header{padding:.35rem .55rem;gap:.4rem}
  html.reading #readerback,html.insettings #readerback{width:44px;height:44px}
  html.reading header h1{font-size:1.05rem;min-width:0}
  html.reading #tab-set,html.reading #theme{display:none!important}
  html.reading #readerqa{order:5;width:100%;margin:0;display:flex;gap:.4rem}
  html.reading #readerqa>.readerquickmenu{min-width:0;flex:1}
  html.reading #readerqa>.readermoremenu{flex:none}
  html.reading #readerqa>.readermoremenu>summary.icon{width:44px;height:44px;border-radius:10px;background:var(--panel2)}
  html.reading #readerqa>.readerquickmenu>summary.icon{width:100%;height:44px;
    padding:.3rem .4rem;border-radius:10px;background:var(--panel2)}
  html.reading #readerqa>.readerquickmenu>summary.icon.on{background:var(--rndbg)}
  .readercommandlabel{font-size:.8rem;overflow:hidden;text-overflow:ellipsis}
  /* An open sheet is modal on a phone: the chart behind it holds still and
     dims, and a tap on the dimmed part closes it (quickMenuShell). Swiping
     used to scroll the chart underneath, which made the sheet feel detached. */
  html.reading:has(#readerqa .readerquickmenu[open]),
  html.reading:has(#readerqa .readerquickmenu[open]) body{overflow:hidden;overscroll-behavior:none}
  #readerqa .readerquickmenu[open]::before{content:"";position:fixed;inset:0;
    background:rgba(0,0,0,.28);z-index:36}
  .pop.readerquickpop{top:auto;left:0;right:0;bottom:0;width:100%;
    max-height:min(86dvh,calc(100dvh - var(--headh) - 1rem));
    padding:0 .35rem var(--safeb);border:1px solid var(--line);border-bottom:0;
    border-radius:16px 16px 0 0;overscroll-behavior:contain;
    box-shadow:0 -8px 40px rgba(0,0,0,.28);animation:sheetup .16s ease-out}
  .readercommandhead{padding-top:1.1rem}
  .readercommandhead::before{content:"";position:absolute;top:.4rem;left:50%;width:2.4rem;height:4px;
    margin-left:-1.2rem;border-radius:2px;background:var(--line)}
  .readerquickpop button,.readerquickpop .chip{min-height:44px}
  .readercommandhead{padding:.6rem .55rem}
  .readercommandsection{padding:.75rem .55rem}
  .readercommandsection>.note{margin:.35rem 0}
  .readercommandsection .erow{gap:.45rem;flex-wrap:wrap}
  .readercommandsection .erow .lbl{flex:1;min-width:7rem}
  .readercommandsection .cuepresets{padding:.25rem 0 .6rem;gap:.45rem}
  .readercommandsection .cuecustomrow{padding:0 0 .35rem}
  .readercommandsection .readerlinks .linkbar{display:grid;grid-template-columns:1fr 1fr;gap:.4rem}
  .readercommandsection .readerlinks .lnk{min-height:44px;display:flex;align-items:center}
}

/* iPad landscape and similarly sized touch screens need a third density.
   Keeping the desktop-style side popover preserves the wide chart, while
   the larger type and 44px controls keep rotation from turning the reader
   into a mouse-oriented interface. This range deliberately stops before a
   typical laptop width; desktop tables retain their compact density. */
@media (min-width:861px) and (max-width:1180px){
  :root{--root:16px}
  html.reading header{padding:.35rem .65rem;gap:.5rem}
  html.reading #readerback,html.insettings #readerback{min-width:44px;min-height:44px}
  html.reading #readerqa{gap:.4rem}
  html.reading #readerqa>.readerquickmenu>summary.icon{min-height:44px;padding:.45rem .75rem}
  .readercommandlabel{font-size:.84rem}
  .pop.readerquickpop{right:.7rem;top:calc(var(--headh) + .55rem);
    width:min(29rem,calc(100vw - 1.4rem));max-height:calc(100dvh - var(--headh) - 1.1rem)}
  .readerquickpop button,.readerquickpop .chip{min-height:44px}
  .readercommandhead{padding:.75rem .7rem}
  .readercommandsection{padding:.85rem .7rem}
  .readercommandsection h3{font-size:.78rem}
  .readercommandsection>p{font-size:.94rem}
  .readercommandsection .cuepresets{padding:.35rem 0 .7rem;gap:.5rem}
  .readercommandsection .cuecustomrow{padding:0 0 .4rem}
  .cuecustomrow input{min-height:44px;font-size:1rem}
  .readerfabbtn{width:3rem;height:3rem}
  .readerfabbtn.small{width:2.75rem;height:2.75rem}
  .toolseg button{min-height:44px;padding:.5rem .8rem}
  .readerquickpop .recstartbtn{min-height:3.4rem}
  .readerrecmenu .recrow .setacts>.chip{min-height:44px}
}

/* ── recording ──────────────────────────────────────────────────────── */
.readerrecordings{display:flex;flex-direction:column;gap:.65rem}
.recbox{display:flex;flex-direction:column;gap:.4rem}
.reckindrow{display:flex;align-items:center}
.recmodes{display:grid;grid-template-columns:1fr 1fr;width:100%}
.readerquickpop .recmodes button{min-height:2.75rem;text-align:center;border-radius:999px}
.readerquickpop .recmodes button.on{background:var(--accent);color:var(--accent-fg)}
.readerquickpop .recstartbtn{width:100%;min-height:3.25rem;border:1px solid var(--bad);border-radius:var(--r);
  background:var(--bad);color:#fff;font-size:1rem;font-weight:700}
.readerquickpop .recstartbtn:disabled{opacity:.65}
.recoptions{border-top:1px solid var(--line2);padding-top:.2rem}
.recoptions>summary{padding:.55rem 0;color:var(--muted);font-size:.86rem;cursor:pointer}
.recoptions .recbeepctl{padding:.35rem 0 .55rem}
.recstarterror{padding:.65rem .75rem;border-radius:var(--r);line-height:1.4;
  background:color-mix(in srgb, var(--bad) 10%, var(--panel));color:var(--bad);font-size:.88rem}
.recattach{display:flex}
.recattach .chip{width:100%;justify-content:center;border-style:dashed;color:var(--muted)}
.recsavedhead{margin-top:.25rem;padding-top:.75rem;border-top:1px solid var(--line2)}
.recempty{text-align:center;color:var(--muted);font-size:.86rem;padding:.45rem 0}
.recindicator{align-items:center}
/* Docked at the bottom like the FAB itself (readerRecIndicator) — inline in
   the flow used to mean Stop scrolled out of reach on a long chart, same bug
   the camera-menu popover had, just relocated. Same z-index/pattern as
   play-along/tuner/metro's own docked panels (see html.playingalong etc.
   below); #reader.recmini nudges the FAB up to clear it, the same way those
   do via their own --Xh variables — this bar's height is stable enough
   (one line, no live meter) not to need one. */
.recindicator.mini{position:fixed;left:0;right:0;bottom:0;z-index:36;
  background:color-mix(in srgb, var(--bad) 10%, var(--panel));
  border-top:1px solid var(--line);padding:.5rem .7rem;
  padding-bottom:calc(.5rem + var(--safeb));margin-bottom:0;justify-content:center}
#reader.recmini .readerfabwrap{bottom:calc(3.4rem + var(--safeb))}
.recdot{width:.6rem;height:.6rem;border-radius:50%;background:var(--bad);
  animation:recpulse 1.4s ease-in-out infinite}
@keyframes recpulse{0%,100%{opacity:1}50%{opacity:.35}}
.recclock{font-variant-numeric:tabular-nums;font-weight:650}
.recuploadwrap{height:6px;border-radius:4px;background:var(--line2);overflow:hidden}
.recuploadbar{height:100%;width:0;background:var(--accent);transition:width .15s}
.recrow audio,.recrow video{display:block}
.reckindrow{align-items:center;flex-wrap:wrap}
.recbeepctl{display:flex;align-items:center;gap:.3rem;font-size:.85rem;color:var(--muted);
  cursor:pointer;margin-left:.3rem}
.recbeepctl input{margin:0;width:1.15rem;height:1.15rem}
.reclocal{padding-top:.4rem;border-top:1px dashed var(--line2)}
.recordreview>audio,.recordreview>video{display:block;width:100%;max-height:42dvh;
  margin:.65rem 0;background:#000;border-radius:var(--r)}
.recstatus{padding:.55rem .7rem;border-radius:var(--r);background:var(--panel2);
  font-size:.88rem;line-height:1.4}
.recstatus.bad{background:color-mix(in srgb, var(--bad) 10%, var(--panel))}
.rectakeactions{display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin:.65rem 0}
.recaction{min-height:3rem;border:1px solid var(--line);border-radius:var(--r);
  background:var(--panel);color:var(--fg);font-weight:650}
.recaction.primary{grid-column:1/-1;background:var(--accent);border-color:var(--accent);
  color:var(--accent-fg)}
.recaction.danger{color:var(--bad)}
.recnotes{margin-top:.35rem;border-top:1px solid var(--line2);border-bottom:1px solid var(--line2)}
.recnotes>summary{padding:.8rem 0;font-weight:650;cursor:pointer}
.recnotes .readernotes{margin-top:0;padding:.2rem 0 .7rem;border-top:0}
.bad{color:var(--bad)}

@media (max-width:860px){
  .readerrecmenu .recrow{flex-direction:column;align-items:stretch}
  .readerrecmenu .recrow .setacts{display:grid;grid-template-columns:repeat(3,1fr);width:100%}
  .readerrecmenu .recrow .setacts>.chip{justify-content:center;min-height:44px}
  .readerrecmenu .recstartbtn{min-height:3.4rem}
  .recordreview .rectakeactions{position:sticky;bottom:calc(-1rem - var(--safeb));
    z-index:1;background:var(--panel);padding:.6rem 0 calc(.6rem + var(--safeb));margin-bottom:0}
}
@media (min-width:861px) and (max-width:1180px){
  .readerquickpop .recstartbtn{min-height:3.4rem}
  .readerrecmenu .recrow .setacts>.chip{min-height:44px}
  .recoptions>summary{min-height:44px;display:flex;align-items:center}
  .recattach .chip{min-height:44px}
}

/* ── offline status pill (header) ──────────────────────────────────────── */
.updatebanner{position:sticky;top:0;z-index:60;padding:.5rem .8rem;padding-top:calc(.5rem + env(safe-area-inset-top));
  background:var(--warn-bg,#fff3cd);color:var(--warn-fg,#7a5b00);font-size:.9rem;text-align:center}
.offlinepill{font-size:.72rem;padding:.15rem .55rem;border-radius:999px;
  background:var(--warn-bg,#fff3cd);color:var(--warn-fg,#7a5b00);border:none;
  white-space:nowrap;cursor:pointer;margin-right:.4rem}
html[data-theme="dark"] .offlinepill{background:#4a3c00;color:#ffe08a}

/* ── perform mode ───────────────────────────────────────────────────── */
/* Countdown dominates the screen on purpose — it's the one moment in the
   whole flow that isn't lyrics-first, a three-second "get ready" before the
   chart takes over again. Tap anywhere to skip straight to recording — except
   the Skip button, a declined song rather than a shortened wait, which needs
   its own tap target inside this same layer since it covers performctl too. */
.performcountdown{position:fixed;inset:0;z-index:40;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:2.5rem;color:#fff;
  background:rgba(0,0,0,.55);cursor:pointer}
.performcountnum{font-size:7rem;font-weight:800}
/* A row now (Skip + Stop performing), not one button — the wrapping
   .performcountskiprow carries the absolute positioning that used to sit on
   the single button directly. */
.performcountskiprow{position:absolute;left:1rem;right:1rem;bottom:calc(1rem + var(--safeb));
  display:flex;gap:.6rem}
.performcountskip{flex:1;background:rgba(255,255,255,.14);
  border-color:rgba(255,255,255,.35);color:#fff}
/* Small and out of the way — confirms you're framed, nothing more. The
   chart is still what you're actually using; see the Camera-preview design
   note in the Perform-mode plan. */
.performcam{position:fixed;top:calc(var(--headh) + .6rem);right:1rem;z-index:34;
  width:6.5rem;border-radius:var(--r);box-shadow:0 2px 10px rgba(0,0,0,.3);
  background:#000;object-fit:cover}
.performctl{position:fixed;left:0;right:0;bottom:0;z-index:36;display:flex;gap:.6rem;
  padding:.7rem 1rem calc(.7rem + var(--safeb));border-top:1px solid var(--line);
  background:color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
.performbtn{min-height:3.4rem;border-radius:var(--r);font-size:1.05rem;font-weight:650;
  background:var(--panel2);border:1px solid var(--line)}
.performbtn.primary{flex:2;background:var(--accent);border-color:var(--accent);color:var(--accent-fg)}
.performbtn.secondary{flex:1}
.performbtn:disabled{opacity:.5;cursor:not-allowed}
/* The small always-on FAB (autoscroll/pen/font) would otherwise sit right
   under the big Next/Skip bar — same corner, same bottom offset. */
#reader.performing .readerfabwrap{bottom:calc(5.5rem + var(--safeb))}
.performsummary{padding:.5rem 0 5rem}
.performsummary .plitems{display:flex;flex-direction:column;gap:.4rem;margin:.8rem 0}
.performsummary .prow{justify-content:space-between;gap:.6rem}
/* Distinct from the recording state's red dot — this is a brief, passive
   moment (the recorder flushing its last chunk), not something to draw the
   eye to, and it must not read as still-recording. */
.recindicator.stopping{color:var(--muted)}
.performwarn{display:flex;align-items:center;gap:.5rem;margin:.5rem 0;padding:.5rem .7rem;
  border-radius:var(--r);background:var(--p1bg);color:var(--p1fg)}

/* ── practice notes & focus ─────────────────────────────────────────── */
.readernotes{margin-top:1rem;padding-top:.6rem;border-top:1px solid var(--line2)}
.focusbanner{display:flex;gap:.5rem;align-items:center;padding:.55rem .7rem;
  margin-bottom:.5rem;border-radius:var(--r);background:var(--rndbg);
  border:1px solid var(--rndline)}
.focusbanner>div:first-child{flex:1;min-width:0}
.focustext{font-weight:600;margin-top:.1rem;line-height:1.35}
.sugpanel{margin-top:.4rem}
.notelist{display:flex;flex-direction:column;gap:.4rem;margin-top:.2rem}
.noterow{padding:.3rem 0;border-top:1px solid var(--line2);line-height:1.4}
.noterow:first-child{border-top:0}
.noterowhead{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
.noteremove{padding:.05rem .4rem;font-size:.78rem;color:var(--muted)}
.noteremove:hover{color:var(--bad);border-color:var(--bad)}

/* ── playlists: master / detail ─────────────────────────────────────── */
#main.split{padding:0;overflow:hidden}
#plsplit{display:grid;grid-template-columns:17rem 1fr;
  height:calc(100vh - var(--headh) - var(--navh));overflow:hidden}
.plmaster{border-right:1px solid var(--line);background:var(--panel);
  overflow-y:auto;padding:.5rem .45rem 3rem}
.pldetail{overflow-y:auto;padding:.7rem .9rem 4rem;min-width:0}
.plbar{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap;margin-bottom:.4rem}
.pastrehearsals{margin-bottom:.4rem}
.pastrehearsals summary{color:var(--muted);font-size:.85rem;cursor:pointer;
  list-style:none;display:inline-block}
.pastrehearsals summary::-webkit-details-marker{display:none}
.pastrehearsals summary::before{content:"▸ ";display:inline-block}
.pastrehearsals[open] summary::before{content:"▾ "}
.pastrehearsals .plbar{margin-top:.35rem;margin-bottom:0}

.plrow{display:flex;align-items:center;gap:.35rem;width:100%;text-align:left;
  background:none;border:0;border-radius:var(--r);padding:.22rem .4rem;
  color:var(--fg);font-size:.86rem;line-height:1.5}
.plrow:hover{background:var(--panel2)}
.plrow.on{background:var(--accent);color:var(--accent-fg);font-weight:600}
.plrow .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.plrow .n{font-variant-numeric:tabular-nums;font-size:.76rem;opacity:.55}
.plrow.on .n{opacity:.85}
.plrow .star{width:.9em;flex:none;opacity:.8}

.plhead{display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap;
  padding-bottom:.4rem;border-bottom:1px solid var(--line);margin-bottom:.4rem}
.plhead h2{margin:0;font-size:1.02rem;font-weight:650;letter-spacing:-.01em}
.plhead .sub{color:var(--muted);font-size:.78rem;flex-basis:100%}
.plhead .acts{margin-left:auto;display:flex;gap:.3rem;flex-wrap:wrap;justify-content:flex-end}
.plback{display:none}
.plstar{background:none;border:0;padding:0 .1rem;font-size:1.2rem;line-height:1;
  color:var(--muted);cursor:pointer}
.plstar.on{color:var(--accent)}

/* The setlist stat cards + description — "this is the list you're looking
   for" at a glance, in place of one small sentence competing with the rest
   of the header for attention. */
.plstats{flex-basis:100%;display:flex;gap:.5rem;margin:.15rem 0}
.plgoals{flex-basis:100%;display:flex;flex-wrap:wrap;gap:.4rem;margin:.1rem 0}
.gigready{flex-basis:100%;display:flex;flex-wrap:wrap;align-items:center;gap:.25rem .4rem;margin:.1rem 0;font-size:.9em}
.gigready:not(.ok):not(:empty){color:var(--bad)}
.gigready.ok{color:var(--ghit)}
.gigready .chip{font-size:.85em}
.plgoal{border:1px solid var(--line);border-radius:999px;background:var(--panel);color:inherit;padding:.3rem .75rem;min-height:36px;font:inherit;font-size:.88rem;text-align:left;cursor:pointer;max-width:100%;overflow-wrap:anywhere}
.plgoaladd{min-height:36px}
.musicalgoal.flash{animation:linkpulse 1.4s ease-out 2;border-color:var(--accent)}
.plstat{flex:1;min-width:5.5rem;padding:.4rem .55rem;border:1px solid var(--line);
  border-radius:var(--r);text-align:center;background:var(--panel)}
.plstat b{display:block;font-size:1.1rem;font-variant-numeric:tabular-nums}
.plstat span{font-size:.72rem;color:var(--muted)}
.plstat.action{cursor:pointer;border-color:var(--accent);border-style:dashed}
.plstat.action b,.plstat.action span{color:var(--accent)}
.plstat.action:hover{background:var(--panel2)}
/* The main reason to be on this page — full width, its own line, well above
   the fold instead of a chip sharing a thin bar with pin/folder/⋯. */
.bigstart{flex-basis:100%;display:block;width:100%;margin:.3rem 0;padding:.7rem 1rem;
  font-size:1.02rem;font-weight:650;border-radius:var(--r);border:0;
  background:var(--accent);color:var(--accent-fg);cursor:pointer}
.bigstart:hover{filter:brightness(1.06)}
/* Start rehearsal and Perform share the row instead of each claiming a full
   line of their own — .startrow takes over the "full width, own line" job
   .bigstart used to do alone, so its two children split that line instead. */
.startrow{display:flex;flex-basis:100%;gap:.5rem;margin:.3rem 0}
.startrow .bigstart{flex:1;flex-basis:0;width:auto;margin:0}
.bigstart.secondary{background:var(--panel2);color:var(--fg);border:1px solid var(--line)}
.bigstart.secondary:hover{filter:none;border-color:var(--accent)}
.pldesc{flex-basis:100%;resize:vertical;min-height:2.1rem;margin:.1rem 0 .2rem;
  padding:.4rem .55rem;border:1px solid var(--line);border-radius:var(--r);
  background:var(--panel);color:var(--fg);font:inherit;font-size:.86rem}
.pldesc::placeholder{color:var(--muted)}
.pldesc[hidden]{display:none}

/* overflow menu — native <details>, no dependency */
.menu{position:relative;display:inline-block}
.menu>summary{list-style:none;cursor:pointer}
.menu>summary::-webkit-details-marker{display:none}
.pop{position:absolute;right:0;top:1.9rem;z-index:25;min-width:13rem;padding:.25rem;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:0 6px 20px rgba(0,0,0,.12);display:grid;gap:.05rem}
.pop button{display:block;width:100%;text-align:left;background:none;border:0;
  padding:.32rem .5rem;border-radius:5px;color:var(--fg);font-size:.85rem}
.pop button:hover{background:var(--panel2)}
.pop button.bad{color:var(--bad)}
.pop hr{border:0;border-top:1px solid var(--line2);margin:.2rem 0}
.pop .note{color:var(--muted);font-size:.75rem;padding:.2rem .5rem;line-height:1.35}
/* An <a> in a popover has to read as one of the menu rows around it — the
   .pop button rule above can't reach it, and an unstyled link sits at a
   different size, color and indent than every row it's stacked with. */
.pop .menulink{display:block;padding:.32rem .5rem;border-radius:5px;
  color:var(--fg);font-size:.85rem;text-decoration:none}
.pop .menulink:hover{background:var(--panel2)}

/* ── practice ───────────────────────────────────────────────────────── */
.psess{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);
  padding:.55rem .7rem;margin-bottom:.9rem}
.psess .thead{display:flex;align-items:center;gap:.45rem;flex-wrap:wrap;
  margin-bottom:.1rem}
.psess .thead h2{margin:0;font-size:1rem;font-weight:650;letter-spacing:-.01em}
.backdatebar .backdate{flex:none}
.backdatebar{margin:.4rem 0;align-items:center;flex-wrap:wrap}
.rchips{display:flex;flex-wrap:wrap;gap:.3rem;align-items:center}
/* Sort and rating filter share one row when it fits; the rating group
   wraps as a unit, never splitting 1 from 2-5. */
.porder .rchips{flex-wrap:nowrap;margin-left:.5rem}
.plfilter{margin:.1rem 0 .45rem}
.plfilter .rchips{flex-wrap:nowrap}
/* A rule, not a widget. It answers "am I nearly done" without reading a number. */
.pbarwrap{height:3px;border-radius:3px;background:var(--line2);margin:.45rem 0 .1rem;
  overflow:hidden}
.pbarfill{height:100%;background:var(--p4fg);transition:width .2s;min-width:0}
.empty{padding:.6rem .1rem;color:var(--muted);line-height:1.45}

/* A Practice row is three children — actions, title, metadata — placed by grid,
   so one piece of DOM is a single line on a desktop and a two-line card on a
   phone. It used to be a flex row of ten items with min-widths and nothing
   allowed to shrink, which is exactly why this screen laid out 914px wide on a
   390px phone. */
.plitem.prow{display:grid;align-items:center;gap:.15rem .6rem;
  grid-template-columns:auto minmax(0,1fr) auto auto;
  grid-template-areas:"acts body meta when";padding:.3rem .1rem}
.prow .chev{display:none}
.prow .pacts{grid-area:acts;display:flex;gap:.3rem;flex:none}
.prow .pbody{grid-area:body;display:flex;align-items:center;gap:.4rem;min-width:0}
.prow .pbody .t{font-weight:650}
.prow .pmeta{grid-area:meta;display:flex;align-items:center;gap:.3rem;
  justify-content:flex-end;min-width:0}
.prow .why{flex:none;width:3.4rem;text-align:center}
.prow .pmeta .pfocus{padding:.1rem .4rem;font-size:.85rem;line-height:1}
.chip.pfocus.on{background:none;color:var(--accent);border-color:var(--accent)}
.prow .when{grid-area:when;color:var(--muted);font-size:.78rem;
  min-width:4.2rem;text-align:right;display:flex;gap:.3rem;
  align-items:center;justify-content:flex-end;white-space:nowrap}
/* How many times this week, on the list you built to work at this week. A
   tally, not a date — "yesterday" on every row of a weekly list says nothing
   about which song you have actually put the hours into. */
.tally{background:var(--p4bg);color:var(--p4fg);font-weight:650;font-size:.72rem;
  border-radius:999px;padding:.02rem .34rem;font-variant-numeric:tabular-nums}
.agotxt{font-variant-numeric:tabular-nums}
/* The two controls this screen exists for. Sized and coloured as buttons, not
   as indicators, and wide enough that "Played" growing a checkmark does not
   shove the title sideways. */
.prow .tick{min-width:4.9rem;text-align:center;padding:.24rem .5rem;
  color:var(--fg);border-color:var(--line)}
.prow .tick:hover{border-color:var(--accent);color:var(--accent)}
.plitem.done{opacity:.55}
.plitem.done .t{text-decoration:line-through}
.plitem.done .tick{background:var(--p4bg);border-color:var(--p4bg);
  color:var(--p4fg);font-weight:650}
.plitem.drag{cursor:grab}
.plitem.dragging{opacity:.4}
.plitem.over{border-top:2px solid var(--accent)}
.grip{color:var(--muted);cursor:grab;user-select:none;padding:0 .15rem;touch-action:none}
#pl.norail #plsplit{grid-template-columns:0 1fr}
#pl.norail .plmaster{overflow:hidden;padding:0;border-right:0;visibility:hidden}
.pcard{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);
  padding:.35rem .7rem;margin-bottom:.5rem}
.pcard>summary{padding:.35rem .1rem}
.linkish{background:none;border:0;padding:0;text-align:left;color:var(--fg);
  font:inherit;cursor:pointer}
.linkish:hover{color:var(--accent);text-decoration:underline}
.readchip{padding:.1rem .4rem;font-size:.85rem;line-height:1}
/* Still a real, clickable button — tapping it offers to add a chart — so
   this is a dim look, not :disabled's inert one. */
.readchip.empty{opacity:.4}
.foot{display:flex;gap:.6rem;align-items:center;color:var(--muted);
  font-size:.8rem;padding-top:.45rem;border-top:1px solid var(--line2);margin-top:.4rem}

/* ── the action sheet ────────────────────────────────────────────────────
   Bottom-anchored, because it is a phone control and the thumb is already
   down there. Above the tab bar (45) and below the toast (60), so confirming
   an action is still visible while it is open. */
.sheetback{position:fixed;inset:0;z-index:55;background:rgba(10,12,18,.45);
  display:flex;align-items:flex-end;justify-content:center;
  animation:sheetfade .14s ease-out}
.aiconsentback{z-index:57}   /* above an open sheet: an AI feature inside one asks from on top */
.aiconsent p{line-height:1.45}
.sheet{width:100%;max-width:34rem;max-height:86vh;overflow-y:auto;
  background:var(--panel);border:1px solid var(--line);border-bottom:0;
  border-radius:14px 14px 0 0;padding:.9rem 1rem calc(1rem + var(--safeb));
  box-shadow:0 -8px 40px rgba(0,0,0,.28);animation:sheetup .16s ease-out}
/* Organize's task list/card is roomier content than the action lists .sheet
   was sized for — a wider cap on desktop and iPad, still 100% width (same as
   .sheet) on a phone. */
.sheet.wide{max-width:46rem}
.sheethead{position:sticky;top:-.9rem;margin:-.9rem -1rem .2rem;
  padding:.9rem 2.6rem .6rem 1rem;background:var(--panel);z-index:1;
  border-bottom:1px solid var(--line2)}
.sheethead h3{margin:0;font-size:1.12rem;font-weight:650;letter-spacing:-.01em;
  line-height:1.25}
.sheetdismiss{position:absolute;top:.6rem;right:.6rem;width:2.2rem;height:2.2rem;
  border-radius:50%;border:1px solid var(--line);background:var(--panel2);
  color:var(--fg);font-size:1.3rem;line-height:1;display:flex;
  align-items:center;justify-content:center}
.sheetsub{font-size:.86rem;margin-top:.2rem;line-height:1.4}
.sheetrow{display:flex;flex-wrap:wrap;gap:.5rem;padding:.55rem 0;
  border-bottom:1px solid var(--line2)}
.sheetrow:empty{display:none}
.sheet .sbtn{flex:1;min-width:8rem;min-height:3rem;border-radius:var(--r);
  border:1px solid var(--line);background:var(--panel);color:var(--fg);
  font-size:1rem;font-weight:550}
.sheet .sbtn.primary{background:var(--accent);border-color:var(--accent);
  color:var(--accent-fg);font-weight:650}
.sheet .sbtn.primary.done{background:var(--p4bg);border-color:var(--p4bg);
  color:var(--p4fg)}
.sheetprof{display:flex;flex-wrap:wrap;gap:.3rem;padding:.5rem 0 .1rem}
.sheetprof .profs{flex-wrap:wrap}
.sheetprof .pb{font-size:.85rem;padding:.2rem .45rem}
/* playedCounter (app.js) — a −/+ row once a song's played today, replacing
   a single "✓ Played today" button that had nothing left to do besides log
   a silent duplicate; − undoes the most recent one instead. */
.playedcounter{display:flex;align-items:center;gap:.5rem;flex:1 1 13rem;min-width:13rem}
.songplayrow{align-items:center}
.songplayrow>.sheetfocus{flex:none;min-height:2.8rem}
.songquickrow{align-items:center}
.songquickrow .chip{min-height:2.4rem;padding:.3rem .8rem;font-size:.9rem}
.songquickrow .songeditbutton{margin-left:auto}
.playedcounter .counterbtn{flex:none;min-width:2.8rem;min-height:2.8rem;
  font-size:1.2rem;font-weight:700;padding:0}
.playedcounter .counterval{flex:1;text-align:center;font-weight:650;
  color:var(--p4fg);white-space:nowrap}
.sheet .linkbar{display:flex;flex-wrap:wrap;gap:.4rem}
/* A search fallback is a real offer here, not a hint in the corner of a row. */
.sheet .lnk.ghost{opacity:.7}
.sheet .lnk{border:1px solid var(--line);border-radius:var(--r);
  padding:.5rem .7rem;font-size:.95rem;min-height:2.6rem;display:flex;
  align-items:center}
.sheetclose{width:100%;margin-top:.7rem;min-height:2.8rem;border-radius:var(--r);
  font-size:.95rem}
/* Band sync (routes_share.py) — the owner's Share sheet, openShareSheet(). */
.sharelinkbox{flex:1;min-width:0;border:1px solid var(--line);border-radius:var(--r);
  padding:.5rem .6rem;font-size:.8rem;background:var(--panel2);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace}
.bandsetupstep{padding:.85rem 0;border-bottom:1px solid var(--line2)}
.bandsetuphead{display:flex;align-items:center;gap:.55rem;margin-bottom:.45rem}
.bandsetupnum{display:grid;place-items:center;width:1.65rem;height:1.65rem;border-radius:999px;
  background:var(--accent);color:var(--accent-fg);font-size:.78rem;font-weight:750;flex:none}
.bandsetuptitle{font-size:.96rem;font-weight:700;flex:1}
.bandsetupstep>p{margin:.2rem 0 .65rem;line-height:1.45}
.bandsetupstep>.sharelinkbox{padding:.65rem .7rem}
.bandsetupstep .sheetrow:last-child{border-bottom:0}
.bandrefresh{margin-left:auto}
.banddevice>div:first-child{flex:1;min-width:10rem}
.bandpresence{font-size:.72rem;margin-top:.18rem;color:var(--muted)}
.bandpresence.online{color:var(--p4fg);font-weight:650}
.bandpresence.online::before{content:"";display:inline-block;width:.45rem;height:.45rem;border-radius:50%;
  background:currentColor;margin-right:.35rem;vertical-align:.04rem}
.bandempty{padding:.35rem 0 .55rem}
.sheet .bandstart{width:100%}
.bandlinksettings{padding:.8rem 0 .2rem;color:var(--muted);font-size:.82rem}
.bandlinksettings summary{cursor:pointer;font-weight:650;margin-bottom:.6rem}
.bandlinksettings .chip.bad{color:var(--bad);border-color:var(--bad)}
.sheet .bandviewlinkbtn{margin-top:.8rem}
.sharechartstoggle{display:flex;gap:.6rem;align-items:flex-start;margin-top:.7rem;cursor:pointer;
  font-size:.88rem;line-height:1.35}
.sharechartstoggle input{width:1.15rem;height:1.15rem;margin-top:.1rem;flex:none}
.sharechartstoggle .dim{font-size:.76rem}
@keyframes sheetup{from{transform:translateY(14%)}to{transform:none}}
@keyframes sheetfade{from{opacity:0}to{opacity:1}}
/* Locks the page behind a sheet on the root only: with body (height:100%)
   hidden too, body stopped overflowing the viewport and the page's scroll
   position clamped to 0 — every sheet open/close jumped the list to the top. */
html.sheeting,html.filtering{overflow:hidden}
html.sheeting body,html.filtering body{overflow:visible}

/* ── smart-playlist filter wizard (inside a .sheet) ─────────────────────── */
.wizbody{display:flex;flex-direction:column;gap:.5rem}
.wizbody .wizcount{font-weight:650;color:var(--fg);font-size:.95rem}
.wizbody input.search{width:100%;box-sizing:border-box}

/* ── AI assist sheet (inside a .sheet) ───────────────────────────────────── */
.assistbody{display:flex;flex-direction:column;gap:.5rem}
.assistbody textarea.notes{min-height:4rem}
.assistbody ol{margin:0;padding-left:1.4rem;display:flex;flex-direction:column;gap:.3rem}
.assistbody ol li{line-height:1.3}

#toast{position:fixed;bottom:calc(1rem + var(--navh) + var(--safeb));left:50%;
  transform:translateX(-50%);background:var(--fg);color:var(--bg);
  padding:.4rem .9rem;border-radius:999px;opacity:0;transition:opacity .18s;
  pointer-events:none;z-index:60;font-size:.85rem;max-width:90vw;text-align:center}
#toast.show{opacity:.94;pointer-events:auto;cursor:pointer}
/* The reader's own FAB sits at the same bottom-right corner #toast's centered,
   90vw-wide bubble can reach into — clear it explicitly rather than relying
   on z-index alone (a longer, tap-to-dismiss error toast sitting half over
   the FAB read as broken, not just visually crowded). html.reading, not
   #reader itself, since #reader stays in the DOM (display:none) off-screen. */
html.reading #toast{bottom:calc(5.5rem + var(--safeb))}

/* ── Goals: two weekly targets, streaks, month calendar ──────────────── */
.ghead{display:flex;flex-direction:column;gap:.3rem;margin-bottom:.8rem}
.ghead h2{margin:0 0 .1rem;font-size:1rem;font-weight:650;letter-spacing:-.01em}
.goalrow{display:flex;align-items:center;gap:.4rem}
.goalnum{font-weight:650;min-width:1.1rem;text-align:center}
/* This week: one card, a row per track (pips, streak, target). */
.gstats{display:grid;grid-template-columns:1fr 1fr;gap:.2rem 1.5rem;margin:0 0 .8rem;padding:.85rem 1rem .4rem;
  border:1px solid var(--line);border-radius:12px;background:var(--panel)}
.gstatstitle{grid-column:1/-1;margin:0 0 .3rem;font-size:1rem;font-weight:650}
.gcol{min-width:0}
.gtracktop{display:flex;align-items:baseline;justify-content:space-between;gap:.5rem}
.gcol h3{margin:0;font-size:.9rem;font-weight:600;color:var(--fg)}
.gstat b{font-size:1.1rem;font-variant-numeric:tabular-nums}
.gstat span{font-size:.8rem;color:var(--muted)}
.gcol.met .gstat b{color:var(--p4fg)}
.gpips{display:flex;gap:3px;margin:.4rem 0 .1rem}
.gpips span{flex:1;height:.5rem;border-radius:99px;background:var(--line)}
.gcol.prac .gpips .on{background:var(--ghit)}
.gcol.rec .gpips .on{background:var(--grec)}
.gtrackfoot{display:flex;align-items:center;justify-content:space-between;gap:.5rem;flex-wrap:wrap;font-size:.8rem}
.gstreak{color:var(--muted)}
.gstreak.on{color:var(--fg);font-weight:600}
.gtrackfoot .gtargetedit{text-align:right}
.gtrackfoot .gtargetedit[open]{flex-basis:100%;text-align:left}
.gtrackfoot .gtargetedit summary{font-size:.8rem;min-height:36px}
.gcalwrap{margin-bottom:1rem}
.gcalhead{display:flex;align-items:center;justify-content:center;gap:.6rem;
  margin-bottom:.35rem}
.gmonth{font-weight:600;min-width:9rem;text-align:center}
.gcal{display:grid;grid-template-columns:repeat(7,1fr);gap:.22rem}
.gdow{text-align:center;font-size:.72rem;color:var(--muted);padding:.15rem 0}
.gday{position:relative;aspect-ratio:1;display:flex;flex-direction:column;
  align-items:center;justify-content:center;border-radius:6px;
  font-variant-numeric:tabular-nums}
.gday .gn{font-size:.82rem}
.gday .gc{font-size:.62rem;line-height:1;opacity:.9}
.gday.out{opacity:.35}
.gday.today{box-shadow:inset 0 0 0 2px var(--accent)}
/* Solid, saturated fill for a practiced day — deliberately far from the
   pastel p1–p5 badges elsewhere, so it reads clearly even with reduced colour
   perception. The recording dot is a separate shape, not a second fill
   colour, for the same reason: two hues on one small cell is the thing that
   was hard to read, not just their strength. */
.gday.hit{background:var(--ghit);color:var(--ghitfg);font-weight:700}
.gday.hit .gc{opacity:.85}
.gday.px{background:var(--pxbg);color:var(--pxfg)}
.grecdot{position:absolute;right:3px;bottom:3px;width:.42rem;height:.42rem;
  border-radius:50%;background:var(--grec);box-shadow:0 0 0 1.5px var(--panel)}
.glegend{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;
  margin-top:.5rem;font-size:.76rem;color:var(--muted)}
.gswatch{width:.7rem;height:.7rem;border-radius:3px;background:var(--pxbg);
  display:inline-block}
.gswatch.hit{background:var(--ghit);border-radius:50%}
.gswatch.rec{background:var(--grec);border-radius:50%;width:.5rem;height:.5rem;
  align-self:center}
.gswatch.prac{background:var(--accent)}
.gswatch.songrec{background:var(--gsongrec)}
.gswatch.p1{background:var(--p1fg)}.gswatch.p2{background:var(--p2fg)}.gswatch.p3{background:var(--p3fg)}
.gswatch.p4{background:var(--p4fg)}.gswatch.p5{background:var(--p5fg)}
.ggrowth>svg{display:block;width:100%;max-height:280px;margin-top:.5rem}
.ggrowth .glegend{gap:.25rem .4rem}
.ggrowth .glegend .gswatch:not(:first-child){margin-left:.35rem}
.gsonglegend{margin-top:0}
.login{min-height:100vh;display:grid;place-content:center;gap:1rem;text-align:center;padding:0 1rem}
.login input{padding:.6rem .9rem;border-radius:var(--r);border:1px solid var(--line);
  background:var(--panel);min-width:15rem}
.login button{padding:.6rem 1.2rem;border-radius:var(--r);border:0;
  background:var(--accent);color:var(--accent-fg);font-weight:600}
.login button:disabled{opacity:.6;cursor:wait}
/* 16px+ keeps iOS Safari from zooming the page when a field takes focus. */
.login input{font-size:max(1rem,16px)}
.login form{display:grid;gap:.6rem}
.login .loginicon{width:88px;height:88px;margin:0 auto -.4rem;border-radius:20px}
.login h1{margin:0}
.pwform,.delform{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.5rem}
.dangerzone{margin-top:1rem;border-top:1px solid var(--line2);padding-top:.6rem}
.dangerzone summary{cursor:pointer}
.dangerbody{display:flex;flex-direction:column;gap:.4rem;margin-top:.5rem}
.chip.bad{color:var(--bad);border-color:color-mix(in srgb, var(--bad) 40%, var(--line))}
.pwform input,.delform input{padding:.45rem .7rem;border-radius:var(--r);border:1px solid var(--line);background:var(--panel);color:inherit;min-width:12rem;flex:1}
.login p{max-width:22rem;margin:0 auto}
.login [hidden]{display:none}
.loginpitch .logintag{font-size:1.1rem;font-weight:600;margin:-.4rem auto .5rem}
.loginpitch ul{list-style:none;margin:0 auto;padding:0;width:fit-content;max-width:20rem;display:grid;gap:.35rem;color:var(--muted);font-size:.95rem;text-align:left}
.loginpitch li{padding-left:1.2rem;text-indent:-1.2rem}
.loginpitch li::before{content:"♪";color:var(--accent);display:inline-block;width:1.2rem;text-indent:0}
.login .loginnew{display:grid;gap:.4rem;margin-top:.4rem}
.login .loginsignup{display:block;padding:.6rem 1.2rem;border-radius:var(--r);border:1px solid var(--accent);
  color:var(--accent);font-weight:600;text-decoration:none}
.loginpitch .loginstart{display:grid;gap:.4rem;margin-top:1.1rem;font-size:.9rem}
.login .loginprimary{background:var(--accent);color:var(--accent-fg);font-size:1rem}
.loginpitch .loginor{margin-top:.6rem;font-size:.9rem}
.login .loginnew[hidden]{display:none}
.loginfresh #go{background:none;border:1px solid var(--accent);color:var(--accent)}

/* ── iPad-width Practice: Played/Skip stay one tap away, everything else
   (key, badges, links, READY/REC/WEEK pills) moves behind the row tap.
   1194px matches handheld() in app.js — the same "this is a handheld device,
   not a desktop" line the app already draws for the default landing tab. Below 700px the
   narrower breakpoint further down takes over and drops the acts column
   too, so Played only lives in the sheet — this tier is deliberately less
   aggressive, because on an iPad there's room to keep the one control this
   whole screen is for right on the row. */
@media (max-width:1194px){
  .plitem.prow{grid-template-columns:auto minmax(0,1fr) auto auto;
    grid-template-areas:"acts body when chev"}
  .prow .pmeta{display:none}
  .prow.pfocused .songtitle::before{content:"★ ";color:var(--accent)}
  /* .prow .t stays clickable here (unlike below 700px) — reading the chart
     is the one thing the comment above promises stays "right on the row" at
     iPad width, and the title carries that now instead of the readchip. */
  .prow .chev{display:block;color:var(--muted);font-size:1.15rem;line-height:1;opacity:.5}
  .plitem.prow{cursor:pointer;-webkit-tap-highlight-color:transparent}
  .plitem.prow:active{background:var(--panel2)}
}

/* ── phone: tabs go to the bottom, rows become cards ─────────────────── */
@media (max-width:860px){
  /* A phone should carry the LARGEST type in this app and was carrying the
     smallest. The root was 14px, so every rem here is 14px and the derived
     scale — built for a dense desktop table — landed between 8.8px and 11.5px
     on the smallest screen in the house.

     Two moves. The root goes up, which scales type, padding and tap targets
     together. Then the sub-1rem classes are pulled up toward it, because a
     .63rem badge is still under 11px after that; the type scale wants to be
     compressed near the top here, not spread from .62rem to 1rem. Nothing on a
     phone should render below about 13px. */
  :root{--navh:52px;--safeb:env(safe-area-inset-bottom,0px);--root:17px}
  header{gap:.45rem;padding:.2rem .6rem}
  header h1{font-size:.92rem}
  /* The drawer/search/back/add icons had no floor and rendered well
     under the 44px a thumb actually needs. Deliberately NOT #readerqa's own
     icons here — up to five of them share the header row with the title on
     a 320-390px phone, and forcing 44px on each one squeezed the song title
     down to a sliver instead (tried it, reverted — see the reader's own
     persistent-controls-pattern notes on why that row stays compact). Not
     #theme (removed from the header entirely — it lived on every page,
     which is part of what pushed the header onto two lines on a narrow
     phone; the toggle now lives in Settings only, see renderSettings())
     and not #readerback either: `display:inline-flex` here used to apply
     unconditionally, overriding its base `display:none` (further up this
     file) regardless of html.reading, so a dead "‹" (no onclick outside
     the reader — see mountReaderHeader() in app.js) sat in the Catalog
     header doing nothing when tapped, and was the other half of the wrap. */
  #drawer,#searchtoggle,#addbtn{min-width:44px;min-height:44px;
    display:inline-flex;align-items:center;justify-content:center}
  /* #readerback is display:none outside html.reading (base rule above) —
     the touch-target floor only applies while it's actually shown. */
  html.reading #readerback,html.insettings #readerback{min-width:44px;min-height:44px}
  #addbtn .albl{display:none}
  #addbtn{padding:.3rem .6rem;font-size:1.05rem;line-height:1}
  #count{font-size:.76rem}

  /* Thumb reach. The tab bar is the one piece of chrome that is always here. */
  nav#tabs{position:fixed;left:0;right:0;bottom:0;top:auto;z-index:45;
    height:auto;padding:0 0 var(--safeb);gap:0;
    border-bottom:0;border-top:1px solid var(--line);
    box-shadow:0 -2px 14px rgba(0,0,0,.09)}
  nav#tabs .tab{flex:1;min-width:0;height:var(--navh);padding:0;font-size:.78rem;
    justify-content:center;
    border-bottom:0;border-top:2px solid transparent;overflow:hidden;
    text-overflow:ellipsis}
  nav#tabs .tab.on{border-top-color:var(--accent);background:var(--panel2);
    font-weight:700}

  /* Let the DOCUMENT scroll rather than a box inside it: an inner scroller on
     iOS fights the address bar, kills momentum at the edges, and made the
     sticky header double up. Panes get bottom padding to clear the tab bar. */
  /* `.shell.wide` (0 1fr) has to be beaten explicitly here, not merely
     overridden by source order: it is the more specific selector, so it wins
     inside this media query too. The aside is position:fixed on a phone and
     therefore out of flow, which makes #main the FIRST grid item — so it lands
     in that 0 column and Playlists, Practice and Settings all render as a
     blank screen. Third variant of the same bug; hence the test. */
  .shell,.shell.wide{grid-template-columns:1fr;height:auto}
  #main{overflow:visible;padding:0 0 calc(var(--navh) + var(--safeb) + 1.5rem)}
  aside{position:fixed;inset:var(--headh) 22% calc(var(--navh) + var(--safeb)) 0;
    z-index:40;visibility:hidden;transform:translateX(-100%);transition:transform .18s;
    box-shadow:0 0 30px rgba(0,0,0,.25);padding:.6rem .55rem 3rem;border-right:0}
  .shell.drawer aside{visibility:visible;transform:none}
  #filterbackdrop{display:none;position:fixed;inset:var(--headh) 0 calc(var(--navh) + var(--safeb));
    z-index:39;border:0;background:rgba(10,12,18,.45);padding:0}
  .shell.drawer #filterbackdrop{display:block}
  .filterheading{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
    position:sticky;top:-.6rem;background:var(--panel);z-index:2;padding:.5rem 0 .75rem;
    margin:-.6rem -.55rem .3rem}
  .filterheading h2{font-size:1.15rem;margin:0}
  #side .chip,#side .clear{min-height:2.75rem}
  .plmaster>.plbar .mobilelisttools{display:inline-flex;align-items:center;min-height:2.75rem}
  .plmaster>.plbar .listsecondary{display:none}
  .sheetdismiss{width:2.6rem;height:2.6rem}
  .sheet .chip,.sheet .search{min-height:2.6rem}
  #catalog .mobilelinklabel{display:inline;margin-left:.3rem;font-size:.85rem}
  #catalog .cardline .lnk{padding:0 .45rem;border:1px solid var(--line);border-radius:8px}
  #catalog .cardline .lnk.ghost{opacity:.75}
  html.reading .readerpophead{display:flex;align-items:center;justify-content:space-between;
    gap:.6rem;position:sticky;top:0;background:var(--panel);z-index:2;
    padding:.5rem;margin:-.25rem -.25rem .3rem;border-bottom:1px solid var(--line)}
  html.reading .readerpophead strong{font-size:.95rem}
  .bulk{left:0;right:0;transform:none;width:auto;max-width:none;bottom:calc(var(--navh) + var(--safeb));
    border-radius:14px 14px 0 0;border-width:1px 0 0;padding:.5rem .6rem;gap:.45rem;
    grid-template-columns:minmax(0,1fr) auto}
  .bulk.show{display:grid}
  .bulkhead{border-right:0;padding-right:0}
  .bulkhead .clear{display:inline}
  .bulkacts{grid-column:1/-1;grid-row:2;min-width:0;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    margin:0 -.6rem;padding:0 .6rem}
  .bulkacts::-webkit-scrollbar{display:none}
  .bulksel,.bulkbtn,.bulkdel{min-height:2.5rem;flex:none}
  .bulkdel{flex:none}
  html.hassel #catalog{padding-bottom:4.5rem}
  #catalog #rows tr.sel{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent);
    background:color-mix(in srgb,var(--accent) 8%,var(--panel))}
  #catalog #rows tr.sel td:first-child{box-shadow:none}

  /* 16px is a hard floor, or iOS zooms the whole viewport the moment a field
     takes focus and leaves you scrolled somewhere arbitrary. Above that it
     follows the text-size setting like everything else. */
  input,select,textarea{font-size:max(16px, 1rem)}
  .search{padding:.5rem .6rem}

  header h1{font-size:1rem}
  #count{font-size:.86rem}
  nav#tabs .tab{font-size:.85rem}
  .chip{font-size:.86rem}
  .clear{font-size:.9rem}
  .pb{font-size:.82rem;padding:.1rem .34rem}
  .lnk{font-size:1rem}
  .tagpill{font-size:.8rem}
  .tagpill.orig{font-size:.72rem}
  .sec>summary{font-size:.8rem}
  .foot{font-size:.9rem}
  .erow>.lbl{font-size:.78rem}
  .pname{font-size:.9rem}
  .danger{font-size:.9rem}
  .pop button{font-size:.95rem}
  .plh{font-size:.8rem}
  .plrow{font-size:1rem}
  .plrow .n{font-size:.86rem}
  .plhead h2{font-size:1.15rem}
  .plhead .sub{font-size:.85rem}
  .plitem .hwhen{font-size:.85rem}
  .psess .thead h2{font-size:1.12rem}
  #toast{font-size:.95rem}
  .kin{width:5.6rem}
  .tin{width:100%}

  thead{display:none}
  table,tbody,tr,td{display:block;width:100%}
  tbody tr{padding:.55rem .7rem}
  .catalogsort{display:flex;gap:.5rem;padding:.5rem .7rem}
  .catalogsort select{flex:1;min-width:0;min-height:2.6rem}
  .catalogsort button{min-width:2.6rem;min-height:2.6rem}
  td{padding:.05rem 0;white-space:normal}
  td.hide{display:none}
  /* The song title leads, at a readable size, and the indicators trail it —
     they are laid out in DOM order (indicators first) on a desktop, where the
     comment in the base sheet explains why they ride in front of the title. */
  tbody tr td.t{display:flex;flex-wrap:wrap;align-items:center;gap:.15rem}
  td.t .selbox{order:1;flex:none;width:1.1rem;height:1.1rem;margin-right:.2rem}
  td.t .ttl{order:2;flex:1 1 auto;min-width:0;font-size:1.05rem;font-weight:600;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  td.t .orig{order:3;flex:none}
  td.t .badges{order:4;flex:none;margin-right:0}
  td.t .cardline{order:5;flex-basis:100%}
  td.t .lyrichit{order:5;flex-basis:100%;max-width:none}
  .cardline{display:flex;gap:.45rem;align-items:center;flex-wrap:wrap;
    color:var(--muted);font-size:.9rem;margin-top:.3rem}
  /* The Last played column is hidden here, so the staleness and the tick that
     resets it ride along on the card's second line instead. Logging a play used
     to mean opening the editor, on the device where that happens most. */
  .cardline .playbox{margin-left:auto}
  .cardline .tick{min-height:1.9rem;min-width:2.5rem;text-align:center;
    color:var(--fg)}
  .pgrid{grid-template-columns:1fr}
  .seg button{min-height:2.2rem;flex:1}
  /* Playlists become single-pane push navigation. min-height (not height) so
     a short list — a 3-song setlist, an empty new playlist — still fills
     down to the tab bar with its own panel background, instead of running
     out partway down the screen and exposing the page's gray behind it; a
     long one still grows past the viewport and lets the document scroll.
     Plain vh, not dvh: dvh tracks Safari's address bar live, so it recomputes
     — and reflows this box — on every scroll tick as the bar hides/shows,
     which is the exact jank the rest of this file goes out of its way to
     avoid by letting the document scroll instead of an inner box. */
  #main.split{overflow:visible}
  /* minmax(0,1fr), not a bare 1fr — a bare 1fr track's minimum size is
     "auto" (its content's preferred width), so a wrapping flex toolbar
     inside it (.plbar) got sized to the sum of every button's width as if
     unwrapped, blowing the column out past the viewport instead of actually
     wrapping. 0 is what lets the track (and .plmaster inside it) shrink to
     the real available width, so flex-wrap can do its job. */
  #plsplit{grid-template-columns:minmax(0,1fr);height:auto;overflow:visible}
  .plmaster,.pldetail{
    min-height:calc(100vh - var(--headh) - var(--navh) - var(--safeb))}
  .plmaster{border-right:0;overflow:visible;padding:.5rem .6rem 1rem}
  .pldetail{display:none;padding:.6rem .7rem 1rem;background:var(--panel)}
  #pl.detail .plmaster{display:none}
  #pl.detail .pldetail{display:block}
  .plback{display:inline-block}
  .pltab td.pos{display:none}
  .tnum{display:inline}
  #pr,#set,#org{padding:.6rem .7rem;max-width:none}
  .psess{padding:.6rem .65rem}

  /* Fat enough to hit with a thumb — but these are state, not the reason you
     opened the screen, so they grow their hit area without growing their type.
     At font-size:.95rem they were the loudest thing in every row. */
  .bdg{padding:.34rem .32rem;font-size:.76rem}
  .badges{gap:.34rem}
  .lnk{padding:.3rem .34rem}
  .plitem{padding:.45rem .1rem}

  .prow .tick{min-height:2.3rem}
  .prow .badges{margin-right:0}
}

/* ── the narrow phone ────────────────────────────────────────────────────
   860px does two different jobs and only one of them belongs on an iPad: at
   820px the table still has to become cards, but the fat-thumb sizing and the
   two-line Practice card just waste a screen that has room for the one-line
   row. So the layout switch gets its own, narrower breakpoint. */
@media (max-width:700px){
  .plitem.prow{grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"body chev";gap:0 .5rem;padding:.7rem .1rem;
    cursor:pointer;-webkit-tap-highlight-color:transparent}
  .plitem.prow:active{background:var(--panel2)}
  .prow .pacts,.prow .pmeta,.prow .when,.prow .pbody > :not(.t){display:none}
  .prow .pbody .t{display:block;width:100%;font-size:1.05rem;
    white-space:normal;overflow-wrap:anywhere;line-height:1.3}
  .prow .songtitle{display:block}
  .prow .songartist{display:block;margin-top:.18rem;font-size:.88rem;font-weight:400}
  .prow .artistsep{display:none}
  .plitem.prow.done{opacity:1}
  .plitem.prow.done .t{color:var(--muted);text-decoration:none}
  .plitem.prow.done .songtitle{text-decoration:line-through}
  .prow .chev{display:block;grid-area:chev;color:var(--muted);
    font-size:1.25rem;line-height:1;opacity:.5}

}
/* The mobile summary line must never render on desktop — it duplicates the
   Artist / Proficiency / Links columns and doubles every row's height. */
@media (min-width:861px){ .cardline,.cardtick,.recmark{display:none} }

/* Musical outcomes live above the existing practice history. */
.goalprojects{margin-bottom:2rem}
.goalprojects h2{margin:0;font-size:1.3rem;letter-spacing:-.02em}
.goalprojects h3{margin:0;font-size:1.05rem;overflow-wrap:anywhere}
.goalprojects p{margin:.4rem 0;line-height:1.5}
.goalcardhead{display:flex;align-items:flex-start;justify-content:space-between;gap:.8rem}
.goalcardhead>div{min-width:0;flex:1}
.goalcardhead>button{flex-shrink:0}
.goalfilters{display:flex;flex-wrap:wrap;align-items:end;gap:.6rem;margin:1rem 0}
.goalfield{display:flex;flex-direction:column;gap:.3rem;min-width:0;font-size:.85rem}
.goalfield .search{width:100%;max-width:100%;box-sizing:border-box;font-size:1rem;min-height:2.6rem}
.goalfilters .goalfield{flex:1;min-width:8rem}
.goalform{display:flex;flex-direction:column;gap:.85rem;padding:.6rem 0}
.goalcheck{display:flex;align-items:center;gap:.5rem}
.goalcheck input,.goalmilestonelabel input{width:1.25rem;height:1.25rem;flex-shrink:0;accent-color:var(--accent)}
.goalcontextedit{display:flex;flex-wrap:wrap;gap:.5rem;align-items:end;padding:.7rem 0;border-bottom:1px solid var(--line)}
.goalcontextedit .goalfield{flex:1;min-width:10rem}
.goalcontextheading{margin:1.2rem 0 .5rem!important;color:var(--muted)}
.musicalgoal{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);padding:1rem;margin-bottom:.8rem;min-width:0}
.musicalgoal.focused{border-color:var(--accent)}
.goalkicker{display:block;font-size:.75rem;font-weight:650;color:var(--accent);margin-bottom:.25rem}
.goalnotes{white-space:pre-wrap;overflow-wrap:anywhere}
.goalprogress{display:block;width:100%;height:.55rem;margin:.9rem 0 .4rem;accent-color:var(--accent);appearance:none;border:0;border-radius:99px;overflow:hidden;background:var(--line)}
.goalprogress::-webkit-progress-bar{background:var(--line);border-radius:99px}
.goalprogress::-webkit-progress-value{background:var(--accent);border-radius:99px}
.goalprogress::-moz-progress-bar{background:var(--accent);border-radius:99px}
.goalmeter{position:relative}
.goalmeter>span{position:absolute;top:0;bottom:0;left:0;border-radius:99px}
.goalmeterdone{background:var(--accent)}
.goalmeterpart{background:color-mix(in srgb,var(--accent) 38%,var(--line))}
.goalnext{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;background:var(--bg);border-radius:var(--r);padding:.8rem;margin:.85rem 0;overflow-wrap:anywhere}
.goalmilestones{list-style:none;margin:.5rem 0;padding:0}
.goalmilestones li{display:flex;align-items:center;gap:.5rem;padding:.55rem 0;border-bottom:1px solid var(--line2)}
.goalmilestonebody{flex:1;min-width:0}
.goalmilestonebody small{display:block;margin-left:1.85rem}
.goalmilestonelabel{display:flex;align-items:center;gap:.6rem;min-height:2.75rem;cursor:pointer;overflow-wrap:anywhere}
.goalmilestones .complete .goalmilestonelabel span{text-decoration:line-through;color:var(--muted)}
.goaldonemilestones{margin:.25rem 0 .5rem}
.goaldonemilestones>summary{cursor:pointer;color:var(--muted);font-size:.9rem;font-weight:600;padding:.4rem 0;min-height:32px}
.goalactions{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.7rem}
.goalactions button{white-space:normal;overflow-wrap:anywhere;text-align:left}
.goalempty{padding:1rem;border:1px dashed var(--line);border-radius:var(--r)}
.goalerror{color:var(--fg);font-weight:600}
.planlimitbuy{display:block;margin-top:8px}
.iapwrap{display:inline-flex;flex-wrap:wrap;align-items:center;gap:6px}
.iapwrap .iapnote:empty{display:none}
.iapdisclosure{font-size:12px;line-height:1.4;margin:4px 0 2px;flex-basis:100%}
.applesub .chip{margin-top:6px}
.billingactions>div{flex-basis:100%}
.iapblock{display:flex;flex-direction:column;align-items:flex-start;gap:6px;padding:10px 0}
.iapblock+.iapblock,.iaprestore{border-top:1px solid var(--line);padding-top:10px}
.iaphead{display:flex;gap:8px;align-items:baseline}
.iapowned{font-weight:600}
.iaplocked>:not(.iapdisclosure){opacity:.55}
.legallinks{font-size:12px;margin-top:10px}
.importskipped td.t,.importskipped td.a{text-decoration:line-through;opacity:.55}
@media(max-width:699px){
  .goalprojects .chip{min-height:2.75rem}
  .musicalgoal{padding:.75rem}
  .goalcardhead{flex-wrap:wrap}
  .goalprojects h2{font-size:1.2rem}
}

.goalfield[hidden]{display:none}
.goaltrackinghint{font-size:.85rem;line-height:1.45;margin:0}
.goalautostate{width:1.25rem;flex-shrink:0;text-align:center;color:var(--accent);font-weight:700}
.goalautoprogress{overflow-wrap:anywhere}
.goalbyhand{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.goalstepdate{display:inline-flex;align-items:center;gap:.25rem;font-size:.85rem}
.goalstepdate input{font:inherit;padding:.1rem .3rem}
.plpickerin{margin-top:.4rem;font-size:.85rem}
.goalmilestonebody .goalprogress{margin:.5rem 0;height:.4rem}

/* Shared surfaces follow the musical-goal cards. */
#catalog{background:var(--panel)}
#catalog td{padding-top:.42rem;padding-bottom:.42rem}
aside .sec{padding:.65rem 0}
:where(button,input,select,textarea,summary):focus-visible{
  outline:2px solid var(--accent);outline-offset:3px}
.pageheading{margin:.2rem 0 1rem;font-size:1.3rem;letter-spacing:-.02em}
#pr .psess,#pr .pcard,#set .pcard{border-radius:12px}
#pr .psess{padding:1rem}
.psess .thead{gap:.6rem;margin-bottom:.6rem}
.psess .thead h2{font-size:1.3rem;letter-spacing:-.02em}
.psess .thead .chip{padding:.35rem .65rem}
#pr .pcard{margin-bottom:.75rem}
#pr .pcard>summary{padding:.6rem .15rem;text-transform:none;
  letter-spacing:0;font-size:.95rem;color:var(--fg)}
.plmaster{padding:.75rem .65rem 3rem}
.plmaster>.plbar{gap:.5rem;margin-bottom:.85rem}
.plmaster>.plbar .search{min-height:2.6rem}
.plmaster .sec{padding:.65rem 0}
.plmaster .sec>summary{margin-bottom:.3rem}
.plrow{padding:.5rem .6rem;border-radius:8px}
.plrow.on{background:var(--rndbg);color:var(--rndfg)}
.plrow .n{border-radius:999px;background:var(--panel2);padding:.05rem .4rem;opacity:1;color:var(--muted)}
.plhead{padding-bottom:.85rem;margin-bottom:.85rem;gap:.65rem}
.plhead h2{font-size:1.3rem;letter-spacing:-.02em;overflow-wrap:anywhere;min-width:0}
.plhead .sub{line-height:1.5}
.plstats{gap:.65rem}
.plstat{padding:.7rem .55rem;border-radius:10px;background:var(--panel)}
.plstat b{font-size:1.35rem}
.setrow{padding:.8rem .15rem}
.settingsgroup{max-width:46rem;margin-bottom:1.1rem}
.settingsgrouphead{margin:0 0 .35rem .25rem;font-size:.72rem;font-weight:650;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted)}
#set .settingslist{padding:0;margin:0;overflow:hidden}
.settingscard{padding:.3rem 1rem;border-top:1px solid var(--line2)}
.settingscard:first-child{border-top:0}
.settingscard .dim{line-height:1.5;max-width:55ch}
.settingscard .plbar{gap:.5rem;margin-top:.6rem;margin-bottom:0}
.settingsinline{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;
  gap:.35rem 1rem;min-height:44px;padding-top:.45rem;padding-bottom:.45rem}
.settingsinline .plbar{margin-top:0}
.settingspills{padding:.5rem 0 .1rem}
.ratinglabels>label{display:flex;gap:.3rem;align-items:center}
.ratinglabels .tin{width:7.5rem}
.settingsmore{margin-top:.6rem}
.settingsmore>summary{cursor:pointer;width:fit-content;font-size:.84rem;padding:.2rem 0}
.settingsmore>:not(summary){margin-top:.4rem}
.settingsminor{margin-top:.9rem;font-size:.78rem}
.settingscard .settingsminor+.plbar{margin-top:.35rem}
.settingsfoot{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem 1rem;max-width:46rem;margin:.2rem .25rem 1rem}
.settingsfoot .dim{flex:1;min-width:12rem}
.settingscard .opts{gap:.4rem}
.settingscard .chip{padding:.35rem .65rem}
.settingscard .tagpill{padding:.25rem .45rem}

.ghistory{padding-top:1.25rem;border-top:1px solid var(--line)}
.ghead{margin-bottom:1rem}
.ghead h2{font-size:1.2rem;letter-spacing:-.02em}
.ghead p{margin:.2rem 0;line-height:1.5}
.gcalwrap,.gtrends{border:1px solid var(--line);border-radius:12px;
  padding:1rem;background:var(--panel);min-width:0}
.gcalwrap h3,.gtrends>h3{margin:0 0 .8rem;font-size:1rem;color:var(--fg);font-weight:650}
.goalrow{gap:.35rem;flex-wrap:wrap;font-size:.8rem}
.goalrow>span:first-child{display:none}
.goalrow .chip{width:2rem;height:2rem;padding:0;font-size:1rem}
.goalrow .chip:disabled{opacity:.35;cursor:default}
.ghistorygrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:.8rem;align-items:start}
.gcalwrap{margin:0}
.gcalhead{justify-content:space-between;margin-bottom:.8rem}
.gcalhead .chip{width:2.5rem;height:2.5rem;padding:0;font-size:1.3rem}
.gmonth{min-width:0;font-size:.95rem}
.gcal{gap:.25rem}
.gday{border-radius:8px}
.gday.px{background:var(--bg);color:var(--muted)}
.gday.out{opacity:.35}
.glegend{margin-top:.8rem;gap:.4rem;font-size:.75rem}
.gswatch.hit{border-radius:3px}
@media(max-width:860px){
  #catalog{padding:.65rem;background:var(--bg)}
  #catalog #rows tr{margin-bottom:.6rem;border:1px solid var(--line);
    border-radius:10px;padding:.65rem;background:var(--panel)}
  #catalog #rows tr.open{background:var(--panel2)}
  #catalog td{padding-top:.1rem;padding-bottom:.1rem}
  #catalog .cardline{gap:.4rem;margin-top:.45rem;font-weight:400}
  #pr .psess{padding:.8rem}
  #pldetail{background:var(--bg)}
  #pldetail .plhead{padding:.85rem;border:1px solid var(--line);border-radius:12px;background:var(--panel)}
  #pldetail .pltab tr{background:var(--panel);border:1px solid var(--line);
    border-radius:10px;margin-bottom:.6rem}
  .settingscard .chip{min-height:2.5rem}
}
@media(max-width:700px){
  .ghistorygrid{grid-template-columns:1fr}
  .gstats{grid-template-columns:1fr;gap:.5rem;padding:.85rem .85rem .3rem}
  .gcalwrap,.gtrends{padding:.85rem}
  .goalrow{font-size:.88rem}
  .goalrow .chip{width:2.6rem;height:2.6rem}
  .psess .thead .chip{min-height:2.5rem}
}

/* Reader sheets: a fixed heading and one independently scrolling body. */
.readercommandhead{position:relative;flex:none}
.readercommandbody{min-height:0;overflow-y:auto;overscroll-behavior:contain}
.readerdisclosure{border-top:1px solid var(--line2);padding:.2rem .55rem}
.readerdisclosure>summary{display:flex;align-items:center;gap:.65rem;min-height:48px;
  cursor:pointer;list-style:none;font-weight:600;font-size:.9rem;color:var(--fg)}
.readerdisclosure>summary::-webkit-details-marker{display:none}
.readerdisclosure>summary::after{content:"›";margin-left:auto;font-size:1.35rem;color:var(--muted)}
.readerdisclosure[open]>summary::after{transform:rotate(90deg)}
.readerdisclosure[open]{padding-bottom:.6rem}
/* A section's own bottom rule already separates it from the row below. */
.readercommandsection+.readerdisclosure{border-top:0}
.readerplaypair{display:grid;grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));gap:.5rem}
.readerquickpop .readerplayprimary{background:var(--accent);color:var(--accent-fg);
  border-radius:var(--r);min-height:54px;text-align:center;font-weight:700;font-size:1rem}
.readerquickpop .readerplayprimary:hover{background:var(--accent);filter:brightness(1.08)}
.readercommandsection>.readerplayhint{font-size:.82rem;color:var(--muted);margin:.65rem 0 .75rem;text-align:center}
.readerquickpop .menulink{min-height:44px;display:flex;align-items:center}

/* Compact song actions, with secondary details available on demand. */
.songsheet>.sheetclose,.songeditorsheet>.sheetclose{display:none}
.songdisclosure{border-bottom:1px solid var(--line2)}
.songdisclosure>summary{display:flex;align-items:center;gap:.6rem;min-height:50px;
  list-style:none;cursor:pointer;font-weight:600;font-size:.92rem}
.songdisclosure>summary::-webkit-details-marker{display:none}
.songdisclosure>summary::after{content:"›";margin-left:auto;font-size:1.35rem;color:var(--muted)}
.songdisclosure[open]>summary::after{transform:rotate(90deg)}
.songdisclosure[open]{padding-bottom:.65rem}
.editorfield{display:grid;gap:.3rem;min-width:0;max-width:100%;flex:1}
.editorfield>.dim{font-size:.78rem}
.editorfield>.tin{width:100%!important;min-width:0}
@media(max-width:860px){
  .songeditorsheet .editorhead{gap:.75rem;border:0;padding-bottom:0}
  .songeditorsheet .tin,.songeditorsheet .kin{min-height:44px}
}

.sheet.songsheet,.sheet.songeditorsheet{display:flex;flex-direction:column;overflow:hidden;padding:0;max-height:86dvh}
.songsheet>.sheethead,.songeditorsheet>.sheethead{position:relative;top:auto;margin:0;flex:none;padding-right:3.4rem}
.songmodalbody{min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:.65rem 1rem calc(1rem + var(--safeb))}

.songsheet .playedcounter .sbtn.primary{background:var(--panel2);border-color:var(--line);color:var(--fg)}


.sheet.songsheet:focus,.sheet.songeditorsheet:focus{outline:none}

/* Secondary facts about a list (goal, listening link, why, gig-ready) as one
   wrapping strip of small chips instead of a stack of full-width rows. */
.plmeta{flex-basis:100%;display:flex;flex-wrap:wrap;align-items:center;gap:.35rem .6rem;min-width:0;font-size:.88rem}
.plmeta>.plgoals,.plmeta>.listenrow{display:contents}
.plmeta>.gigready{flex-basis:auto;margin:0}
.plmeta .chip,.plmeta .clear,.plmeta .plgoal{min-height:32px;font-size:.85rem}
.plhead .acts .plfolder{width:auto;max-width:9rem;min-height:0;padding:.25rem .5rem;font-size:.85rem}
.pltools{display:flex;flex-wrap:wrap;align-items:flex-start;gap:.4rem .6rem;margin-bottom:.4rem}
.pltools>.pladd{flex:1 1 16rem;max-width:28rem;min-width:0}
.pltools .pladd>.plbar{margin:0}
.pltools>.chip,.pltools>.plreadytoggle{min-height:36px;align-self:flex-start}
.pltools .pladd input.search{max-width:none!important}
.practiceactions{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap}
@media(min-width:701px){
  .plorganization>summary{display:none}
  .plorganization::details-content{content-visibility:visible}
  .plorganization:not([open])>.plorganizationbody{display:flex}
}
@media(max-width:700px){
  #pldetail .plhead{display:grid;grid-template-columns:minmax(0,1fr) 44px;gap:.55rem .4rem;padding:.85rem}
  #pldetail .plhead>.plback{grid-column:1/-1;justify-self:start;border:0;background:none;padding:0;min-height:32px}
  #pldetail .plhead>h2{font-size:1.35rem;line-height:1.2;align-self:center}
  #pldetail .plhead>.plstar{width:44px;height:44px;align-self:start;font-size:1.45rem}
  #pldetail .plhead>.plstats,#pldetail .plhead>.startrow,#pldetail .plhead>.plgoals,
  #pldetail .plhead>.acts,#pldetail .plhead>.plmeta,#pldetail .plhead>.pldesc{grid-column:1/-1;min-width:0}
  #pldetail .plstats{gap:0;padding:.65rem 0;margin:.15rem 0;border-block:1px solid var(--line2);flex-wrap:wrap;row-gap:.6rem}
  #pldetail .plstat{min-width:0;flex:1 1 25%;padding:0 .35rem;border:0;border-radius:0;background:none}
  #pldetail .plstat+.plstat{border-left:1px solid var(--line2)}
  #pldetail .plstat b{font-size:1.15rem;line-height:1.3}
  #pldetail .plstat span{display:block;font-size:.7rem;line-height:1.4}
  #pldetail .startrow{gap:.5rem;margin:.15rem 0}
  #pldetail .bigstart{min-height:48px;padding:.65rem .4rem;font-size:.88rem;border-radius:10px}
  #pldetail .startrow .bigstart:first-child{flex:1.35}
  #pldetail .plhead>.acts{margin:0;justify-content:flex-start;align-items:center;gap:.4rem}
  #pldetail .plhead>.acts>.chip{min-height:44px;display:flex;align-items:center}
  #pldetail .plhead>.acts>.menu{margin-left:auto}
  #pldetail .plhead>.acts>.menu>summary{min-height:44px;min-width:44px;text-align:center}
  #pldetail .plhead>.acts{flex-wrap:nowrap;gap:.3rem}
  #pldetail .plhead>.acts>.plfolder{min-height:44px;max-width:6.5rem;min-width:0;flex:0 1 auto}
  #pldetail .plhead>.acts>.chip{white-space:nowrap;padding-inline:.5rem}
  .plready{padding:.75rem .85rem;background:var(--panel)}
  .plreadyhead{gap:.4rem;flex-wrap:wrap;margin-bottom:.35rem}
  .plreadytoggle{min-height:32px;font-size:.73rem}
  .plreadybar{height:.5rem}
  .plreadylegend{gap:.35rem .65rem}
  .plreadymeters{gap:.65rem;margin-top:.65rem}
  .plreadymeter{min-width:0;flex-basis:28%}
  .plreadymeterhead{flex-direction:column;gap:.15rem;font-size:.7rem}
  .plreadytrack{height:4px}
  #pr .psess{padding:.85rem}
  .psess .thead{gap:.7rem}
  .psess .prog{margin-left:auto;font-size:.82rem}
  .psess .thead .chip{font-size:.8rem;padding:.35rem .5rem;min-height:44px;border-radius:9px}
  .practiceactions{display:grid;grid-template-columns:1fr 1fr;gap:.4rem}
  .psess .practiceprimary{background:var(--accent);border-color:var(--accent);color:var(--accent-fg);font-weight:650}
  .psess .pbarwrap{margin:.4rem 0 .6rem;height:4px}
  .psess .plitem.prow{padding:.7rem 0}
  .psess .prow .pbody .t{font-size:1rem;line-height:1.3}
  .psess .prow .songartist{font-size:.82rem;margin-top:.2rem}
}

/* Catalog identity stays separate from its compact practice metadata. */
.catalogartist{display:none}
.settingscard>.plh,.settingssummary .plh{margin:0;font-size:1.05rem;font-weight:650;
  color:var(--fg);text-transform:none;letter-spacing:-.01em}
.settingssummary{display:flex;align-items:center;flex-wrap:wrap;gap:.25rem .75rem;
  cursor:pointer;list-style:none;min-height:44px}
.settingssummary::-webkit-details-marker{display:none}
.settingssummary::after{content:"›";font-size:1.25rem;color:var(--muted);margin-left:auto}
.settingsdisclosure[open]>.settingssummary::after{transform:rotate(90deg)}
.settingshint{font-size:.78rem;color:var(--muted);min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.settingsbody{min-width:0}
.settingsbody>.dim{line-height:1.5;max-width:55ch}
.settingsdisclosure[open]>.settingsbody{padding:.1rem 0 .75rem}
@media(max-width:860px){
  #catalog{padding:.65rem .75rem}
  #catalog .catalogsort{padding:0 0 .65rem;gap:.4rem}
  #catalog .catalogsort .search{background:var(--panel);border-radius:9px;font-size:.85rem}
  #catalog .catalogsort button{border-radius:9px;min-width:44px;min-height:44px}
  #catalog #rows tr{padding:.6rem .7rem;margin-bottom:.45rem;border-radius:12px}
  #catalog #rows td.t{display:grid;grid-template-columns:22px minmax(0,1fr) auto auto;
    gap:.15rem .4rem;align-items:center}
  #catalog td.t .selbox{grid-column:1;grid-row:1;margin:.15rem 0 0;width:18px;height:18px;align-self:start}
  #catalog td.t .ttl{grid-column:2;grid-row:1;white-space:normal;overflow-wrap:anywhere;
    font-size:1.05rem;line-height:1.3;font-weight:650}
  #catalog .catalogartist{display:flex;grid-column:2;grid-row:2;min-width:0;gap:.3rem;
    font-size:.82rem;font-weight:400;color:var(--muted);line-height:1.4}
  #catalog .catalogartist .cartist{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #catalog .catalogartist .when{flex:none;white-space:nowrap}
  #catalog .catalogartist .when::before{content:"·";margin-right:.3rem}
  #catalog .catalogartist .when .agotxt::before{content:"played "}
  #catalog .catalogartist .when.never{display:none}
  /* The toggle drawn grey read as a claim it was recorded, and the word was
     the widest thing on the card. The phone card shows only the on state, as
     a borderless tile beside the tick — a bordered record dot read as a
     button that starts recording. The song sheet toggles it. */
  #catalog td.t .badges{display:none}
  #catalog td.t .recmark{grid-column:3;grid-row:1/3;display:flex;align-items:center;justify-content:center;
    width:34px;height:34px;border-radius:8px;background:var(--panel2);font-size:1.15rem;line-height:1}
  #catalog td.t .cardtick{grid-column:4;grid-row:1/3;justify-self:end;
    min-width:44px;min-height:44px;border-radius:10px;font-size:1.05rem;color:var(--fg)}
  #catalog td.t .orig{grid-column:2/-1;justify-self:start}
  #catalog td.t .cardline{grid-column:2/-1;gap:.35rem;margin-top:.3rem;font-size:.82rem;min-width:0}
  #catalog .cardline .profs{flex-wrap:wrap;gap:.25rem}
  #catalog .cardline .pb.px{border:0;color:var(--muted);opacity:.8;padding-left:.15rem;padding-right:.15rem}
  /* A search link on every card is the same three buttons 200 times over; the
     reader and the song sheet still offer the search when there's no link. */
  #catalog .cardline .lnk.ghost{display:none}
  #catalog .cardline .lnk{min-width:36px;min-height:36px;display:inline-flex;align-items:center;justify-content:center}
  #catalog .cardline .mobilelinklabel{display:none}
  #catalog .cardline .practicego{margin-left:auto;min-height:36px;padding:0 .7rem}
  #set>.pageheading{display:none}
  #set .settingscard{padding-left:.9rem;padding-right:.9rem}
  #set .settingsinline{display:block;padding-top:.7rem;padding-bottom:.7rem}
  #set .settingsinline .plbar{margin-top:.5rem}
  #set .settingsfoot{flex-direction:column;align-items:flex-start;gap:.4rem}
  #set .settingssummary{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.15rem .5rem}
  #set .settingssummary .plh{grid-column:1;font-size:.95rem;line-height:1.35}
  #set .settingshint{grid-column:1;grid-row:2;font-size:.78rem}
  #set .settingssummary::after{grid-column:2;grid-row:1/3}
  #set .settingsbody>.dim{font-size:.84rem}
  #set .settingsbody .plbar{margin-top:.6rem;gap:.4rem}
  #set .settingscard:not(.settingsdisclosure) .plbar{display:flex;flex-wrap:wrap;padding:.25rem;
    border:1px solid var(--line2);border-radius:10px;background:var(--panel2)}
  #set .settingscard:not(.settingsdisclosure) .plbar .chip{flex:1;border-color:transparent;
    border-radius:7px;min-height:44px;padding:.35rem .4rem}
  #set .settingscard:not(.settingsdisclosure) .plbar .chip.on{background:var(--panel);
    color:var(--accent);border-color:var(--line);font-weight:650}
  #set .settingscard .chip{min-height:44px;white-space:normal}
  #set .settingscard .tagpill{display:inline-flex;align-items:center;gap:.3rem;max-width:100%;
    padding:.15rem .15rem .15rem .55rem;white-space:normal;overflow-wrap:anywhere;font-size:.85rem}
  #set .settingscard .tagpill .x{min-width:36px;min-height:40px;font-size:1.1rem;flex:none}
  #set .ratinglabels{display:grid;grid-template-columns:minmax(0,1fr);gap:.5rem}
  #set .ratinglabels>label{display:grid;grid-template-columns:1.5rem minmax(0,1fr);gap:.5rem}
  #set .ratinglabels .tin{width:100%;min-height:44px}
  #set .ratinglabels>.chip{justify-self:end;min-width:5rem}
}

/* Song volume complements the existing day-based habit targets. */
.gsongtrend{border:1px solid var(--line);border-radius:12px;background:var(--panel);padding:1rem;margin:0 0 .8rem;min-width:0}
.gsonghead{display:flex;align-items:center;justify-content:space-between;gap:.6rem;flex-wrap:wrap}
.gsonghead h3{margin:0;font-size:1rem}
.gsongcontrols{display:flex;gap:.2rem;padding:.2rem;background:var(--panel2);border-radius:9px}
.gsongcontrols .chip{min-height:44px;border-radius:7px;border-color:transparent;padding:.35rem .7rem}
.gsongcontrols .chip.on{background:var(--panel);border-color:var(--line);color:var(--accent)}
.gsongsummary{display:flex;align-items:baseline;gap:.55rem;margin-top:.8rem}
.gsongsummary b{font-size:1.8rem;letter-spacing:-.03em;line-height:1.1}
.gsongcaption,.gsongempty{font-size:.82rem;margin:.5rem 0}
.gsongtrend>svg{display:block;width:100%;max-height:280px;margin-top:.7rem}
.gsongdata{border-top:1px solid var(--line2);margin-top:.5rem;font-size:.82rem}
.gsongdata>summary{cursor:pointer;min-height:44px;align-content:center;color:var(--muted)}
.gsongdata table{display:table;width:100%;border-collapse:collapse}
.gsongdata tr{display:table-row}
.gsongdata th,.gsongdata td{display:table-cell;width:auto;padding:.5rem;text-align:left;border-bottom:1px solid var(--line2)}
.gsongdata caption{text-align:left;color:var(--muted);padding:.4rem}
.gsongdata td{text-align:right;font-variant-numeric:tabular-nums}
@media(max-width:860px){
  .plmaster>.plbar{display:grid;grid-template-columns:1fr 1.4fr auto;gap:.45rem;margin-bottom:.8rem}
  .plmaster>.plbar>.search{grid-column:1/-1;grid-row:1;width:100%;min-width:0;min-height:44px;background:var(--panel);border-radius:10px}
  .plmaster>.plbar>.chip{min-height:44px;border-radius:9px;padding:.4rem .45rem;font-size:.82rem;justify-content:center}
  .plmaster>.plbar>.mobilelisttools{grid-column:3;grid-row:2}
  .plmaster>.plbar>.chip:first-child{grid-column:1;grid-row:2}
  .plmaster>.plbar>.chip:nth-child(3){grid-column:2;grid-row:2}
  .plmaster>.sec{padding:.5rem 0;border-bottom:0}
  .plmaster>.sec>summary{min-height:40px;padding:.4rem .25rem;color:var(--muted);font-size:.78rem;text-transform:none;letter-spacing:.02em}
  .plmaster>.sec>.opts{border:1px solid var(--line);border-radius:12px;background:var(--panel);overflow:hidden}
  .plmaster .plrow{min-height:56px;padding:.75rem .65rem;border-radius:0;gap:.55rem;font-size:.95rem}
  .plmaster .plrow+.plrow{border-top:1px solid var(--line2)}
  .plmaster .plrow .nm{white-space:normal;overflow-wrap:anywhere;line-height:1.35}
  .plmaster .plrow .star{color:var(--accent);font-size:1rem}
  .plmaster .plrow .n{min-width:1.8rem;text-align:center;font-size:.78rem;flex:none}
  .plrailempty{padding:1rem .5rem;font-size:.9rem}
  .goalprojecthead{display:grid;grid-template-columns:1fr 1fr;gap:.55rem}
  .goalprojecthead>div{grid-column:1/-1}
  .goalprojecthead>div>p{font-size:.85rem;margin:.4rem 0 .2rem}
  .goalprojecthead>.chip{min-width:0;border-radius:9px;font-size:.85rem}
  .goalprojecthead>.chip.on{grid-column:1;grid-row:2}
  .goalprojecthead>.chip:not(.on){grid-column:2;grid-row:2}
  .goalfilters{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:.55rem;margin:.85rem 0}
  .goalfilters .goalfield{min-width:0;font-size:.78rem;color:var(--muted)}
  .goalfilters .search{font-size:.88rem;color:var(--fg);min-height:44px;border-radius:9px;background:var(--panel)}
  .goalfilters>.chip{grid-column:1/-1;justify-self:end;background:none;border-color:transparent;font-size:.8rem;min-height:36px}
  .goalempty{background:var(--panel);border-style:solid;padding:.9rem}
  .goalempty h3{font-size:.95rem}
  .goalempty p{font-size:.85rem}
  .musicalgoal{border-radius:12px;padding:.9rem}
  .musicalgoal .goalnotes,.musicalgoal>.dim{font-size:.85rem}
  .musicalgoal .goalcardhead{gap:.5rem}
  .musicalgoal .goalcardhead>.chip{font-size:.78rem;background:none}
  .goalnext{padding:.7rem .8rem;margin:.75rem 0;border-radius:8px}
  .goalnext strong{font-size:.95rem}
  .goalactions>.chip{font-size:.8rem;border-radius:8px}
  .ghistory{padding-top:1rem;margin-top:1rem}
  .ghead{margin-bottom:.75rem}
  .ghead h2{font-size:1.05rem}
  .ghead p{font-size:.85rem}
  .gsongtrend{padding:.85rem}
  .gsonghead h3{font-size:.95rem}
  .gsongcontrols .chip{font-size:.8rem;padding:.3rem .6rem}
}
@media(max-width:860px){
  .plmaster>.sec>.opts:empty{border:0;background:none}
}

/* Organize: choose a quiet task row, then focus on one song. */
.organizesheet #org{padding:.65rem 0 0}
.organizesheet>.sheetclose{display:none}
.orgscope{margin:0 0 .85rem;gap:.5rem}
.orgscope>.dim{font-size:.8rem}
.orgscope .search{flex:1;min-width:0;max-width:none!important;min-height:44px;border-radius:9px}
.orgtasks{display:grid;gap:0;margin:.75rem 0;border:1px solid var(--line);border-radius:12px;overflow:hidden}
.orgtasks .orgtask{display:flex;align-items:center;justify-content:space-between;gap:.6rem;
  padding:.85rem .75rem;min-height:60px;border:0;border-radius:0;background:var(--panel);text-align:left;white-space:normal}
.orgtasks .orgtask+.orgtask{border-top:1px solid var(--line2)}
.orgtaskname{font-weight:600;font-size:.9rem;color:var(--fg)}
.orgtaskcount{font-size:.78rem;color:var(--muted);flex:none}
.orgtask:disabled{opacity:.5;cursor:default}
.orgtask:not(:disabled):hover{background:var(--panel2)}
.orgtaskhead{gap:.5rem;margin:.5rem 0 .75rem;align-items:center}
.orgtaskhead>b{font-size:.9rem;flex:1}
.orgtaskhead>.dim{font-size:.8rem}
.orgsong{padding:.9rem;border-radius:12px;max-width:42rem;background:var(--bg)}
.orgsong .plhead{border:0;padding:0;margin-bottom:.7rem;gap:.25rem}
.orgsong .plhead h2{font-size:1.2rem;line-height:1.3}
.orgsong>.dim{font-size:.85rem}
.orgfields{padding:.8rem 0;gap:.65rem;min-width:0}
.orgform{display:flex;flex-wrap:wrap;gap:.6rem;width:100%;min-width:0}
.orgform>.tin{flex:1;min-width:0;width:100%;min-height:44px}
.orgfield{display:flex;flex:1;min-width:0;flex-direction:column;gap:.3rem;font-size:.78rem;color:var(--muted)}
.orgfield .kin{width:100%;min-width:0;min-height:44px;color:var(--fg)}
.orgsave{min-height:44px;border-radius:9px}
.orgfoot{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem;border-top:1px solid var(--line);padding-top:.65rem;margin-top:.2rem}
.orgfoot>.chip,.orgfoot>.clear{min-height:44px;font-size:.82rem}
.orgfoot>.dim{margin-left:auto;font-size:.78rem}
.orgfields>.pgrid{width:100%;min-width:0}
.orgfields input[type=range]{flex:1;min-width:0;min-height:44px}
@media(max-width:700px){
  .orgtaskhead>.chip{flex-basis:100%;justify-self:start;text-align:left;border:0;padding-left:0;min-height:44px}
  .orgtaskhead{flex-wrap:wrap}
  .orgform>.orgsave{width:100%}
  .orgfields .prow1{flex-direction:column;align-items:stretch;gap:.5rem}
  .orgfields .seg{display:flex;width:100%}
  .orgfields .seg button{min-height:44px}
  .orgfields>.chip{min-height:44px}
  .orgtaskcount{max-width:35%;white-space:normal;text-align:right}
}

/* AI drafting: a compact request, then a distinct review and apply step. */
.assistbody{gap:.8rem;padding:.65rem 0}
.assistprompt{display:flex;flex-direction:column;gap:.5rem;font-weight:600;font-size:.9rem}
.assistprompt textarea.notes{width:100%;min-height:7rem;resize:vertical;line-height:1.5;
  font-weight:400;padding:.75rem;border-radius:10px;background:var(--bg)}
.assisthint{font-size:.8rem;margin:.5rem 0}
.assistpropose{margin:.6rem 0 0}
.assistpropose>.chip{min-height:44px;border-radius:9px;min-width:7rem}
.assistrequest>summary,.assistdropped>summary{cursor:pointer;min-height:44px;align-content:center;font-size:.82rem;color:var(--muted)}
.assistrequest[open]>.assistprompt{margin-top:.5rem}
.assiststatus:empty{display:none}
.assiststatus{font-size:.85rem;line-height:1.5}
/* A network wait (AI proposal, import) shows motion, not just a label change:
   a static "Thinking…" read as stuck. */
.spin::before{content:"";display:inline-block;width:.8em;height:.8em;margin-right:.45em;vertical-align:-.08em;
  border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin .75s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.assistbody .assistreview{padding:.9rem;border:1px solid var(--line);border-radius:12px;background:var(--bg)}
.assistreview>h3{margin:0 0 .7rem;font-size:.95rem}
.assistreply{font-size:.85rem;line-height:1.5;margin:0 0 .75rem;white-space:pre-wrap}
.assistbody ol{gap:0;padding-left:1.4rem}
.assistbody ol li{padding:.7rem 0 .7rem .15rem;border-bottom:1px solid var(--line2);font-size:.9rem;overflow-wrap:anywhere}
.assistbody ol li:last-child{border-bottom:0}
.assistbody ol li::marker{color:var(--muted);font-size:.8rem}
.assistsongtitle{font-weight:650}
.assistnew{margin-left:.4rem;padding:.05rem .4rem;border-radius:6px;font-size:.7rem;font-weight:600;background:var(--line2);color:var(--muted);vertical-align:middle}
.assistcatalogonly{display:flex;align-items:center;gap:.4rem;min-height:44px;font-size:.85rem}
.assistsongartist{display:block;margin:.2rem 0;font-size:.82rem}
.assistbody ol li>.dim{font-size:.82rem;line-height:1.45}
.assisttime{float:right;margin-left:.5rem}
.assistgoaltitle{display:block;margin:.3rem 0 .6rem;font-size:1.05rem;line-height:1.35}
.assistdropped{border-top:1px solid var(--line);margin-top:.7rem}
.assistdropped ul{font-size:.82rem;color:var(--muted);padding-left:1.2rem}
.assistactions{display:flex;flex-wrap:wrap;gap:.5rem;margin:0}
.assistactions>.chip{min-height:44px;border-radius:9px;white-space:normal}
@media(max-width:700px){
  .assistactions{display:grid;grid-template-columns:minmax(0,1fr);width:100%}
  .assistactions>.assistdiscard{justify-self:center;border-color:transparent;background:none;min-width:7rem}
  .assistpropose>.chip{width:100%}
  .assistsheet>.sheetclose{display:none}
}

/* Weekly check-in */
.ckme{align-self:flex-end;max-width:85%;padding:.5rem .75rem;border-radius:12px;background:var(--line2)}
.ckcoach{max-width:92%}
.ckgroup{margin:.8rem 0 0}
.ckgroup>h4{margin:0 0 .3rem;font-size:.85rem}
.ckpick{display:flex;gap:.6rem;align-items:flex-start;min-height:44px;padding:.35rem 0;font-size:.9rem;line-height:1.35;overflow-wrap:anywhere}
.ckpick input{margin-top:.2rem;flex:none;width:1.1rem;height:1.1rem}
.ckplrow{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.4rem}
.ckplrow>*{min-height:44px;flex:1 1 12rem;min-width:0}

/* Imports keep instructions secondary and make the preview a distinct step. */
.importpane{max-width:48rem;min-width:0}
.importpane>.dim,.importintro{font-size:.88rem;line-height:1.5;margin:.6rem 0}
.importhelp{margin:.5rem 0 .75rem;font-size:.84rem}
.importhelp>summary{cursor:pointer;color:var(--muted);min-height:44px;align-content:center}
.importhelp>div{padding:.5rem 0;line-height:1.6}
.importpane>textarea.notes{width:100%;padding:.8rem;border-radius:10px;line-height:1.5;background:var(--panel)}
.importdestination{display:flex;flex-wrap:wrap;gap:.5rem;margin:.9rem 0}
.importdestination>.search{flex:1;min-width:0;min-height:44px}
.importsync{flex-basis:100%;display:flex;align-items:flex-start;gap:.5rem;font-size:.8rem;line-height:1.5;padding:.5rem 0}
.importsync input{flex:none;margin:.2rem 0;width:18px;height:18px}
.importparts{gap:.4rem;margin-top:.8rem}
.importparts>.dim{flex-basis:100%;font-size:.82rem;margin-bottom:.15rem}
.importparts>.chip{min-height:44px;font-size:.82rem}
.importpreviewactions>.chip,.importapply,.importcommit>.chip,.importfile>.chip{min-height:44px;border-radius:9px}
.importsummary{border:1px solid var(--line);border-radius:12px;background:var(--panel);padding:.85rem;margin:1rem 0;font-size:.85rem;line-height:1.5}
.importsummary h3{font-size:1rem;margin:0 0 .5rem}
.importtable td{overflow-wrap:anywhere}
.importtable .importmatch>.chip{display:block;margin-top:.5rem;min-height:44px}
.importfile{display:flex;flex-wrap:wrap;gap:.75rem;margin:1rem 0}
.importfile input[type=file]{max-width:100%;min-width:0;font-size:.85rem}
.importfilename{overflow-wrap:anywhere}
.importsets>div{padding:.6rem 0;border-bottom:1px solid var(--line2);font-size:.85rem}
@media(max-width:860px){
  #pldetail .importpane .plhead{gap:.5rem;background:none;border:0;padding:.25rem 0 .75rem;margin-bottom:0}
  #pldetail .importpane .plhead h2{grid-column:1/-1;font-size:1.25rem}
  .importdestination>.dim{flex-basis:100%;font-size:.82rem}
  .importdestination>.search{width:100%;flex-basis:100%;font-size:.88rem}
  .importpane>.importapply,.importpreviewactions>.chip,.importcommit>.chip,.importfile>.chip{width:100%;white-space:normal}
  #pldetail .importtable tr{padding:.8rem;margin-bottom:.5rem}
  .importtable td.t{font-size:1rem;font-weight:650}
  .importtable td.a{font-size:.85rem;color:var(--muted);margin:.2rem 0 .5rem}
  .importtable td.dim,.importmatch{font-size:.82rem;line-height:1.5}
  .sbppane .importtable tbody tr{display:grid;grid-template-columns:24px minmax(0,1fr);gap:.2rem .5rem}
  .sbppane .importtable td{width:auto;min-width:0;grid-column:2}
  .sbppane .importtable .importcheck{grid-column:1;grid-row:1/4}
  .importcheck input{width:18px;height:18px;margin:.25rem 0}
  .importtable button.clear{min-height:44px;margin-left:.3rem}
}
@media(max-width:860px){
  .importpane{padding-bottom:calc(var(--navh) + var(--safeb) + 1rem)}
  .importpane button,.importpane input,.importpane textarea{scroll-margin-bottom:calc(var(--navh) + var(--safeb) + 1rem)}
}

/* First-run card (renderFirstRun in app.js): an empty library gets one
   "add your songs" panel; after that, a one-line next step. */
#firstrun[hidden]{display:none}
.firstrun{margin:.6rem .6rem .2rem;padding:.9rem 1rem;display:grid;gap:.6rem}
.firstrunhead{display:flex;align-items:center;gap:.6rem}
.firstrunhead strong{font-size:1.15rem}
.firstrunhead .linkbtn{margin-left:auto}
.firstrunask{width:100%;box-sizing:border-box;font:inherit;padding:.6rem .7rem;border:1px solid var(--line);
  border-radius:10px;background:var(--panel);color:var(--fg);resize:vertical;min-height:4.5rem}
.firstrungo{justify-self:start;font-size:1rem;padding:.45rem 1rem}
.firstrunmore{display:flex;flex-wrap:wrap;gap:.2rem 1rem;font-size:.9rem}
.linkbtn{all:unset;cursor:pointer;color:var(--accent);font-size:.9rem;padding:.2rem 0}
.linkbtn:hover{text-decoration:underline}
.linkbtn:focus-visible{outline:2px solid var(--accent);border-radius:3px}
.linkbtn:disabled{opacity:.5;cursor:default}
.firstrunresult{display:grid;gap:.4rem;border-top:1px solid var(--line);padding-top:.6rem}
.firstrunresult p{margin:0}
.firstrunpicks{list-style:none;margin:0;padding:0;display:grid;gap:.3rem;max-height:18rem;overflow:auto}
.firstrunpicks label{display:flex;align-items:center;gap:.45rem;cursor:pointer}
.firstrunacts{display:flex;flex-wrap:wrap;align-items:center;gap:.8rem}
.firstrunnext{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem .7rem}
.firstrunnext .firstruncount{margin-left:auto}
@media (max-width:860px){.firstrungo,.firstrunacts .chip,.firstrunnext .chip{min-height:44px}.linkbtn{padding:.45rem 0}}

/* Weekly recap (recapBlock in app.js). */
.recapsection{margin:.6rem .6rem .4rem;padding:.8rem .9rem}
.recaphead{display:flex;align-items:baseline;gap:.6rem;flex-wrap:wrap}
.recaphead strong{font-size:1.05rem}
.recaphead .chip{margin-left:auto}
.recapnav{display:flex;align-items:center;gap:.4rem;margin-bottom:.4rem}
.recapnav h3{margin:0 auto 0 0}
.recapdays{display:flex;gap:.3rem;margin:.6rem 0 .5rem}
.recapdays span{width:1.7rem;height:1.7rem;border-radius:50%;display:inline-flex;align-items:center;
  justify-content:center;font-size:.72rem;font-weight:600;background:var(--panel2);color:var(--muted)}
.recapdays span.on{background:var(--accent);color:var(--accent-fg)}
.recapstats{list-style:none;margin:0 0 .4rem;padding:0;display:flex;flex-wrap:wrap;gap:.25rem 1rem}
.recapline{margin:.25rem 0}
.recapnext{border-top:1px solid var(--line);margin-top:.55rem;padding-top:.5rem;display:grid;gap:.4rem}
.recapsub{font-weight:600}
.recapchips{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
.recapfocus{all:unset;cursor:pointer;display:block;line-height:1.4}
.recapfocus:focus-visible{outline:2px solid var(--accent);border-radius:4px}
.recapfocus span{color:var(--muted)}
.firstrunset{display:flex;flex-wrap:wrap;align-items:center;gap:.45rem}
.firstrunset input.search{flex:1 1 10rem;min-width:8rem}
.goalcardacts{display:flex;flex-wrap:wrap;gap:.35rem;justify-content:flex-end}
.assistgoalfor{font-weight:600;margin-bottom:.4rem}
.assistsuggest{margin-top:.8rem;border-top:1px solid var(--line);padding-top:.5rem}
.assistsuggestrow{display:flex;gap:.6rem;align-items:center;justify-content:space-between;padding:.35rem 0}
.assistsuggestrow>div{min-width:0;overflow-wrap:anywhere}
.assistsuggestrow .chip{flex:none}
.readerscore{display:flex;flex-direction:column;gap:.5rem}
.scorebar{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem;position:sticky;top:var(--headh);z-index:3;padding:.35rem 0;background:var(--bg)}
.scorebar select{font:inherit;font-size:.85rem;padding:.25rem .35rem;border:1px solid var(--line);border-radius:var(--r);background:var(--panel);color:inherit;max-width:11rem}
.scorepos{font-variant-numeric:tabular-nums;font-size:.85rem;margin-left:auto}
/* isolation: alphaTab's cursor layers are absolutely positioned with their own
   z-index — without a stacking context here they paint over the reader header. */
.scorehost{position:relative;isolation:isolate;background:#fff;color:#000;border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.scorehost .at-cursor-bar{background:rgb(255 200 0 / .25)}
.scorehost .at-cursor-beat{background:rgb(64 64 255 / .75);width:3px}
.scorehost .at-highlight *{fill:#0078ff;stroke:#0078ff}
.miditracks{display:flex;flex-direction:column;gap:.3rem}
.miditrack{display:flex;align-items:center;gap:.5rem;padding:.35rem .5rem;border:1px solid var(--line);border-radius:var(--r)}

/* Desktop practice: keep controls readable from an instrument's distance. */
.desktopcontrollabel{display:none}
@media (min-width:861px){
  :root{--head:60px;--navh:46px}
  header{gap:.85rem;padding:.6rem 1.25rem}
  header h1{flex:0 1 13rem;font-size:1.1rem}
  header .spacer{flex:1}
  header .catsearchinput{flex:1 1 20rem;max-width:36rem;min-height:42px;
    padding:.6rem .9rem;background:var(--bg);border-radius:9px}
  #searchtoggle{display:none!important}
  #addbtn{min-height:42px;padding:.5rem .9rem}
  header>.icon{min-width:40px;min-height:40px}
  nav#tabs{padding:0 1.25rem;gap:1rem}
  #tabs .tab{padding:0 .8rem;font-size:.95rem;gap:.5rem}
  #tabs .navicon{width:18px;height:18px}
  #pr,#goals,#stats,#set{margin:0 auto;padding:1.8rem 2rem;max-width:82rem}
  #pr .psess{padding:1.5rem}
  .practiceactions{gap:.5rem}
  .psess .practiceprimary{background:var(--accent);border-color:var(--accent);
    color:var(--accent-fg);font-weight:650;padding:.5rem 1rem}
  #pr .plitem.prow{padding:.65rem .2rem}
  #pr .prow .tick{min-height:38px}
  html.reading header h1{flex:1;font-size:1.2rem}
  html.reading #reader{margin-left:auto;margin-right:auto;padding:1.5rem 1.5rem 13rem}
  html.playingalong #reader{margin-right:calc(var(--playaw) + 1.2rem)}
  html.tunerActive #reader{margin-right:calc(var(--tunerw) + 1.2rem)}
  html.metroActive #reader{margin-right:calc(var(--metrow) + 1.2rem)}
  .playalong{width:min(42rem,38vw);padding:1rem 1.1rem;gap:.8rem;box-shadow:-3px 0 18px rgba(0,0,0,.06)}
  .playactl,.playaseek{width:100%;max-width:none}
  .playactl{gap:.8rem}
  .playarow{gap:.65rem}
  .playarow .readerfabbtn{width:56px;height:56px;background:var(--panel2)}
  .playarow .chip,.playaoptions .toolseg button{min-height:42px;font-size:.95rem}
  .playatime{font-size:1rem}
  .playaseek{font-size:.95rem;gap:.4rem}
  .playaseek input{min-height:28px;cursor:pointer}
  .playaoptions>summary{font-size:1rem;padding:.6rem .1rem}
  .readerfab{background:var(--panel);padding:.65rem;gap:.6rem;border-radius:16px}
  .readerfab .readerfabbtn{width:54px;height:54px;background:var(--panel2)}
  .readerfab .readerfabbtn.on{background:var(--accent)}
  .readerfabnav{gap:.6rem}
  .readerfabbtn svg{width:26px;height:26px;flex:none}
  html.reading #readerqa>details>summary.icon{min-height:42px;padding:.5rem .8rem}
  .readercommandlabel{font-size:.95rem}
  .readerfabnav .readerfabbtn{border-color:var(--accent);color:var(--accent)}
  .readerfabnav .readerfabbtn.on{color:var(--accent-fg)}
}
@media (min-width:1200px){
  .desktopcontrollabel{display:block;font-size:.8rem;font-weight:600;white-space:nowrap}
  .readerfab{display:flex;align-items:center;gap:.5rem}
  .readerfab .readerfabbtn{flex-direction:column;gap:.25rem;width:76px;height:64px;border-radius:10px}
  .readerfabnav{border-left:1px solid var(--line);padding-left:.5rem;gap:.5rem}
  .readerfabnav .readerfabbtn{width:86px}
  .playarow .readerfabbtn{width:auto;min-width:100px;gap:.6rem;border-radius:12px;padding:0 .9rem}
  .playarow .desktopcontrollabel{font-size:1rem}
  html.reading #reader{padding-bottom:9rem}
}

.readercommandtitle{min-width:0}
.readercommandcontext{font-size:.85rem;color:var(--muted);margin-top:.3rem;overflow-wrap:anywhere}
.liveworkflow{display:none}
@media (min-width:861px){
  .readerrecmenu>.readerquickpop,.readerlivemenu>.readerquickpop{
    width:min(38rem,calc(100vw - 2rem));right:1rem;top:calc(var(--headh) + .75rem);
    max-height:calc(100dvh - var(--headh) - 1.5rem);border-radius:16px;padding:0;
    box-shadow:0 12px 40px rgba(0,0,0,.2)}
  .readerquickmenu .readercommandhead{padding:.6rem 1.25rem}
  .readerquickmenu .readercommandhead strong{font-size:1rem}
  .readerquickmenu .readercommandcontext{font-size:.95rem;margin-top:.4rem}
  .readerquickmenu .readercommandbody{padding:.25rem 1.25rem 1rem}
  .readerquickmenu .readercommandsection{padding:1.1rem 0}
  .readerquickmenu .readercommandsection h3{font-size:.78rem}
  .readerlivemenu .readercommandsection>p{font-size:1rem;line-height:1.6;margin-bottom:1rem}
  .readerquickmenu .readerdisclosure{padding:.25rem 0}
  .readerquickmenu .readerdisclosure>summary{min-height:54px;font-size:1rem}
  .readerquickmenu .readercommandbody :where(button){min-height:44px}
  :is(.readerrecmenu,.readerlivemenu) .readercommandprimary{min-height:54px;border:1px solid var(--accent);border-radius:10px;
    background:var(--accent);color:var(--accent-fg);font-size:1rem;text-align:center}
  :is(.readerrecmenu,.readerlivemenu) .readercommandprimary.on{background:var(--bad);border-color:var(--bad);color:#fff}
  .readerlivemenu .readercommandprimary.readersecondaryaction{background:var(--panel2);border:1px solid var(--line);color:var(--fg)}
  .readerrecmenu .recbox{gap:.8rem}
  .readerrecmenu .recstartbtn{min-height:56px;border-radius:10px;font-size:1.05rem;text-align:center}
  .readerrecmenu .recoptions>summary{min-height:44px;font-size:.95rem}
  .readerrecmenu .recattach .chip{min-height:44px;width:100%;justify-content:center}
  .readerrecmenu .recrow{flex-direction:column;align-items:stretch;gap:.65rem;padding:.9rem 0}
  .readerrecmenu .recrow .setacts{justify-content:flex-end}
  .readerrecmenu .recrow .setacts .chip{min-width:42px;min-height:42px;justify-content:center}
  .readerrecmenu textarea{min-height:100px;font-size:1rem}
  .readerlivemenu .cuepresets{display:grid;grid-template-columns:1fr 1fr;padding:.5rem 0;gap:.65rem}
  .readerlivemenu .cuepresets button{min-height:46px;justify-content:center;border:1px solid var(--line);background:var(--panel2)}
  .readerlivemenu .cuecustomrow{padding:.5rem 0;gap:.65rem}
  .readerlivemenu .cuecustomrow input{min-height:44px;padding:.65rem;font-size:1rem}
  .liveworkflow{display:flex;gap:1.25rem;justify-content:space-between;list-style-position:inside;
    color:var(--muted);font-size:.85rem;padding:0;margin:1rem 0 0}
  .sheetback:has(>.songsheet),.sheetback:has(>.songeditorsheet){align-items:center;padding:2rem}
  .sheet.songsheet{max-width:56rem}
  .sheet.songeditorsheet{max-width:66rem}
  .sheet.songsheet,.sheet.songeditorsheet{border:1px solid var(--line);border-radius:18px;max-height:calc(100dvh - 4rem);animation:sheetfade .14s ease-out}
  .songsheet>.sheethead,.songeditorsheet>.sheethead{padding:1.4rem 4.5rem 1.2rem 1.5rem}
  :is(.songsheet,.songeditorsheet) .sheethead h3{font-size:1.55rem}
  :is(.songsheet,.songeditorsheet) .sheetsub{font-size:.95rem;margin-top:.5rem}
  :is(.songsheet,.songeditorsheet) .sheetdismiss{width:42px;height:42px;top:1.1rem;right:1.1rem}
  :is(.songsheet,.songeditorsheet) .songmodalbody{padding:1rem 1.5rem 1.5rem}
  .songprimaryactions{display:flex;flex-wrap:wrap;gap:.75rem;padding-bottom:1rem;border-bottom:1px solid var(--line)}
  .songprimaryactions>.sheetrow{flex:1 1 18rem;padding:0;border:0}
  .songprimaryactions>.songprofrow{flex-basis:100%}
  .songsheet .songprimaryactions .sbtn{min-height:50px;padding:.65rem .9rem}
  .songsheet .songdisclosure>summary{min-height:60px;font-size:1.05rem}
  .songsheet .songdisclosure[open]{padding-bottom:1rem}
  .songsheet textarea{min-height:100px;font-size:1rem}
  .songeditorsheet .tin,.songeditorsheet .kin{min-height:40px}
}
/* Chord popup (openChordPop in app.js) — fixed to the viewport near the tapped
   chord, parented to <body> so a reader re-render doesn't take it away. */
.chordpop{position:fixed;z-index:60;width:min(17rem,calc(100vw - 16px));display:flex;flex-direction:column;
  gap:.4rem;padding:.6rem .7rem .5rem;background:var(--panel);border:1px solid var(--line);
  border-radius:12px;box-shadow:0 8px 28px rgba(0,0,0,.22)}
.chordpophead{display:flex;align-items:center;gap:.5rem}
.chordpophead b{font-size:1.15rem;color:var(--accent)}
.chordpophead .dim{font-size:.85rem;font-variant-numeric:tabular-nums}
.chordpopx{margin-left:auto;min-width:32px;min-height:32px}
.chordpopnote{font-size:.85rem;line-height:1.4}
.chordpop .sbtn{min-height:40px;border-radius:var(--r);border:1px solid var(--line);
  background:var(--panel2);color:var(--fg);font-size:.95rem;font-weight:550}
.chordpop .sbtn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-fg)}
.chordpop .sbtn.bad{color:var(--bad)}
.chordpopstop{display:flex;align-items:center;gap:.35rem;font-size:.92rem;min-height:36px;cursor:pointer}
.chordpop .linkbtn{align-self:flex-start;font-size:.85rem}
/* A tagged chord the track pauses at — the tag dot turns solid red. */
.cpchord.tagged.stopmark::after{background:var(--bad);opacity:1;width:.4em;height:.4em}
@media (max-width:860px){.chordpop .sbtn{min-height:44px}}
/* Score mixer + play-along sync (scoreView in app.js). Synced, the score has
   no transport of its own — play-along's ▶ is the one play button. */
.scoremixer{border:1px solid var(--line);border-radius:var(--r);padding:.3rem .5rem;background:var(--panel)}
.scoremixer>summary{cursor:pointer;font-size:.88rem;padding:.2rem 0}
.mixrow{display:flex;align-items:center;gap:.4rem;padding:.25rem 0;border-top:1px solid var(--line2)}
.mixrow:first-of-type{border-top:0}
.mixname{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.9rem}
.mixrow .chip{min-width:3.4rem}
.mixrow .chip.on{background:var(--accent);border-color:var(--accent);color:var(--accent-fg)}
.scoresyncchip{display:none}
.readerscore.synced .ownplayonly{display:none}
.readerscore.synced .scoresyncchip{display:inline-flex;border-color:var(--accent);color:var(--accent)}
@media (max-width:860px){.mixrow .chip{min-height:40px}}
/* Catalog ▶ Practice: always there on touch; on a mouse-driven desktop it
   shows with the row under the pointer, so 200 rows don't each shout it. */
@media (min-width:861px) and (hover:hover){
  #rows tr .practicego{opacity:0;transition:opacity .12s}
  #rows tr:hover .practicego,#rows .practicego:focus-visible{opacity:1}
}
/* openVideoLinkSheet: the paste box pulses once a search tab opens, so it's
   the obvious next step on coming back. */
.videolinkin{width:100%;box-sizing:border-box}
.videolinkin.pulse{animation:linkpulse 1.4s ease-out 3;border-color:var(--accent)}
@keyframes linkpulse{0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 55%,transparent)}
  100%{box-shadow:0 0 0 .7rem transparent}}
.pausecustom{width:8.5rem;text-align:center}
.howaskoff{display:flex;align-items:center;gap:.4rem;font-size:.88rem;color:var(--muted);margin-top:.6rem;cursor:pointer}
/* Reader right-click / long-press menu (openReaderContextMenu). */
.ctxmenu{gap:.1rem;padding:.35rem;width:min(16rem,calc(100vw - 16px))}
.ctxitem{all:unset;box-sizing:border-box;cursor:pointer;padding:.55rem .65rem;border-radius:8px;font-size:.95rem}
.ctxitem:hover,.ctxitem:focus-visible{background:var(--panel2)}
.ctxitem:disabled{opacity:.45;cursor:default}
/* Long-press opens that menu — don't let iOS's own callout fight it. */
.readermatcontent{-webkit-touch-callout:none}
@media (max-width:860px){.ctxitem{padding:.7rem .7rem}}
/* Header pill: back to a rehearsal you stepped out of / band waiting. */
.resumepill{flex:0 1 auto;min-width:0;max-width:22rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  border:1px solid var(--accent);background:color-mix(in srgb,var(--accent) 12%,var(--panel));color:var(--accent);
  border-radius:999px;padding:.35rem .8rem;font-size:.82rem;font-weight:700;min-height:36px;cursor:pointer}
.resumepill.waiting{background:var(--accent);color:var(--accent-fg);animation:resumepulse 2s ease-in-out infinite}
@keyframes resumepulse{50%{box-shadow:0 0 0 .35rem color-mix(in srgb,var(--accent) 25%,transparent)}}
html.reading .resumepill{display:none}
@media (max-width:860px){.resumepill{max-width:46vw;font-size:.76rem;padding:.3rem .6rem}}
/* "Highlight my place" (paintReaderPlace) — the band page's follow-line look. */
#reader .chordpro .placeline{background:color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius:.35rem;box-shadow:inset 3px 0 0 var(--accent)}
/* Song dialog overview (songOverview) — a setlist page's stat cards and big
   actions, ahead of the edit fields. */
.songoverview{display:flex;flex-direction:column;gap:.5rem;margin-bottom:.4rem}
.songoverview .plstats{flex-wrap:wrap}
.songoverview .plstat{text-align:center;font:inherit;color:inherit;background:var(--panel)}
.songoverview button.plstat{cursor:pointer}
.songoverview .plstat:not(.action){border-style:solid}
.songoverview .plstat.action b{color:var(--fg)}
.songoverview .plstat.action{border-style:solid;border-color:var(--line)}
.songoverview .plstat.action:hover{border-color:var(--accent)}
.songoverview .startrow{margin:0}
.songoverview .bigstart{min-height:48px;font-weight:650}
@media (max-width:860px){
  .songoverview .plstats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0;
    border:1px solid var(--line);border-radius:10px;background:var(--panel);overflow:hidden}
  .songoverview .plstat,.songoverview .plstat.action{min-width:0;padding:.45rem .25rem;border:0;border-radius:0;background:none}
  .songoverview .plstat+.plstat{border-left:1px solid var(--line2)}
  .songoverview .plstat b{font-size:1.02rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .songoverview .plstat span{display:block;font-size:.68rem;line-height:1.3}
  .songoverview .startrow{flex-wrap:wrap}
  .songoverview .bigstart{flex:1 1 40%}
}

/* Rating rows must fit their grid cells before another field can sit beside them. */
.songeditorsheet .pgrid{grid-template-columns:repeat(auto-fit,minmax(min(100%,24rem),1fr));gap:.7rem}
.songeditorsheet .prow1{flex-wrap:wrap;min-width:0}
.songeditorsheet .prow1 .seg{flex:none}
.badges .bdg{text-transform:none;letter-spacing:0}
@media(min-width:861px){
  :root{--readerrailw:min(42rem,max(28rem,38vw))}
  html.reading #readerqa .readerquickmenu>.readerquickpop{top:var(--headh);right:0;bottom:0;
    width:var(--readerrailw);max-height:calc(100dvh - var(--headh));border-radius:0;border:0;
    border-left:1px solid var(--line);padding:0;box-shadow:-4px 0 20px rgba(0,0,0,.07);animation:readerslide .16s ease-out}
  html.reading:has(#readerqa .readerquickmenu[open]) #reader{margin-right:calc(var(--readerrailw) + 1.2rem)}
  html.reading:has(#readerqa .readerquickmenu[open]) .readerfabwrap{right:calc(var(--readerrailw) + 1.2rem)}
  .sheetback:has(>.listcreatorsheet),.sheetback:has(>.bandsetupsheet){align-items:center;padding:2rem}
  .sheet.listcreatorsheet,.sheet.bandsetupsheet{max-width:52rem;border:1px solid var(--line);border-radius:18px;animation:sheetfade .14s ease-out}
}
@keyframes readerslide{from{clip-path:inset(0 0 0 24px);opacity:0}to{clip-path:inset(0);opacity:1}}
.listcreateform{display:grid;gap:.9rem;padding:.8rem 0 1.2rem}
.listcreateform .search{min-height:46px;width:100%;font-size:1rem}
.listcreateform p{margin:0;line-height:1.5}
.listcreateform .sbtn{min-height:48px}
.listcreateheading{margin:.2rem 0 .8rem;font-size:1rem}
.listcreatechoices{display:grid;grid-template-columns:1fr 1fr;gap:.65rem}
.listcreatechoice{display:flex;flex-direction:column;gap:.4rem;padding:1rem;border:1px solid var(--line);
  border-radius:10px;background:var(--panel2);text-align:left;white-space:normal}
.listcreatechoice strong{font-size:.95rem}
.listcreatechoice .lcicon{font-size:1.4rem;line-height:1;color:var(--accent)}
.listwizard{padding:.6rem 0 .4rem}
.listwizardnav{display:flex;gap:.6rem;justify-content:space-between}
.listwizardnav .sbtn{flex:1}
.listcreatechoice .lcdesc{font-size:.85rem;color:var(--muted);line-height:1.45}
.listcreatechoice:hover{border-color:var(--accent)}
.listcreatechoice:disabled{opacity:.5;cursor:not-allowed}
#plmaster>.plbar.listcreatebar{display:flex;flex-wrap:wrap;gap:.65rem}
#plmaster>.listcreatebar>.search{order:-1;width:100%;flex:1 0 100%}
#plmaster>.listcreatebar>.chip{min-height:44px;flex:1}
@media(max-width:600px){.listcreatechoices{grid-template-columns:1fr;gap:.5rem}
  .listcreatechoice{display:grid;grid-template-columns:2rem 1fr;column-gap:.6rem;row-gap:.15rem;padding:.7rem .8rem}
  .listcreatechoice .lcicon{grid-row:span 2;align-self:center;text-align:center}}

/* ── Practice: home question + session (renderPracticeHome/Session) ── */
.pinst{display:flex;gap:.35rem;margin:0 0 .8rem;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}
.pinst::-webkit-scrollbar{display:none}
.pinst .chip{flex:none}
.pinst .chip{min-height:40px;padding:.35rem .8rem}
.pcontinue{display:flex;flex-direction:column;align-items:flex-start;gap:.15rem;width:100%;
  text-align:left;font:inherit;color:inherit;cursor:pointer;padding:.85rem 1rem;margin-bottom:1rem;
  border:1px solid var(--accent);border-left-width:4px;border-radius:12px;background:var(--panel)}
.pcontinue b{font-size:1.05rem;overflow-wrap:anywhere}
.pkicker{font-size:.75rem;font-weight:650;text-transform:uppercase;letter-spacing:.05em;color:var(--accent)}
.pask{font-size:1.2rem;letter-spacing:-.01em;margin:.2rem 0 .7rem}
.pchoices{display:grid;grid-template-columns:1fr 1fr;gap:.6rem;margin-bottom:1rem}
.pchoice{display:flex;flex-direction:column;align-items:flex-start;gap:.25rem;min-height:7.5rem;min-width:0;
  padding:.9rem;text-align:left;font:inherit;color:inherit;cursor:pointer;
  border:1px solid var(--line);border-radius:14px;background:var(--panel)}
.pchoice:hover,.pchoice:active{border-color:var(--accent);background:var(--panel2)}
.pchoice b{font-size:1rem;line-height:1.25}
.pchoice .dim{font-size:.82rem;line-height:1.3;overflow-wrap:anywhere}
.pchoiceicon{font-size:1.6rem;line-height:1;margin-bottom:.35rem}
@media (min-width:900px){.pchoices{grid-template-columns:repeat(4,1fr)}}
.psrcpick{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;width:100%;min-height:48px;
  text-align:left;font:inherit;color:inherit;background:none;border:0;border-top:1px solid var(--line2);
  padding:.6rem .1rem;cursor:pointer}
.psrcpick .t{min-width:0;overflow-wrap:anywhere}
.psrcpick .dim{flex:none;font-size:.85rem}
.psrcpick:active,.psrcpick:hover{background:var(--panel2)}
/* Technique (TECHNIQUE_PLAN.md): a slim full-width tile under the four
   song-centred ones, so it reads as secondary rather than a fifth peer. */
.pchoice.pchoicewide{grid-column:1/-1;min-height:0;flex-direction:row;align-items:center;gap:.6rem;padding:.7rem .9rem}
.pchoicewide .pchoiceicon{margin:0;font-size:1.3rem}
.pchoicewide .dim{margin-left:auto;text-align:right}
.drillrow{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:100%;min-height:52px;
  text-align:left;font:inherit;color:inherit;background:none;border:0;border-top:1px solid var(--line2);
  padding:.6rem .1rem;cursor:pointer}
.drillrow:active,.drillrow:hover{background:var(--panel2)}
.drillmain{display:flex;flex-direction:column;min-width:0}
.drillmain b{overflow-wrap:anywhere}
.drillmain .dim{font-size:.82rem;overflow-wrap:anywhere}
.drilltempo{display:flex;flex-direction:column;align-items:flex-end;gap:.2rem;flex:none;font-variant-numeric:tabular-nums;white-space:nowrap}
.drilltempo .secbar{width:4.5rem;flex:none;min-width:0}
.warmup{border:1px dashed var(--line);border-radius:12px;padding:.45rem .8rem .2rem;margin:.6rem 0;background:var(--panel2)}
.warmhead{display:flex;align-items:center;gap:.4rem}
.warmhead .pkicker{margin-right:auto}
.drillswitch{display:flex;gap:.5rem;align-items:flex-start;margin-top:.9rem;font-size:.88rem;cursor:pointer}
.drillswitch input{margin-top:.2rem;flex:none}
.drillretired{margin-top:.7rem}
.drillretired summary{cursor:pointer;color:var(--muted);font-size:.88rem}
.drillretiredrow{display:flex;align-items:center;gap:.5rem;padding:.4rem 0;border-top:1px solid var(--line2)}
.drillretiredrow span:first-child{margin-right:auto;overflow-wrap:anywhere;min-width:0}
.drillnotes{white-space:pre-wrap;margin:.2rem 0 .5rem}
.drilllink{display:inline-block;margin-bottom:.5rem}
.drillsteplabel{text-align:center}
.drillstep{display:flex;align-items:center;justify-content:center;gap:.8rem;margin:.2rem 0 .7rem}
.drillstep .chip{min-width:3rem;min-height:44px}
.drillbpm{font-size:2.4rem;font-weight:700;font-variant-numeric:tabular-nums;min-width:3.5ch;text-align:center}
.seg.drillclean{margin-bottom:.7rem}
.seg.drillclean button{flex:1;min-height:44px}
.drillladder{display:block;width:100%;height:80px;margin:.3rem 0 .7rem}
.drillladder .tgt{stroke:var(--p4fg);stroke-width:1;stroke-dasharray:4 4}
.drillladder polyline{fill:none;stroke:var(--accent);stroke-width:2}
.drillladder circle{fill:var(--accent)}
.drillladder circle.sloppy{fill:var(--panel);stroke:var(--accent);stroke-width:1.5}
.drillnote{width:100%;margin-bottom:.4rem}
.drillform{display:flex;flex-direction:column;gap:.7rem}
.drillform label{display:flex;flex-direction:column;gap:.25rem;min-width:0}
.drillform .search,.drillform select,.drillform textarea{width:100%}
.drillform select{min-height:44px;padding:.4rem .5rem;border:1px solid var(--line);border-radius:var(--r);background:var(--panel)}
.drillrow2{display:grid;grid-template-columns:1fr 1fr;gap:.6rem}
.drillsongchips{display:flex;flex-wrap:wrap;gap:.3rem}
.drillsongchips:empty{display:none}
.drillpick{display:flex;gap:.6rem;align-items:flex-start;padding:.55rem 0;border-top:1px solid var(--line2);cursor:pointer}
.drillpick input{margin-top:.25rem;flex:none;width:1.1rem;height:1.1rem}
.drillpick>span{display:flex;flex-direction:column;gap:.15rem;min-width:0;overflow-wrap:anywhere}
.drillpick .dim{font-size:.84rem}
.drillwhy{font-style:italic}
.focusdrill{display:block;padding:.3rem 0 0;font-size:.85rem}
.pmore{margin:.6rem 0;min-height:40px}
.pstep.upsell{border-style:dashed}
.pstephead{font-size:.78rem;font-weight:650;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);margin:.9rem 0 .4rem}
.pstep .thead{margin-bottom:.3rem}
.pdraw .chip{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pdescribe{width:100%;box-sizing:border-box;min-height:6rem;margin:.9rem 0 .6rem;font:inherit}
.psess .thead{flex-direction:column;align-items:stretch;gap:.55rem}
.psess .thead>.plback{align-self:flex-start;border:0;background:none;padding:0;min-height:32px;display:inline-block}
.psess .thead h2{overflow-wrap:anywhere}
.psess .thead .prog{margin-left:0;font-variant-numeric:tabular-nums}
.psess .pbarwrap{margin:0}
.psesssub{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}
.psess .pinst{margin:0}
.psess.stale .prow .agotxt:empty::before{content:"never"}
/* "Haven't played" is about how long it has been, so on a phone (where rows
   otherwise hide the staleness behind the tap) it stays on the row. */
@media (max-width:700px){
  .psess.stale .plitem.prow{grid-template-areas:"body chev" "when chev"}
  .psess.stale .prow .when{display:block;text-align:left;margin-top:.15rem}
  .psess.stale .prow .agotxt:empty::before{content:"never played"}
}
.psess{min-width:0}
.psess .thead>*{min-width:0;max-width:100%}

/* Goals stay scannable; details and actions belong inside the disclosure. */
.goalsummary{list-style:none;cursor:pointer;min-height:44px}
.goalsummary::-webkit-details-marker{display:none}
.goalsummary:focus-visible{outline:2px solid var(--accent);outline-offset:5px;border-radius:4px}
.goalsummarytitle{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:.8rem}
.goalsummarytitle .goalkicker{grid-column:1/-1}
.goalsummarytitle h3{margin:0;font-size:1rem;overflow-wrap:anywhere}
.goalchevron{font-size:1.4rem;color:var(--muted);transition:transform .15s}
.musicalgoal[open] .goalchevron{transform:rotate(180deg)}
.goalsummarymeta{display:flex;flex-wrap:wrap;gap:.25rem 1rem;font-size:.82rem;margin-top:.45rem}
.goalbody{border-top:1px solid var(--line);margin-top:1rem;padding-top:.8rem}
.goalbody>.goalcardacts{justify-content:flex-end;flex-wrap:wrap}
.goalmilestones li.goalnext{flex-direction:row;align-items:center}
.goalmilestonebody .goalnotes{font-size:.85rem;margin:.25rem 0 .25rem 1.85rem}
#stats .ghistory{border:0;padding-top:0;margin-top:0}
.gtargetedit summary,.practicereview>summary,.practicerecs>summary{cursor:pointer;min-height:44px;align-content:center;color:var(--muted);font-size:.85rem}
.gtargetedit .goalrow{padding:.4rem 0}
.practicereview,.practicerecs{margin-top:1.25rem;border-top:1px solid var(--line)}
.practicereview>summary,.practicerecs>summary{font-weight:600}
.practicereview .recapsection{margin-top:.3rem}

@media(max-width:860px){
  nav#tabs .tab{flex-direction:column;gap:.15rem;font-size:.76rem}
  nav#tabs .navicon{width:16px;height:16px}
}

/* Persistent practice modes, with their content directly below. */
.pchoice.selected{border-color:var(--accent);background:var(--panel2);box-shadow:inset 0 0 0 1px var(--accent)}
.pchoice.selected b{color:var(--accent)}
.pchoice:focus-visible,.milestonepractice:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.practicework:empty{display:none}
.milestonepractice{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem .7rem;min-height:44px;min-width:0;border:0;background:none;padding:0;color:inherit;font:inherit;text-align:left;cursor:pointer;overflow-wrap:anywhere}
.milestonepractice:hover{color:var(--accent)}
.milestonepracticehint{font-size:.75rem;color:var(--accent);white-space:nowrap}
.goalmilestones .complete .milestonepracticehint{text-decoration:none;color:var(--accent)}

/* The player stays mounted while its slot follows the Record panel. */
.playalong.recorddocked{position:fixed;right:auto;bottom:auto;height:auto;max-height:none;
  z-index:38;padding:0;box-shadow:none;border:0;background:var(--panel);overflow:visible}
.playalong.recorddocked .playavid{width:100%;height:auto;max-width:100%}
.playalong.recorddocked.spotify:not(.novideo) .playavid{height:152px}
.playalong.recorddocked.novideo .playavid{height:0}
.readerplayerslot{width:100%;min-width:0}
.readerperformtoggle{border-top:0;padding:.7rem 0}
.readerrecordlive .performcam,.readerrecordlive .performctl,.readerrecordlive .recindicator{
  position:static;width:100%;max-width:100%;margin:.6rem 0;box-shadow:none}
.readerrecordlive .performctl{display:flex;flex-wrap:wrap;padding:0;background:none}
.readerrecordlive .performbtn{flex:1;min-width:5rem;font-size:1rem;padding:.7rem}
.readerbandmanagement:empty{display:none}
.readerbandmanagement .bandsetupstep{padding:.7rem 0}
@media(max-width:860px){
  #readerqa .readerquickmenu[open]::before{top:var(--headh)}
}

/* Chord diagram strip above a ChordPro chart (reader ▸ Chart settings ▸
   Diagrams). One scrolling row, so it never pushes the chart off a phone. */
.chorddiagrams{display:flex;gap:.5rem;overflow-x:auto;padding:.3rem 0 .5rem;margin-bottom:.4rem;
  border-bottom:1px solid var(--line2);scrollbar-width:thin}
.chorddiag{margin:0;flex:none;display:flex;flex-direction:column;align-items:center;gap:.1rem}
.chorddiag figcaption{font-weight:700;color:var(--accent);font-size:.85rem}
.chorddiagart{display:flex;align-items:center;justify-content:center;min-height:2.2rem}
.chorddiag.unknown .chorddiagart{color:var(--muted);width:3rem;min-height:4.8rem}
.chorddiag svg{display:block}
.chorddiag .dgline{stroke:var(--muted);stroke-width:1}
.chorddiag .dgnut{stroke:var(--fg);stroke-width:3}
.chorddiag .dgdot,.chorddiag .pkon{fill:var(--accent)}
.chorddiag .dgopen{fill:none;stroke:var(--fg);stroke-width:1.1}
.chorddiag .dgmark,.chorddiag .dgfret{fill:var(--muted);font-size:9px;text-anchor:middle}
.chorddiag .dgfret{text-anchor:start}
.chorddiag .pkw{fill:#fff;stroke:var(--muted);stroke-width:.6}
.chorddiag .pkb{fill:#222;stroke:#222;stroke-width:.6}
.chorddiag .pkw.pkon,.chorddiag .pkb.pkon{fill:var(--accent)}

/* Print / save as PDF (printCharts in app.js): the app hides and #printroot,
   built just for this print, takes its place — one song per page. */
#printroot{display:none}
@media print{
  @page{margin:14mm 14mm 16mm}
  body.printing{background:#fff !important;color:#000}
  body.printing > :not(#printroot){display:none !important}
  body.printing #printroot{display:block;font-size:11pt;line-height:1.45;color:#000;
    -webkit-print-color-adjust:exact;print-color-adjust:exact}
  #printroot .printsong + .printsong{break-before:page}
  #printroot h1{font-size:17pt;margin:0 0 .15rem}
  #printroot .printmeta{color:#555;margin-bottom:.5rem}
  #printroot .printnote{font-style:italic;color:#333;margin:.3rem 0 .6rem}
  #printroot .printcover ol{font-size:13pt;line-height:1.8;padding-left:1.4rem}
  #printroot .chordpro{background:none;max-height:none;overflow:visible;padding:0;font-size:inherit}
  #printroot .cpsection{break-after:avoid;border-top-color:#ccc}
  #printroot .cpline,#printroot .cpgrid,#printroot .chorddiag{break-inside:avoid}
  #printroot .cpchord::before,#printroot .cpchordonly .cpchord,#printroot .cpgcell,
  #printroot .chorddiag figcaption{color:#000}
  #printroot .chorddiagrams{overflow:visible;flex-wrap:wrap;border-bottom-color:#ccc}
  #printroot .readertext{white-space:pre-wrap}
}
/* Nashville numbers (renderChordPro numbersTonic): same float, the number instead of the name. */
.chordpro.nashville .cpline:not(.cpchordonly) .cpchord::before{content:attr(data-num)}
.annotnote{width:100%;box-sizing:border-box;font:inherit;min-height:4.5rem;resize:vertical}
.onsongsetrow{flex-wrap:wrap;align-items:center;gap:.5rem}
.onsongsetrow .tin{flex:1;min-width:12rem}
.onsongsetlbl{display:flex;align-items:center;gap:.35rem}
.chordpro.cppaged{overflow:hidden;column-fill:auto;padding-bottom:0}
/* Chromium paints absolutely positioned descendants of overflow columns (every
   page past the first) piled at the container's right edge, so here the chord
   label is raised in the line's own flow instead of floated above it. */
.chordpro.cppaged .cpline{padding-top:0}
.chordpro.cppaged .cpchord::before{position:static;display:inline-block;width:0;vertical-align:1.2em}
.chordpro.cppaged .cpchord.nowplaying::before{margin-right:-.35em}
.chordpro.cppaged .cpchord.tagged::after{position:static;display:inline-block;vertical-align:1.6em;margin-right:-.3em}
.readerpagebar{display:flex;align-items:center;justify-content:center;gap:.75rem;margin:.4rem 0 .6rem}
.readerpagebar .lbl{font-size:.85rem;color:var(--muted);min-width:6.5em;text-align:center}
.readerpagebar .chip{min-height:40px}
.feedbacksheet textarea.feedbackbody{min-height:8rem;resize:vertical;line-height:1.5;margin:.55rem 0}
.feedbackctx{font-size:.8rem;padding-bottom:.4rem}
.feedbackctx pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:.3rem 0 0;font-size:.75rem}

.chordpop{max-height:calc(100dvh - 16px);overflow-y:auto}
.chordvoicings{display:flex;flex-direction:column;align-items:center;gap:.45rem;text-align:center}
.chordvoicings .chorddiag{margin:.4rem}
.chordvoicings .chorddiagart svg{width:130px;height:150px}
.chordvoicings .dim{font-size:.8rem}
.voicingfrets{font-size:.85rem;font-variant-numeric:tabular-nums}
.chordbanksheet .plbar{flex-wrap:wrap;margin:.75rem 0}
.chordbanksheet > input{width:100%}
.addchartsheet .editor{margin-top:.75rem}

.plpicker{display:flex;flex-direction:column;gap:.4rem;width:100%}
.plpicker .search{width:100%;max-width:none}
.plpickerlist{display:flex;flex-wrap:wrap;gap:.35rem}

/* Practice panel (readerPracticeMenu): one page — track, tools, chart. */
.practiceplay .readerplaypair{display:flex;gap:.5rem}
.practiceplay .readerplaypair>button{flex:1}
.readerquickpop .readerplaysecondary{flex:none!important;width:auto;padding:0 1rem;text-align:center;
  background:var(--panel2);border:1px solid var(--line);border-radius:var(--r)}
.readercommandsection.practiceplay>.readerplayhint{margin:.5rem 0 0;text-align:left}
.practicetrack{display:flex;align-items:center;gap:.6rem;margin-top:.6rem;min-width:0}
.practicetrackname{flex:1;min-width:0;display:flex;flex-direction:column;gap:.1rem;font-size:.86rem}
.practicetrackname>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.readerquickpop .practicechange{flex:none;justify-content:center;padding:0 .8rem;border:1px solid var(--line);background:var(--panel2)}
.practicetrack .linkbar{flex:none;gap:.35rem}
.practicetrack .lnk{display:inline-flex;align-items:center;min-height:2.3rem;padding:0 .6rem;border:1px solid var(--line);
  border-radius:999px;background:var(--panel);color:var(--fg)}
.practicetrack .lnk.ghost{opacity:1;color:var(--muted);border-style:dashed}
.readerquickpop .practicechange{min-height:2.3rem!important}
.readerquickpop .practicechartsec .readerscrollsettings .chip{min-height:2.3rem;padding:0 .8rem;border:1px solid var(--line);
  border-radius:999px;background:var(--panel2);font-size:.85rem}
.practicetoolrow{display:flex;gap:.5rem}
.readerquickpop .practicetool{flex:none;width:4.6rem;height:4.6rem;min-height:0;padding:.35rem .2rem;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:.3rem;text-align:center;background:var(--panel2);
  border:1px solid var(--line2);border-radius:12px;color:var(--fg)}
.readerquickpop .practicetool:hover{border-color:var(--accent)}
.readerquickpop .practicetool.on{background:var(--rndbg);border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.practicetoolicon{font-size:1.35rem;line-height:1}
.practicetoollabel{font-size:.74rem;font-weight:600}
.practicetool.on .practicetoollabel{color:var(--accent)}
.practicerow{display:flex;align-items:center;gap:.75rem;min-height:48px;padding:.35rem 0;border-top:1px solid var(--line2)}
.readercommandsection>h3+.practicerow{border-top:0}
.practicerowtext{flex:1;min-width:0;display:flex;flex-direction:column;gap:.1rem}
.practicerowlabel{font-size:.92rem;font-weight:550}
.practicerow>.practicerowlabel{flex:1}
.practicerowsub{font-size:.78rem;color:var(--muted);line-height:1.3}
.practicerow.disabled .practicerowtext,.practicerow.disabled>.practicerowlabel{opacity:.45}
.practiceswitch{cursor:pointer}
.practiceswitch.disabled{cursor:not-allowed}
input.switch{appearance:none;-webkit-appearance:none;flex:none;position:relative;width:2.7rem;height:1.6rem;margin:0;
  border-radius:999px;background:var(--line);cursor:inherit;transition:background .15s}
input.switch::before{content:"";position:absolute;top:.2rem;left:.2rem;width:1.2rem;height:1.2rem;border-radius:50%;
  background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:transform .15s}
input.switch:checked{background:var(--accent)}
input.switch:checked::before{transform:translateX(1.1rem)}
input.switch:disabled{opacity:.4}
input.switch:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.practiceseg{flex-wrap:wrap}
.practiceseg .toolseg{flex:none}
.readerquickpop .practiceseg .toolseg button{min-height:2.3rem;padding:.3rem .75rem;text-align:center;width:auto}
.stepper{flex:none;display:inline-flex;align-items:center;border:1px solid var(--line);border-radius:999px;background:var(--panel)}
.readerquickpop .stepper button{width:2.6rem;min-height:2.3rem;padding:0;text-align:center;border:0;background:none;
  border-radius:999px;font-size:1.15rem;font-weight:600;color:var(--fg)}
.readerquickpop .stepper button:hover:not(:disabled){background:var(--panel2)}
.stepper .transposeval{min-width:2.4rem;font-size:1rem}
.readerquickpop .transposereset{flex:none;width:auto;min-height:2.3rem;padding:0 .4rem;background:none;border:0;
  color:var(--accent);font-size:.85rem;font-weight:600}
button.tagpill.readertranspose{cursor:pointer;font-family:inherit;color:var(--fg)}
button.tagpill.readertranspose.ghost{color:var(--muted);border-style:dashed}
button.tagpill.readertranspose:disabled{opacity:.45;cursor:not-allowed}
.transposebody{display:flex;flex-direction:column;align-items:center;gap:.7rem;padding:.8rem 0 1rem}
.transposerow{display:flex;align-items:center;gap:.8rem}
.stepper.big button{width:3.4rem;min-height:3rem;padding:0;border:0;background:none;border-radius:999px;font-size:1.5rem;font-weight:600;color:var(--fg);cursor:pointer}
.stepper.big button:hover{background:var(--panel2)}
.stepper.big .transposeval{min-width:3.2rem;font-size:1.35rem}
.transposekey{color:var(--muted);font-size:.95rem}
.origkeyrow{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem;border-top:1px solid var(--line2);padding-top:.7rem}
.origkeypick{display:flex;align-items:center;gap:.4rem;font-size:.9rem}
.origkeypick select{min-height:40px}
.origkeyrow a.chip{min-height:40px;display:inline-flex;align-items:center;text-decoration:none}
.practicechartsec .readerscrollsettings{padding-top:.5rem;border-top:1px solid var(--line2);margin:0}
/* ⋯: a plain dropdown of song-level actions. */
.readermoremenu>summary.icon{display:inline-flex;align-items:center;justify-content:center;min-width:2.5rem;min-height:2.5rem}
.readermoremenu>summary svg{width:20px;height:20px}
.pop.readermorepop{top:calc(100% + .35rem);min-width:15rem;z-index:38}
.readermorepop button{padding:.6rem .7rem;font-size:.92rem}
.readermorepop button:disabled{opacity:.45}
/* A tool still running shows on the Practice button, like Record's dot. */
.readerquickmenu.toolon>summary.icon{position:relative}
.readerquickmenu.toolon>summary.icon::after{content:"";position:absolute;top:.3rem;right:.3rem;width:.5rem;height:.5rem;
  border-radius:50%;background:var(--accent)}
.readercommandsection>button{display:block;width:100%;margin-bottom:.45rem;text-align:left}

.pinstlabel{flex:none;align-self:center;color:var(--muted);font-size:.85rem;margin-right:.15rem}

/* ── pedalboard (MIDI) ─────────────────────────────────────────────── */
.midipill{display:inline-flex;align-items:center;gap:.3rem;border:1px solid var(--line);background:none;color:var(--fg);
  border-radius:999px;padding:.12rem .5rem;margin-right:.35rem;cursor:pointer;font-size:.9rem;min-height:30px}
.midipill[hidden]{display:none}
.midipill.flash,.midireadout.flash{animation:midiflash .7s ease-out}
@keyframes midiflash{from{background:color-mix(in srgb,var(--accent) 38%,transparent)}}
.mdot{display:inline-block;width:.6rem;height:.6rem;border-radius:50%;background:var(--muted);flex:none}
.mdot.live{background:var(--ghit);box-shadow:0 0 0 3px color-mix(in srgb,var(--ghit) 25%,transparent)}
.mdot.wait{background:#d97706}
.midihead{display:flex;align-items:center;gap:.6rem;margin:.5rem 0}
.midihead.big .mdot{width:.85rem;height:.85rem}
.midihead.big b{font-size:1.1rem}
.midiheadtext{min-width:0}
#pedal{max-width:52rem}
#pedal .pcard{margin-bottom:.7rem;padding:.8rem .9rem;border-radius:12px}
.pedaltop{display:flex;align-items:center;gap:.6rem}
.pedaltop .pageheading{margin:.2rem 0 .6rem}
.pedalsetup>summary{cursor:pointer;margin:0}
.midireadout{display:flex;align-items:center;gap:1rem;padding:.75rem .9rem;border-radius:10px;background:var(--panel2);margin:.3rem 0 .5rem}
.midireadout .big{font-size:2.3rem;font-weight:700;font-variant-numeric:tabular-nums;min-width:5rem;text-align:center;line-height:1}
.midireadoutwhat{display:flex;flex-direction:column;align-items:flex-start;gap:.3rem;min-width:0}
.midilog{list-style:none;margin:0;padding:0;font-size:.85rem}
.midilog li{display:flex;gap:.5rem;padding:.25rem 0;border-top:1px solid var(--line2)}
.midilog .t{color:var(--muted);min-width:2.4rem;font-variant-numeric:tabular-nums}
.midilog .dir{width:1rem;text-align:center;color:var(--muted)}
.midilog li.other{opacity:.55}
.midisearch{width:100%;margin:.2rem 0 .5rem}
.midishow{width:fit-content;max-width:100%;margin-bottom:.4rem}
.midiassign{display:flex;align-items:center;justify-content:space-between;gap:.6rem;padding:.5rem .7rem;margin:.2rem 0 .5rem;
  border-radius:8px;background:color-mix(in srgb,var(--accent) 13%,transparent);font-weight:600}
.midilist{display:flex;flex-direction:column}
.midiempty{padding:.6rem 0}
.midirow{display:flex;align-items:center;gap:.4rem .7rem;padding:.5rem 0;border-top:1px solid var(--line2);flex-wrap:wrap}
.midibadge{flex:none;min-width:3.6rem;min-height:38px;text-align:center;padding:.25rem .45rem;border-radius:8px;font:inherit;font-weight:650;
  font-variant-numeric:tabular-nums;cursor:pointer;border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);
  background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent)}
.midibadge:hover{border-color:var(--accent)}
.midibadge.none{background:var(--panel2);color:var(--muted);font-weight:500;border:1px dashed var(--line)}
.midibadge.learning{background:var(--accent);color:var(--accent-fg);border-color:var(--accent);animation:midilearn 1s ease-in-out infinite}
@keyframes midilearn{50%{opacity:.55}}
.midirowinfo{flex:1 1 9rem;min-width:0}
.midirowtitle,.midirowsub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.midirowsub{font-size:.78rem}
.midirowacts{display:flex;align-items:center;gap:.35rem;flex:none}
.midirowacts .chip{min-height:34px}
.midirowacts [hidden]{display:none}
.midisnap{font:inherit;font-size:.85rem;padding:.2rem .3rem;border-radius:var(--r);border:1px solid var(--line);background:var(--panel);color:var(--fg);min-height:34px}
.midirowinput{flex-basis:100%;margin-left:4.3rem;max-width:calc(100% - 4.3rem)}
.midirowinput[hidden]{display:none}
.midisheet .midilist{margin:.3rem 0 .6rem}
@media (max-width:600px){
  .midirowacts{margin-left:auto}
  .midirowinput{margin-left:0;max-width:100%}
}
@media(max-width:520px){
  .musicalfields{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .editornames{grid-template-columns:minmax(0,1fr)}
}

/* Auto-scroll running and nobody touching the screen: the reader cluster
   folds to its Pause button (see fabWake in app.js). Only where the cluster
   floats over the chart — the side rail below has its own column to live in. */
@media not all and (min-width:1024px) and (min-height:640px){
  html.fabidle .readerfab{display:flex;background:transparent;border-color:transparent;box-shadow:none;
    animation:fabfold .25s ease-out}
  html.fabidle .readerfab>:not(#autoscrollbtn){display:none}
  /* Alone, the usual see-through fill was too faint to find mid-song. */
  html.fabidle #autoscrollbtn{background:color-mix(in srgb, var(--accent) 85%, transparent);
    box-shadow:0 2px 8px rgba(0,0,0,.25)}
}
@keyframes fabfold{from{opacity:.4}to{opacity:1}}

/* Wide and tall enough: the cluster becomes a column down the right edge,
   beside the chart rather than on top of it, so it never needs hiding. The
   chart's max-width gives up the rail's width on both sides (staying
   centered), and a right-docked player/tuner/metronome's margin adds it on top. */
@media (min-width:1024px) and (min-height:640px){
  :root{--fabrail:6.5rem}
  html.reading #reader{max-width:min(60rem,calc(100vw - 2 * var(--fabrail)))}
  .readerfab{display:flex;flex-direction:column;align-items:center}
  .readerfabnav{flex-direction:column;border-top:1px solid var(--line);padding-top:.6rem}
  .readertext,.readermaterials .chordpro{padding-bottom:2rem}
  html.playingalong #reader{margin-right:calc(var(--playaw) + 1.2rem + var(--fabrail))}
  html.tunerActive #reader{margin-right:calc(var(--tunerw) + 1.2rem + var(--fabrail))}
  html.metroActive #reader{margin-right:calc(var(--metrow) + 1.2rem + var(--fabrail))}
  /* Not the settings panel: it's open only while adjusting, and taking the
     rail's width too squeezed Fit-to-screen columns down to one. */
}
@media (min-width:1200px) and (min-height:640px){
  :root{--fabrail:7.5rem}
  .readerfab{gap:.4rem}
  .readerfab .readerfabbtn,.readerfabnav .readerfabbtn{width:76px;height:58px}
  .readerfabnav{border-left:0;padding-left:0;gap:.4rem}
}

/* ✨ Fill missing details review */
.detailcard { display: flex; flex-direction: column; gap: 4px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.detailrow { display: flex; align-items: center; gap: 6px; min-height: 32px; }
.detailcard small a { color: inherit; }
.chip.rusty{font-size:.75rem;padding:.1rem .5rem;opacity:.85}
.recheckcard{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin:.6rem 0}
.recheckt{margin:.2rem 0}
/* Bottom-left: clear of the reader header's controls and the FAB's column. */
#curveball{position:fixed;left:1rem;bottom:calc(1rem + var(--safeb));z-index:60;max-width:min(22rem, calc(100vw - 10rem));
  background:var(--accent);color:var(--accent-fg);border-radius:1rem;padding:.7rem 1rem;font-size:1.1rem;font-weight:600;
  box-shadow:0 6px 24px rgba(0,0,0,.25);cursor:pointer}
.memorycues{display:grid;gap:.3rem;margin:.5rem 0}
.memorycue{display:grid;grid-template-columns:6.5rem 1fr;gap:.5rem;overflow-wrap:anywhere}
.recreviewrow{display:grid;grid-template-columns:minmax(0,1fr) auto;justify-items:start;align-items:center;column-gap:.4rem;row-gap:.1rem}
.recreviewrow>.chip{grid-column:2;grid-row:1/span 2}
.recnoteadd{display:flex;flex-direction:column;gap:.4rem;margin:.4rem 0}
.recnoteadd .sbtn{align-self:flex-start}
.recnotes{list-style:none;padding:0;margin:.4rem 0}
.recnotes li{display:flex;gap:.5rem;align-items:baseline;padding:.4rem 0;border-bottom:1px solid var(--line)}
.recnotebody{flex:1;white-space:pre-wrap;min-width:0;overflow-wrap:anywhere}
.chip.recat{font-variant-numeric:tabular-nums;min-width:3.4rem}
.reclinkbox{margin-top:.8rem}
.secrow{display:flex;flex-direction:column;gap:.35rem;padding:.5rem 0;border-bottom:1px solid var(--line2)}
.sechead{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
.sectempo{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem}
.secbar{flex:1 1 6rem;min-width:5rem;height:.5rem}
.secadd{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.5rem}
.secadd input[type=number]{width:7rem}
.secadd input:not([type]){flex:1 1 10rem}
.vrefsum{margin-bottom:.4rem}
.vrefrow{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;margin-bottom:.4rem}
.vrefrow select{flex:1 1 14rem;min-width:0}
.vreftranspose{width:4.5rem}
.vrefnow{min-height:1.3em;margin-top:.3rem}
.vrefget{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.5rem}
.wbadd{display:flex;gap:.4rem;margin:.3rem 0 .6rem}.wbadd input{flex:1}
.wbrow{display:grid;grid-template-columns:1fr auto;gap:.3rem .5rem;align-items:center;padding:.55rem 0;border-bottom:1px solid var(--line2)}
.wbrow .wbtitle{font-weight:600;text-align:left}
.wbrow .wbfacts{grid-column:1/-1}
.wbrow .chip{justify-self:start}
.writestage{display:flex;align-items:center;gap:.5rem}

/* After saving a shared setlist: "Which of these can you already play?" */
.postsavelist{list-style:none;padding:0;margin:.4rem 0}
.postsavelist li{display:flex;flex-wrap:wrap;gap:.3rem .6rem;align-items:center;justify-content:space-between;
  padding:.45rem 0;border-top:1px solid var(--line)}
.postsavename{min-width:0;overflow-wrap:anywhere}
.postsavelist li.dropped .postsavename{text-decoration:line-through;opacity:.6}
.postsavechips{display:flex;gap:.3rem;flex-wrap:wrap}
.capopts{display:flex;flex-direction:column;gap:.4rem;margin-top:.5rem}
.capopts .chip{align-self:flex-start}
.capmeter{display:flex;flex-direction:column;gap:.25rem;margin:.4rem 0}
.capmeterrow{display:grid;grid-template-columns:5.5rem 1fr;gap:.4rem;align-items:center}
.capmeterbar{height:.5rem;border-radius:.25rem;background:var(--line2);overflow:hidden}
.capmeterfill{height:100%;width:0;background:var(--accent);transition:width 60ms linear}
.capmeterfill.hot{background:var(--bad,#c0392b)}
/* Recording analysis page (app.js openRecordingAnalysis): a full-screen
   sheet of four questions. Colour never carries meaning alone (blue is you,
   grey outline the melody, orange ✕ a different note, dashed "?" unmeasured),
   and blue/orange stay apart for colourblind eyes. */
.anpage{--an-voice:#2a5fd0;--an-voice-soft:rgba(42,95,208,.14);--an-melody:#8d95a3;--an-flag:#c25e00;--an-flag-soft:rgba(194,94,0,.13);--an-mono:ui-monospace,Menlo,monospace}
html[data-theme="dark"] .anpage{--an-voice:#79a2ff;--an-voice-soft:rgba(121,162,255,.16);--an-melody:#7f8796;--an-flag:#ff9d4a;--an-flag-soft:rgba(255,157,74,.15)}
.sheetback:has(>.anpage){align-items:stretch}
.sheet.anpage{max-width:none;width:100%;height:100dvh;max-height:none;border-radius:0;border:0;padding:0;display:flex;flex-direction:column;overflow:hidden;background:var(--bg)}
.sheet.anpage:focus{outline:none}
.anpage [hidden]{display:none!important}
.anpage>.sheethead{position:relative;top:auto;margin:0;flex:none;padding:.7rem 3.4rem .6rem 1rem;padding-top:calc(.7rem + env(safe-area-inset-top,0px))}
.anpage>.sheetclose{display:none}
.anscroll{flex:1;min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;padding:16px max(16px,env(safe-area-inset-left,0px)) 28px}
.anwrap{max-width:860px;margin:0 auto;display:grid;grid-template-columns:minmax(0,1fr);gap:16px;min-width:0}
.anwrap h1,.anwrap h2{margin:0;letter-spacing:-.01em;text-wrap:balance}
.anwrap h1{font-size:clamp(1.45rem,5vw,1.9rem);font-weight:800;line-height:1.1}
.anwrap h2{font-size:1.2rem;font-weight:700}
.anwrap p{margin:0}
.annote{font-size:.86rem;color:var(--muted);max-width:68ch}
.anmono{font-family:var(--an-mono);font-variant-numeric:tabular-nums}
.anlabel{font-size:.8rem;font-weight:650;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.anpanel{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;display:grid;grid-template-columns:minmax(0,1fr);gap:12px;min-width:0}
.anpage svg{display:block;width:100%;overflow:visible}
.anpage svg text{font-family:var(--an-mono);font-size:11px;fill:var(--muted)}
.anpage svg text.ansyl{font-family:inherit}
.anpage select,.anseg,.anbpm,.anplay{font:550 .9rem system-ui,sans-serif;color:var(--fg);background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:7px 12px;min-height:40px}
.anpage select{max-width:100%;text-overflow:ellipsis}
.anseg,.anplay{cursor:pointer}
.anseg:disabled{opacity:.45;cursor:not-allowed}
.anseg.primary,.anplay.primary{background:var(--fg);color:var(--panel);border-color:var(--fg)}
.anplay{min-height:36px;padding:5px 11px;font-size:.86rem}
.anbpm{width:7.5em;font-family:var(--an-mono)}
.anlink{all:unset;cursor:pointer;color:var(--an-voice);font-weight:600;text-decoration:underline;text-underline-offset:3px}
.anpage :is(button,select,input,summary):focus-visible{outline:2px solid var(--an-voice);outline-offset:2px}
.antools{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.anstatus{padding:8px 0}
/* header */
.anhead{display:grid;gap:10px}
.anid{display:grid;gap:4px}
.ankey{display:flex;flex-wrap:wrap;gap:6px 12px;align-items:center;border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:var(--panel)}
.ankey b{font-weight:700}
.ansettings{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:0 14px}
.ansettings>summary{cursor:pointer;display:flex;flex-wrap:wrap;gap:4px 12px;align-items:baseline;padding:12px 0;font-weight:650;list-style:none}
.ansettings>summary::-webkit-details-marker{display:none}
.ansettings>summary::before{content:"▸";color:var(--muted)}
.ansettings[open]>summary::before{content:"▾"}
.ansetinner{display:grid;gap:12px;padding-bottom:14px}
.anctl{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.anctl select{flex:1 1 13rem;min-width:0}
.anrefinner{display:grid;gap:6px;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--panel2)}
.anbpmrow{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.anprogress{display:flex;gap:14px;align-items:center;border:1px solid var(--line);border-radius:12px;padding:16px;background:var(--panel)}
.anspin{width:24px;height:24px;border-radius:50%;border:3px solid var(--an-voice-soft);border-top-color:var(--an-voice);animation:anspin .9s linear infinite;flex:none}
@keyframes anspin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.anspin{animation-duration:3s}}
.ansteps{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:.88rem;color:var(--muted);margin:4px 0}
.ansteps .on{color:var(--fg);font-weight:650}
.ansteps .done::before{content:"✓ ";color:var(--an-voice)}
.anstale{display:flex;flex-wrap:wrap;gap:10px;align-items:center;justify-content:space-between;border:1.5px dashed var(--an-flag);border-radius:10px;padding:10px 12px}
.anbody.stale{opacity:.4;pointer-events:none;user-select:none}
/* the four questions */
.anqs{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:12px}
.anq{all:unset;box-sizing:border-box;cursor:pointer;background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;display:grid;gap:9px;align-content:start;min-width:0}
.anq.static{cursor:default}
.anq:not(.static):hover{border-color:var(--an-voice)}
.anq .ask{font-size:.8rem;font-weight:650;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;display:flex;flex-wrap:wrap;justify-content:space-between;gap:4px 8px}
.anq .sure{font:500 .74rem var(--an-mono);text-transform:none;letter-spacing:0}
.anq .ans{font-size:1.12rem;font-weight:700;line-height:1.3}
.anq .more{color:var(--an-voice);font-weight:650;font-size:.9rem}
.anstack{display:flex;gap:3px;height:14px;border-radius:4px;overflow:hidden}
.anstack .on{background:var(--an-voice)}
.anstack .off{background:var(--an-voice-soft);border:1.5px solid var(--an-voice)}
.anstack .wrong{background:repeating-linear-gradient(45deg,var(--an-flag) 0 2px,transparent 2px 5px);border:1.5px solid var(--an-flag)}
.anstack .unclear{border:1.5px dashed var(--an-melody)}
.ankeys{display:flex;flex-wrap:wrap;gap:4px 16px;font-size:.84rem;color:var(--muted)}
.ankeys>span{display:inline-flex;align-items:center;gap:6px}
.ankeys svg{width:auto;flex:none}
.ansince{display:flex;flex-wrap:wrap;gap:6px 16px;align-items:baseline;border:1px solid var(--line);border-radius:12px;padding:12px 14px;background:var(--panel);font-size:.9rem}
.anfoot{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* one question */
.anback{justify-self:start}
.anline{border-top:1px solid var(--line);padding-top:12px;display:grid;gap:6px;min-width:0}
.anwhole{overflow-x:auto;border-top:1px solid var(--line);padding-top:12px}
.anpage .anwhole svg{width:auto;max-width:none}
.anlh{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:baseline}
.anwords{font-weight:600;min-width:0;overflow-wrap:anywhere}
.antag{font:500 .76rem var(--an-mono);color:var(--muted)}
.anbad{font-weight:650;font-size:.84rem;color:var(--an-flag)}
.angood{font-weight:650;font-size:.84rem;color:var(--an-voice)}
.anseg2{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.anseg2 button{font:550 .88rem system-ui,sans-serif;border:0;background:var(--panel);color:var(--fg);padding:7px 12px;min-height:38px;cursor:pointer}
.anseg2 button[aria-pressed="true"]{background:var(--an-voice);color:#fff}
.anmore>summary{cursor:pointer;font-size:.88rem;color:var(--muted);list-style:none;padding:6px 4px}
.anmore>summary::-webkit-details-marker{display:none}
.anmore[open]{flex-basis:100%}
.anhow>summary{cursor:pointer;font-size:.88rem;color:var(--muted)}
.anwrap:focus{outline:none}
.anpage svg .anhit{cursor:pointer}
.anpage svg text.ansyl.on{font-weight:700;fill:var(--fg)}
.annoteinfo{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:center;font-size:.9rem;min-height:38px}
.annoteinfo b{color:var(--an-flag)}
.annotebtns{display:inline-flex;gap:6px;flex-wrap:wrap}
.antakes{display:grid;gap:8px;border:1px solid var(--line);border-radius:12px;padding:12px 14px;background:var(--panel)}
.antakerow{display:flex;gap:6px;overflow-x:auto;padding-bottom:2px}
.antakebar{flex:0 0 64px;display:grid;grid-template-rows:auto 70px auto auto;gap:3px;justify-items:center;padding:4px 2px;border:1px solid transparent;border-radius:8px;background:none;color:var(--fg);font:500 .74rem system-ui,sans-serif;cursor:pointer}
.antakebar.me{border-color:var(--an-voice);cursor:default;opacity:1}
.antakeval{font-weight:650;font-size:.84rem}
.antakebar.pending{color:var(--muted)}
.antakebar.pending .antakeval{font-weight:500;font-size:.66rem;line-height:1.1;text-align:center}
.antakebar.pending .antakecol{background:none;border:1px dashed var(--line)}
.antakecol{width:22px;height:70px;display:flex;align-items:flex-end;border-radius:4px;background:var(--an-voice-soft)}
.antakecol>span{width:100%;border-radius:4px;background:var(--an-voice)}
.antakewhen{color:var(--muted);text-align:center;line-height:1.2}
.antakeme{font-weight:650;color:var(--an-voice)}
.anhow p{font-size:.88rem;color:var(--muted);max-width:68ch;margin-top:6px}
/* guitar layer */
.anguitar{display:grid;gap:8px}
.ansummary{font-weight:550}
.anbars{display:flex;flex-wrap:wrap;gap:3px;align-items:stretch}
.anbarsec{flex-basis:100%;font-size:.78rem;font-weight:650;color:var(--muted);margin-top:6px}
.anbar{min-width:34px;height:40px;padding:2px 3px;border:1.5px solid var(--an-voice);border-radius:4px;background:var(--an-voice-soft);cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:1px;font:600 .74rem system-ui,sans-serif;color:var(--fg)}
.anbar .got{font-weight:700;color:var(--an-flag);white-space:nowrap}
.anbar.chord{border-color:var(--an-flag);background:var(--an-flag-soft)}
.anbar.dropout{border-style:dashed;border-color:var(--an-melody);background:none;color:var(--muted)}
.anbar{position:relative}
.anbar.now{outline:2.5px solid var(--fg);outline-offset:1px}
.anbarhead{position:absolute;top:-4px;bottom:-4px;width:2px;margin-left:-1px;background:var(--fg);pointer-events:none}
.anends{justify-content:space-between}
.anline svg text.anoff{fill:var(--an-flag);font-weight:650;paint-order:stroke;stroke:var(--panel);stroke-width:3px}
.antakehead{display:flex;flex-wrap:wrap;gap:6px 12px;align-items:center;justify-content:space-between}
.anseg2.small button{font-size:.78rem;padding:5px 9px;min-height:32px}
.antakemove{font-size:.72rem;color:var(--muted);text-align:center;line-height:1.2}
.anplay.playing{border-color:var(--an-voice)}
/* the pinned player */
.anplayer{flex:none;background:var(--panel);border-top:1px solid var(--line);box-shadow:0 -6px 18px rgba(0,0,0,.08);
  padding:8px max(16px,env(safe-area-inset-left,0px)) calc(8px + env(safe-area-inset-bottom,0px))}
.anbin{max-width:860px;margin:0 auto;display:grid;gap:6px}
.anb1{display:flex;flex-wrap:wrap;gap:2px 10px;align-items:baseline;font-size:.84rem;min-width:0}
.anprov{color:var(--muted)}
.anb2{display:grid;grid-template-columns:auto auto 1fr auto auto;gap:8px;align-items:center}
.antb{all:unset;cursor:pointer;width:38px;height:38px;border-radius:50%;display:grid;place-items:center;border:1px solid var(--line);font-size:.95rem;color:var(--fg)}
.antb.main{background:var(--fg);color:var(--panel);border-color:var(--fg)}
.antb[aria-pressed="true"]{background:var(--an-voice);color:#fff;border-color:var(--an-voice)}
.antime{font:.78rem var(--an-mono);color:var(--muted);white-space:nowrap}
.anopts{display:flex;flex-wrap:wrap;gap:6px 14px;align-items:center;font-size:.82rem;color:var(--muted)}
.anopts label{display:flex;gap:6px;align-items:center}
.anopts select{padding:3px 6px;font-size:.82rem;min-height:0}
.anscrub{-webkit-appearance:none;appearance:none;width:100%;background:transparent;height:24px;margin:0;min-width:0}
.anscrub::-webkit-slider-runnable-track{height:8px;border-radius:4px;background:var(--trk,var(--line))}
.anscrub::-moz-range-track{height:8px;border-radius:4px;background:var(--trk,var(--line))}
.anscrub::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:var(--fg);margin-top:-4px;border:2px solid var(--panel)}
.anscrub::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:var(--fg);border:2px solid var(--panel)}
.anvol{width:90px}
@media (max-width:420px){.antime{display:none}.anb2{grid-template-columns:auto auto 1fr auto}}
/* Print report: the detail of every question, nothing interactive. */
@media print{
  body:has(.anpage.anprinting)>*:not(.sheetback){display:none!important}
  html:has(.anpage.anprinting),body:has(.anpage.anprinting){overflow:visible!important;height:auto!important}
  .sheetback:has(>.anpage.anprinting){position:static;background:none;display:block}
  .sheet.anpage.anprinting{height:auto;overflow:visible;display:block;border:0;box-shadow:none}
  .anprinting .anscroll{overflow:visible;padding:0}
  .anprinting :is(.anplayer,.sheethead,.ansettings,.antools,.anback,.anseg2,.antakes,.anstale,.annoteinfo){display:none!important}
  .anprinting .anpanel{break-inside:auto;border:0;padding:0}
  .anprinting .anline{break-inside:avoid}
  .anprinting .anhow[open] p,.anprinting .anhow p{display:block}
}
.tagpill .plgo{background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}
.loadoutrow{display:flex;align-items:center;gap:.4rem}
.loadoutrow .loadoutpick{flex:1;text-align:left;min-height:2.4rem}
.loadoutcur{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem}
.loadoutcur .chip{min-width:2.4rem;min-height:2.4rem}
.loadouttr{min-width:2rem;text-align:center;font-weight:600}
.loadoutsave{display:flex;gap:.4rem}
.loadoutsave .tin{flex:1;min-width:0}
.songlists{display:flex;flex-wrap:wrap;gap:.4rem}
.orgdumpbtn{margin-bottom:.6rem}
textarea.orgdump{width:100%;box-sizing:border-box;margin:.6rem 0;font:inherit;font-size:.9rem}
.orgdumpchart pre{max-height:16rem;overflow:auto;white-space:pre-wrap;font-size:.8rem;margin:.3rem 0 .5rem 1.6rem}
.orgdumpdone{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}
.orgdumpdone p{flex-basis:100%;margin:.4rem 0}

/* Open mics (openOpenMicSheet) */
.plrow.ompast{opacity:.55}
.omgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(8.5rem,1fr));gap:.5rem}
.omsheetbody{display:flex;flex-direction:column;gap:.55rem}
.omsheetbody .sec{margin:0}
.omstatus{margin:0}
.omh{margin:.6rem 0 0;font-size:.95rem}
.omrow,.omback{padding:.5rem 0;border-bottom:1px solid var(--line)}
.omhead{display:flex;flex-wrap:wrap;align-items:baseline;gap:.25rem .5rem}
.omn{color:var(--accent);font-variant-numeric:tabular-nums;min-width:1.6rem}
.omctl{margin-left:auto;display:flex;gap:.15rem}
.omctl .icon{min-width:2.2rem;min-height:2.2rem}
.omdet{font-size:.85rem;margin-top:.15rem;overflow-wrap:anywhere}
.omnote{font-style:italic}
.omwalk{display:flex;gap:.5rem}
.omwalk .search{flex:1;min-width:0}
.omsheetbody .sec>.opts{flex-direction:column;flex-wrap:nowrap;align-items:stretch;gap:.5rem}
.omsheetbody .omgrid .goalfield .search{min-width:0}
.omhead>.sbtn{margin-left:auto;flex:none;padding:.3rem .7rem}
.omlinklabel{font-size:.8rem}
.ommeter{display:flex;flex-direction:column;gap:.3rem;padding:.6rem;border:1px solid var(--line);border-radius:10px}
.ombar{height:.5rem;border-radius:999px;background:var(--line);overflow:hidden}
.ombar>span{display:block;height:100%;background:var(--accent);border-radius:inherit}
.omready{font-weight:600;color:var(--accent)}
.omdecided{font-weight:600;align-self:center}.omdecided.on{color:var(--accent)}
.omneed{color:var(--bad)}
.omback.full>strong{color:var(--accent)}

/* Run a section (D4): read from a music stand, a metre away. */
.drpage .drscroll{flex:1;overflow:auto;padding:0 max(1rem,calc(50% - 26rem)) calc(1rem + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:.7rem}
.drctl{display:flex;flex-wrap:wrap;gap:.45rem;align-items:center}
.drctl select{max-width:100%}
.drtempo{display:inline-flex;align-items:center;gap:.35rem}
.drauto{display:inline-flex;align-items:center;gap:.3rem}
.drsub{font-size:.85rem}
.drwords{margin:0;font-size:1.05rem;line-height:1.45;max-width:68ch}
.drbig{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);padding:1.2rem 1rem;text-align:center;min-height:13rem;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:.45rem;cursor:pointer;user-select:none}
.drhead{font-size:1.8rem;font-weight:700;line-height:1.15}
.drbig{min-height:8rem;padding:.9rem 1rem}
.drpage .drscroll>*{flex:none}
.drcount{font-size:5rem}
.drphase{font-size:1rem;color:var(--muted)}
.drline{font-size:1.05rem;max-width:34rem}
.drgo{font-size:1.35rem;padding:.75rem 2.2rem;margin-top:.4rem;background:var(--accent);border:1px solid var(--accent);color:var(--accent-fg);border-radius:var(--r);font-weight:600}
.drworst{font-weight:600;margin-bottom:.4rem}
.drgrid{overflow-x:auto;padding-bottom:.3rem}
.drrow{display:flex;gap:2px;align-items:center;font-family:var(--an-mono);font-size:1rem;width:max-content}
.drsyl{font-size:.68rem;color:var(--muted)}
.drn{flex:none;width:3.2rem;text-align:left}
.drcell{flex:none;width:1.6rem;text-align:center;overflow:hidden;white-space:nowrap}
.drcell.on,.drcell.yours{color:var(--an-voice)}
.drcell.off,.drcell.wrong{color:var(--an-flag);font-weight:700}
.drcell.missed,.drcell.unclear{color:var(--an-melody)}
.drcurve svg{display:block;width:100%}
.drcurve svg text{font-family:var(--an-mono);font-size:11px;fill:var(--muted)}
.drkey{font-size:.8rem;margin-top:.2rem}
.drk-v{color:var(--an-voice)}.drk-t{color:var(--fg);opacity:.6}.drk-p{color:var(--muted)}.drk-m{color:var(--an-melody)}
.drrow.sel{background:var(--an-voice-soft);border-radius:6px}
.drrow:not(.drsyl){cursor:pointer}
.drsave{display:flex;flex-wrap:wrap;gap:.45rem;align-items:center}
/* Your recordings (openRecordingsManager) */
.recmgr{--an-voice:#2a5fd0}
html[data-theme="dark"] .recmgr{--an-voice:#79a2ff}
.recmgrtools{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;margin:.4rem 0 .6rem}
.recmgrall{display:inline-flex;align-items:center;gap:.3rem;font-size:.9rem}
.recmgrlist{display:grid;gap:2px}
.recmgrrow{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:.3rem .6rem;align-items:center;padding:.45rem .3rem;border-bottom:1px solid var(--line)}
.recmgrrow.sel{background:rgba(42,95,208,.12)}
.recmgrtitle{overflow-wrap:anywhere}
.recmgrmeta{font-size:.82rem}
.recmgracts{display:flex;gap:.3rem}
.recmgrplayer{grid-column:1/-1;width:100%;max-height:50vh}
