/* Settings drawer: slides in over the stage from the right, sized so the keyboard stays
   visible underneath — a player changing transpose or colours wants to see the change land,
   not a modal over the top of it. */
.settings {
  position: absolute; top: var(--header-h); right: 0; bottom: 0; z-index: 20;
  width: 330px; max-width: 92vw;
  display: flex; flex-direction: column;
  color: var(--pink-soft);
  background: #1d0f17;
  border-left: 1px solid rgba(255,255,255,.09);
  box-shadow: -24px 0 48px rgba(0,0,0,.42);
  /* data-open drives it, not `hidden` — a display:none panel cannot animate. The visibility
     flip is what takes it out of the tab order and off the a11y tree once it has gone.
     It is a plain fade. It used to slide 330px in 220ms on an eased curve, and a panel that
     large arriving that fast is a gesture the content does not earn — the drawer is where you
     go to nudge a tempo, not an event. A fade at 120ms reads as "it is there now" and nothing
     else. */
  opacity: 0; visibility: hidden;
  transition: opacity 120ms ease, visibility 0s linear 120ms;
  /* Not a speculative hint. The transition promotes the panel to its own layer for the
     duration and drops it at the end, and each promote/demote re-renders the text between
     subpixel and greyscale antialiasing — a visible snap at both ends. Holding the layer is
     what removes it. Its partner is the rule above: no `backdrop-filter` here, because a
     backdrop blur is re-sampled every frame over a waterfall that is itself animating, and
     under this fill it bought a ghost you could barely measure. */
  will-change: opacity;
}
.settings[data-open] { opacity: 1; visibility: visible; transition-delay: 0s, 0s; }

/* The head doubles as the sub-page title bar: back on the left, the category's name in the
   middle, close on the right. A sub-page therefore costs no chrome of its own. */
.settings-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,.08);
}
.settings-head h2 { flex: 1; margin: 0; font-size: var(--text-lg); font-weight: 800; letter-spacing: -.01em; color: #fff; }
.icon-btn {
  width: 30px; height: 30px; display: grid; place-items: center;
  border: 1px solid rgba(255,255,255,.14); border-radius: 9px;
  color: var(--pink-soft); background: rgba(255,255,255,.05); cursor: pointer;
}
.icon-btn:hover { background: rgba(255,255,255,.12); }
.icon-btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }

/* Full-bleed rows on the menu, so padding lives on the children rather than the scroller. */
.settings-body { flex: 1; min-height: 0; overflow-y: auto; }
.settings section { padding: 14px 16px 20px; }

/* The root view: a couple of controls inline, then the category list. The split is by how
   often you touch a thing, not by what kind of thing it is — tempo and transpose get reached
   for mid-practice, everything below gets set once. */
.settings-inline { padding: 10px 16px 14px; border-bottom: 1px solid rgba(255,255,255,.07); }

/* The category list. One row per section[data-label], built in settings.js. */
.settings-menu { display: flex; flex-direction: column; }
.menu-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 15px 16px; cursor: pointer; text-align: left;
  color: var(--pink-soft); background: none; border: 0;
  border-bottom: 1px solid rgba(255,255,255,.07);
  font-size: var(--text-md); font-weight: 700;
  transition: background 140ms ease;
}
.menu-row:hover { background: rgba(255,255,255,.06); }
.menu-row span { flex: 1; }
/* The leading icon is the category's identity, the trailing chevron is affordance — so the
   icon sits at label brightness and the chevron stays muted with the value beside it. */
.menu-row > svg:first-child { stroke: var(--pink-soft); }
/* The current value, so the list says what is set without opening anything. */
.menu-row b { color: #a9829a; font-weight: 700; font-variant-numeric: tabular-nums; }
.menu-row svg { width: 15px; height: 15px; flex: none; fill: none; stroke: #a9829a; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
/* The soundfont list. Full-bleed rows the way the category menu is, so the page reads as one
   list rather than a card floating in padding — hence the negative margin off section padding. */
.choices { display: flex; flex-direction: column; margin: -4px -16px 12px; }
.choice {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px; cursor: pointer;
  font-size: var(--text-md); font-weight: 700;
  transition: background 140ms ease;
}
.choice:hover { background: rgba(255,255,255,.06); }
/* A whole group can be a readout rather than a control (the lab's Mode). Nothing to hover,
   nothing to press — say so, or a row that ignores the click reads as broken. */
.choice:has(:disabled) { cursor: default; }
.choice:has(:disabled):hover { background: none; }
.choice span { flex: 1; }
/* The radio itself is the hit target and the focus anchor; the tick is what you actually see. */
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice svg { width: 15px; height: 15px; flex: none; fill: none; stroke: var(--pink-light); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; opacity: 0; }
/* A leading glyph is identity, the trailing tick is state — so the glyph is always on, at
   label brightness, the way a category row carries its own icon. The rule above hides every
   svg in a row until it is checked, which is right for the tick and wrong for this. */
.choice .glyph { opacity: 1; stroke: var(--pink-soft); }
.choice:has(:checked) { color: #fff; }
.choice:has(:checked) svg { opacity: 1; }
.choice:has(:focus-visible) { outline: 3px solid #ffd2e5; outline-offset: -3px; }

/* Segmented variant, for a list short enough to see at once — channel focus is the score's
   channels plus "all", two to four values that are read side by side rather than scanned.
   Same .choice rows, so the radio, the focus ring and the summary read-back are untouched and
   nothing in JS knows about this: only the arrangement changes.
   The trailing tick goes, because the filled segment IS the tick — and the fill is the row's
   own --hand, the same mapping the ring uses, so a channel is still named in the colour its
   paws strike in. "All" has no hand and falls back to plain light. The glyph sits ABOVE the
   label: side by side, a 15px ring plus "All channels" does not fit a third of a 330px panel,
   and shrinking the label to fit is how a control becomes unreadable. */
.choices.group { flex-direction: row; gap: 4px; margin: -4px 0 12px; padding: 4px; background: rgba(255,255,255,.05); border-radius: 12px; }
.choices.group .choice { flex: 1; min-width: 0; flex-direction: column; justify-content: center; gap: 5px; padding: 9px 4px; border-radius: 9px; text-align: center; font-size: var(--text-sm); }
.choices.group .choice span { flex: none; }
.choices.group .choice > svg:not(.glyph) { display: none; }
.choices.group .choice:has(:checked) { background: color-mix(in srgb, var(--hand, #fff) 20%, transparent); }

.row { display: grid; grid-template-columns: 8.5ch 1fr auto; align-items: center; gap: 10px; padding: 5px 0; font-size: var(--text-md); font-weight: 700; }
.row b { font-variant-numeric: tabular-nums; min-width: 4.5ch; text-align: right; color: #fff; font-weight: 800; }
/* A switch row has nothing in the middle column; let the label take the slack. */
.row.wide { grid-template-columns: 1fr auto; }

.settings input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 100%; height: 4px;
  border-radius: 2px; background: rgba(255,255,255,.18); cursor: pointer;
}
.settings input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%;
  background: var(--pink-light); border: 2px solid #23121c; cursor: pointer;
  box-shadow: 0 0 0 1px rgba(255,145,191,.45);
}
.settings input[type="range"]::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%; border: 2px solid #23121c;
  background: var(--pink-light); cursor: pointer;
}
.settings select {
  width: 100%; height: 32px; padding: 0 8px; cursor: pointer;
  color: var(--pink-soft); background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.14); border-radius: 9px;
  font-size: var(--text-md); font-weight: 700;
}
/* Minus / value / plus in a bordered pill — the shape a DAW or a metronome uses, and the reason
   tempo is a stepper rather than the slider it was: a player wants 96 BPM, not "about four
   fifths of the written tempo". */
.stepper-box {
  display: flex; align-items: center; height: 32px;
  border: 1px solid rgba(255,255,255,.14); border-radius: 9px;
  background: rgba(255,255,255,.05);
}
.stepper-box button {
  width: 30px; height: 100%; padding: 0; cursor: pointer;
  display: grid; place-items: center;
  color: var(--pink-soft); background: none; border: 0;
  font-size: 15px; font-weight: 700; line-height: 1; border-radius: 9px;
  transition: background 140ms ease, color 140ms ease;
}
.stepper-box button:hover:not(:disabled) { background: rgba(255,255,255,.12); }
.stepper-box button:disabled { color: #6d4a5c; cursor: default; }
.stepper-box b {
  min-width: 4.5ch; text-align: center; color: #fff;
  font-size: var(--text-md); font-weight: 800; font-variant-numeric: tabular-nums;
}

/* A switch, not a checkbox: these two are modes the app is in, not items on a list, and a
   pill that slides reads as "on/off" at a glance where a tick reads as "selected". */
.switch {
  -webkit-appearance: none; appearance: none;
  position: relative; width: 40px; height: 23px; flex: none; cursor: pointer;
  border: 0; border-radius: 999px; background: rgba(255,255,255,.16);
  transition: background 160ms ease;
}
.switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 17px; height: 17px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
  transition: transform 160ms cubic-bezier(.3,1.4,.5,1);
}
.switch:checked { background: var(--pink); }
.switch:checked::after { transform: translateX(17px); }
.switch:focus-visible { outline: 3px solid #ffd2e5; outline-offset: 2px; }

/* Within-page grouping, now that Appearance holds two things. */
.settings h3 {
  margin: 18px 0 8px; font-size: var(--text-sm); font-weight: 800;
  letter-spacing: .09em; text-transform: uppercase; color: #a9829a;
}
.settings section > h3:first-child { margin-top: 0; }

/* Three presets plus a free pick, per hand — the presets are there because most players want
   a legible pair in one click, and the picker is there because "channel colour" means any
   colour. Everything else in the app (black-key tile, lit key, plume, anticipation pulse) is
   derived from whichever one lands here. */
.swatches { display: flex; align-items: center; gap: 8px; padding: 4px 0 10px; }
.swatches > span { font-size: var(--text-md); font-weight: 700; min-width: 5ch; }
.swatch {
  width: 26px; height: 26px; padding: 0; cursor: pointer; border-radius: 8px;
  border: 2px solid transparent; background: var(--tone);
  box-shadow: 0 0 0 1px rgba(0,0,0,.35);
}
.swatch[aria-pressed="true"] { border-color: #fff; }
.swatches input[type="color"] {
  width: 26px; height: 26px; padding: 0; cursor: pointer;
  background: none; border: 1px solid rgba(255,255,255,.3); border-radius: 8px;
}
.swatches input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.swatches input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 5px; }

.settings input[type="file"] { display: none; }

/* The picker is the preview. Two buttons reading "Choose image" / "Remove" said nothing about
   what was chosen — you had to close the panel to find out. The thumb paints --bg-image
   straight off the root in a ::after, so the preview costs no JS and no second copy of the data
   URL; with no image the var is `none`, nothing paints, and the placeholder glyph shows through.
   Remove only exists when there is something to remove. */
.bg-picker { display: flex; gap: 8px; align-items: center; padding: 4px 0 8px; }
.bg-choose {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
  padding: 7px; cursor: pointer; text-align: left;
  color: var(--pink-soft); background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.14); border-radius: 12px;
  transition: background 140ms ease, border-color 140ms ease;
}
.bg-choose:hover { background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.26); }
.bg-thumb {
  position: relative; flex: none; width: 44px; height: 34px; display: grid; place-items: center;
  border-radius: 8px; background: rgba(0,0,0,.3); box-shadow: inset 0 0 0 1px rgba(255,255,255,.1);
}
.bg-thumb svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; opacity: .7; }
.bg-thumb::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background-image: var(--bg-image); background-size: cover; background-position: center;
}
.bg-copy { min-width: 0; display: grid; gap: 1px; }
.bg-copy b { color: #fff; font-size: var(--text-md); font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bg-copy small { font-size: var(--text-sm); font-weight: 700; color: #a9829a; }

/* Opacity means nothing with no image behind it, so it goes quiet rather than lying about
   being live. Contrast stays above the 4.5:1 floor for the label that is still readable. */
.row:has(input[type="range"]:disabled) { opacity: .45; }
