/* Self-hosted (not Google Fonts CDN) so loading the app never sends a
   visitor's IP to Google just to render the header title. */
@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/playfair-display-bold-latinext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/playfair-display-bold-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Sampled directly from icons/logo.png (the choir's own mark), not
     picked to taste -- accent takes the logo's navy ("BEMOWO"), danger and
     pause take its red (clef/"JP2") and gold (flame/"CHÓR") respectively.
     --play stays green: no logo equivalent, and green-for-"go" is a
     stronger, more universal signal than forcing a brand color onto it. */
  --accent: #0d3465;
  --accent-dark: #092549;
  --danger: #cd191b;
  --play: #2fb350;
  --pause: #f9b415;
  --bg: #f5f5f7;
  --surface: #fff;
  --border: #e2e2e5;
  --text: #1d1d1f;
  --text-muted: #6e6e73;
  --text-faint: #aaa;
  --radius: 10px;
  --radius-sm: 7px;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 6px 16px rgba(0, 0, 0, 0.05);
}

/* --- Splash: shown on every cold load, held briefly by app.js's setTimeout
   then faded into the library underneath (see index.html). Fixed/full-
   viewport so it fully covers the library while up, regardless of how far
   that page has already rendered underneath it. */
.splash-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  padding: 48px;
  opacity: 1;
  transition: opacity 0.5s ease;
}

.splash-screen.fade-out {
  opacity: 0;
  pointer-events: none;
}

.splash-logo {
  width: 100%;
  max-width: min(420px, 78vw);
  height: auto;
}

@media (prefers-reduced-motion: reduce) {
  .splash-screen {
    transition: none;
  }
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html {
  overflow-x: hidden;
}

/* Without this, body's height is just "auto" -- sized to whatever's in
   normal document flow. Whenever that's shorter than the screen (e.g. an
   empty library with no songs yet), body's own box ends early, and setting
   overflow-x here makes the browser auto-compute overflow-y as "auto" too
   (a CSS quirk: you can't mix overflow:visible on one axis with a clipping
   value on the other). That combination silently clips anything absolutely
   positioned past body's short box -- including the Settings panel, which
   extends below the header -- while the *fixed*-position backdrop behind it
   keeps covering the full screen regardless, since fixed elements ignore an
   ancestor's overflow clipping. The visible symptom: the panel's lower
   sections (Zarządzanie/login) look like they vanish into the backdrop with
   no way to scroll to them. Keeping body at least a full screen tall avoids
   the whole situation. */
body {
  margin: 0;
  overflow-x: hidden;
  min-height: 100vh;
  min-height: 100dvh;
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  padding-bottom: env(safe-area-inset-bottom);
}

header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 14px 20px;
  padding-top: max(14px, env(safe-area-inset-top));
  padding-left: max(20px, env(safe-area-inset-left));
  padding-right: max(20px, env(safe-area-inset-right));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

header h1 {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
  min-width: 0;
}

/* Matches the logo's own lettering (a display serif) and its color story --
   gold flame fading through red into the navy of "BEMOWO" -- rather than
   plain text, since the header is the one place the brand mark itself sits. */
.app-title-text {
  font-family: "Playfair Display", Georgia, "Times New Roman", serif;
  font-weight: 700;
  background: linear-gradient(90deg, var(--pause), var(--danger), var(--accent));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.app-logo {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
}

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

.icon-btn {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 1.1rem;
  line-height: 1;
  padding: 8px 12px;
  min-width: 42px;
  min-height: 42px;
  touch-action: manipulation;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.icon-btn[aria-expanded="true"] {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* Slides down from behind the header and sits above the rest of the page
   (see .settings-backdrop) instead of pushing content down in normal flow,
   so opening it focuses attention on just this panel. Anchored via
   top:100% on the header (a positioned ancestor) rather than a fixed pixel
   offset, so it lines up correctly regardless of the header's own height
   (which varies with safe-area insets on notched phones). */
.settings-panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 20px;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
  background: #fafafc;
  border-bottom: 1px solid var(--border);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
  /* vh is based on the largest possible viewport (address bar hidden) on
     mobile browsers, so it overstates how much is actually visible whenever
     the address bar is showing -- the panel could then render taller than
     the real screen, pushing the Zarządzanie section below the reachable
     area. dvh tracks the *current* visible viewport instead; the plain vh
     line stays first as a fallback for browsers without dvh support. */
  max-height: 80vh;
  max-height: 80dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  transform: translateY(-100%);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.25s ease, opacity 0.25s ease, visibility 0s linear 0.25s;
}

.settings-panel.open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  transition: transform 0.25s ease, opacity 0.25s ease, visibility 0s linear 0s;
}

.settings-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 700;
  font-size: 1rem;
  margin-bottom: 4px;
}

/* Separates song-specific controls (Tempo, Loop) from general/app-level
   ones (Playlist, Instrument) so it's clear why the former only appear
   once a song is actually loaded, instead of just looking inconsistent. */
.settings-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.settings-group[hidden] {
  display: none;
}

.settings-group-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-faint);
}

/* Dims the rest of the page while settings is open, and doubles as a
   tap-to-close target -- standard modal/overlay convention. */
.settings-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  z-index: 9;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}

.settings-backdrop.open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.25s ease, visibility 0s linear 0s;
}

.settings-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 320px;
}

/* Needed because .settings-field's own display:flex (an author rule) would
   otherwise beat the native [hidden] UA rule regardless of specificity. */
.settings-field[hidden] {
  display: none;
}

.settings-field-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 28px;
  background: transparent;
  touch-action: manipulation;
}

input[type="range"]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: var(--border);
}

input[type="range"]::-moz-range-track {
  height: 6px;
  border-radius: 3px;
  background: var(--border);
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px;
  height: 22px;
  margin-top: -8px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #fff;
  box-shadow: var(--shadow);
  cursor: pointer;
}

input[type="range"]::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid #fff;
  box-shadow: var(--shadow);
  cursor: pointer;
}

#instrument-select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 0.95rem;
  cursor: pointer;
  touch-action: manipulation;
  min-height: 42px;
  width: 100%;
}

.loop-controls {
  display: flex;
  gap: 8px;
}

.loop-btn {
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-weight: 700;
  touch-action: manipulation;
}

.loop-btn.active {
  background: var(--accent);
  border-color: var(--accent-dark);
  color: #fff;
}

#loop-clear-btn {
  min-width: 42px;
  min-height: 42px;
}

/* --- Library screen: browse/search world. Songs, composers, playlists --
   nothing plays here, picking a song hands off to the Player screen. --- */

.library-search {
  max-width: 1000px;
  margin: 20px auto 0;
  padding: 0 20px;
}

.library-search-input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0 16px;
  font-size: 1.05rem;
  color: var(--text);
  min-height: 52px;
}

.library-search-input::-webkit-search-cancel-button {
  cursor: pointer;
}

.library-section-label {
  max-width: 1000px;
  margin: 24px auto 10px;
  padding: 0 20px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.playlist-tiles {
  max-width: 1000px;
  margin: 20px auto 0;
  padding: 0 20px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}

.playlist-tiles[hidden] {
  display: none;
}

/* A plain container, not a button -- like .song-row, it now holds two
   independent buttons (toggle the filter / open the "⋮" playlist actions),
   which a <button> can't nest without breaking click handling. */
.playlist-tile {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  touch-action: manipulation;
}

.playlist-tile.active {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}

.playlist-tile-main {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 14px 30px 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Small and transparent, tucked into the card's own corner rather than
   competing for width in an already-narrow (150px) tile. */
.playlist-tile-kebab {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  min-width: 26px;
  min-height: 26px;
  padding: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-faint);
}

.pt-name {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text);
}

.playlist-tile.active .pt-name {
  color: var(--accent-dark);
}

.pt-count {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
}

.song-list {
  max-width: 1000px;
  margin: 0 auto;
  padding: 0 20px 24px;
  display: flex;
  flex-direction: column;
}

/* A plain row container, not a button -- it holds two independent buttons
   (open the song / open its "⋮" materials sheet) side by side, and a
   <button> can't nest another <button> without breaking click handling and
   accessibility semantics. The border-bottom divider lives here so it still
   spans the full row width including alongside the kebab. */
.song-row {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  border-bottom: 1px solid var(--border);
}

.song-row-main {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
  text-align: left;
  background: none;
  border: none;
  padding: 14px 4px;
  touch-action: manipulation;
}

/* Position within the *currently displayed* list (search/playlist filter
   included) -- matches the same numbering the playlist print/open view
   already uses. Fixed width so titles still line up regardless of whether
   a number is 1 or 2 digits. */
.song-row-number {
  flex-shrink: 0;
  width: 1.6em;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* Only rendered for a song that actually has a PDF/cover/recording (see
   songHasMaterials() in app.js) -- most of the catalog shows no kebab at
   all, exactly as before this feature existed. */
.song-row-kebab {
  flex-shrink: 0;
  margin-right: 4px;
}

.song-row-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.song-row-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.song-row-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 1px 0;
}

.tag-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--accent-dark);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  white-space: nowrap;
}

.song-row-composer {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.song-row-chevron {
  color: var(--text-faint);
  font-size: 1.1rem;
  flex-shrink: 0;
}

.song-list-empty {
  color: var(--text-muted);
  padding: 14px 4px;
  margin: 0;
}

/* --- Player screen back row: replaces the old song picker entirely -- the
   library is now the only place a song gets chosen. --- */

.player-back-row {
  max-width: 1000px;
  margin: 20px auto 0;
  padding: 0 20px;
  display: flex;
  align-items: center;
  gap: 14px;
}

.back-to-library-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--accent);
  font-size: 0.95rem;
  font-weight: 600;
  padding: 8px 0;
  touch-action: manipulation;
}

.now-playing-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  text-align: right;
  flex: 1;
}

/* Transpose selector: picks the actual key name (tonacja) to sing in --
   easier for a maestro to reason about than a raw semitone count. A small
   pill next to the title/composer; gets an accent tint whenever a key
   other than the original is chosen, so a transposition is never silently
   invisible. */
.transpose-select-wrap {
  position: relative;
  flex-shrink: 0;
}

.transpose-select-wrap::after {
  content: "";
  position: absolute;
  right: 9px;
  top: 50%;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid var(--text-muted);
  border-bottom: 1.5px solid var(--text-muted);
  transform: translateY(-65%) rotate(45deg);
  pointer-events: none;
}

.transpose-select {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  font-size: 0.86rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 24px 6px 12px;
  max-width: 128px;
  text-overflow: ellipsis;
}

.transpose-select.active {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}

/* --- Mixer panel: the mute/practice-select rows, collapsed by default so
   it never costs the reading area a permanent row -- toggled by the 🎚️
   icon in the playback bar (see setVoiceMixExpanded() in app.js, which
   also re-syncs the body's reserved space and, if Tekst is the active
   view, its height). --- */

.voice-mix-sheet {
  display: flex;
  flex-direction: column;
}

.voice-mix-sheet[hidden] {
  display: none;
}

/* max-height (not display:none) is what actually shows/hides this panel --
   see animateExpandablePanel() in app.js -- so opening/closing slides
   smoothly instead of flickering in and out instantly. */
.voice-mix-panel {
  max-width: 1000px;
  width: 100%;
  margin: 0 auto;
  padding: 0 20px;
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.28s ease;
}

/* --- Repeat-whole-song panel: same collapsed-by-default/expand pattern as
   the mixer above, triggered by the repeat icon next to Play instead. --- */

.repeat-panel {
  max-width: 1000px;
  width: 100%;
  margin: 0 auto;
  padding: 0 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.28s ease;
}

.repeat-options {
  display: flex;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px;
  gap: 4px;
  /* Same reasoning as .tracks-panel's margin: keeps .repeat-panel itself
     free to reach a true zero height when collapsed. */
  margin: 14px 0 10px;
}

.repeat-option {
  flex: 1;
  text-align: center;
  padding: 8px 10px;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--text-muted);
  background: transparent;
  border-radius: calc(var(--radius-sm) - 2px);
  min-height: 36px;
}
.repeat-option.active {
  background: var(--surface);
  color: var(--accent);
  box-shadow: var(--shadow);
}


/* The "N times" option carries its own stepper instead of a separate row
   underneath -- tapping − or + also selects this mode, so reaching for the
   count is never a two-step action. */
.repeat-option-count {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 4px 6px;
}

.repeat-count-btn {
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: transparent;
  color: inherit;
  font-size: 1rem;
  font-weight: 700;
  border-radius: 6px;
  flex-shrink: 0;
}

.repeat-count-value {
  min-width: 2em;
  text-align: center;
  font-size: 0.86rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: inherit;
}

.pb-repeat {
  width: 44px;
  height: 44px;
  min-height: 44px;
  font-size: 1.1rem;
  flex-shrink: 0;
  position: relative;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-muted);
}

.pb-repeat.active {
  color: var(--accent);
}

.pb-mixer {
  width: 44px;
  height: 44px;
  min-height: 44px;
  font-size: 1.1rem;
  flex-shrink: 0;
  position: relative;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
}

.pb-mixer[aria-expanded="true"] {
  background: var(--accent);
  border-color: var(--accent-dark);
  color: #fff;
}

/* Small badge instead of reviving a text label: a subtle "something is
   picked" signal even while the mixer is collapsed, without needing to
   say which voice -- that's one tap away in the panel itself. */
.pb-mixer.has-pick::after {
  content: "";
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  border: 1.5px solid var(--surface);
}

.pb-mixer[aria-expanded="true"].has-pick::after {
  background: #fff;
  border-color: var(--accent);
}

.voice-select {
  flex: 1;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0 16px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  touch-action: manipulation;
  min-height: 46px;
}

/* Replaces the old vanishing status-line summary with a persistent,
   readable card -- stays for review until a fresh attempt begins (see
   clearTrainingReview() in app.js), same lifecycle as the note coloring. */
.training-result {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 1000px;
  margin: 0 auto;
  padding: 14px 20px;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.training-result[hidden] {
  display: none;
}

/* Shown only during infinite/counted repeat -- a single non-repeating run
   has no previous lap to compare against, so this row just doesn't render. */
.tr-lap-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.8rem;
  font-weight: 700;
}

.tr-lap-counter {
  color: var(--text-muted);
}

.tr-trend {
  font-variant-numeric: tabular-nums;
}

.tr-trend-up {
  color: var(--play);
}

.tr-trend-down {
  color: var(--pause);
}

.tr-trend-flat {
  color: var(--text-muted);
}

.tr-body {
  display: flex;
  align-items: center;
  gap: 20px;
}

.tr-accuracy {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
}

.tr-accuracy-value {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.tr-accuracy-label {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.tr-breakdown {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex: 1;
}

/* Chip colors deliberately match the note-coloring in the score exactly
   (--play/--pause/--accent are the same hex values used there) so the
   summary reads as the same language, not a new one. */
.tr-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: var(--chip-color, var(--bg));
  color: var(--chip-text, var(--text-muted));
}

.tr-chip.tr-correct {
  --chip-color: color-mix(in srgb, var(--play) 16%, var(--surface));
  --chip-text: var(--play);
}

.tr-chip.tr-sharp {
  --chip-color: color-mix(in srgb, var(--pause) 18%, var(--surface));
  /* Darkened well past --pause itself for text-on-tint contrast (pure
     gold reads fine as an icon/border but is too light for body text). */
  --chip-text: #6f4e03;
}

.tr-chip.tr-flat {
  --chip-color: color-mix(in srgb, var(--accent) 14%, var(--surface));
  --chip-text: var(--accent-dark);
}

.tr-chip.tr-missed {
  --chip-color: var(--bg);
  --chip-text: var(--text-muted);
}

/* Icon-only, color-coded transport buttons: shape + color together identify
   the action without needing a text label, so they stay compact enough to
   share one line with the (shrinking) song selector at any width. */
.transport-btn {
  width: 52px;
  height: 52px;
  min-height: 52px;
  padding: 0;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: 1.3rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

/* Hand-drawn icon set (see the <symbol> sprite in index.html): stroke/fill
   both use currentColor, so a button's existing color states (active,
   muted, hover) apply to its icon automatically -- no per-icon variants. */
.btn-icon {
  width: 1.3em;
  height: 1.3em;
  fill: currentColor;
  stroke: currentColor;
  flex-shrink: 0;
}

/* Short (1-2 letter) reminder of which voice is being practiced, shown
   only while actively listening -- voice names come from the song itself
   (see computeVoiceAbbreviation() in app.js) and can be anything, so this
   is never a hardcoded SATB assumption. */
.tt-badge {
  position: absolute;
  bottom: 2px;
  right: 2px;
  min-width: 14px;
  height: 14px;
  padding: 0 2px;
  border-radius: 4px;
  background: #fff;
  color: var(--play);
  font-size: 0.6rem;
  font-weight: 800;
  line-height: 14px;
  text-align: center;
}

.tt-badge[hidden] {
  display: none;
}

/* Needed because .transport-btn's own display:inline-flex (an author rule)
   would otherwise beat the native [hidden] UA rule regardless of
   specificity, leaving the buttons visibly rendered before a song loads. */
.transport-btn[hidden] {
  display: none;
}

.play-btn {
  background: var(--play);
}

.pause-btn {
  background: var(--pause);
}

.stop-btn {
  background: var(--danger);
}

/* --- Bottom dock: the training result, "Twój głos" and the playback bar
   fixed to the viewport bottom as one cluster, so none of it needs
   scrolling to reach (see syncPlaybackBarSpacer() in app.js, which reserves
   matching space at the bottom of the page so this never covers content). */
.bottom-dock {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 15;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.playback-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 1000px;
  margin: 0 auto;
  width: 100%;
  padding: 10px 16px;
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  background: var(--surface);
  border-top: 1px solid var(--border);
  box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.08);
}

.playback-bar[hidden] {
  display: none;
}

.pb-play-pause {
  width: 56px;
  height: 56px;
  min-height: 56px;
  font-size: 1.5rem;
  flex-shrink: 0;
}

.pb-stop {
  width: 44px;
  height: 44px;
  min-height: 44px;
  font-size: 1.1rem;
  flex-shrink: 0;
}

.pb-tempo {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.pb-tempo input[type="range"] {
  flex: 1;
  min-width: 0;
}

.pb-tempo-value {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-muted);
  min-width: 3.4em;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

button {
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  font-size: 0.9rem;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  min-height: 40px;
  transition: background 0.15s ease, transform 0.1s ease;
}

button:disabled {
  background: #c2c2c6;
  cursor: not-allowed;
}

button:not(:disabled):active {
  transform: scale(0.97);
}

.status {
  max-width: 1000px;
  margin: 8px auto 0;
  padding: 0 20px;
  font-size: 0.85rem;
  color: var(--text-muted);
  min-height: 1.2em;
  word-break: break-word;
}

/* Collapses to zero height with no text (e.g. during normal playback, where
   the transport buttons' own state already shows what's happening) instead
   of permanently reserving a blank line. */
.status:empty {
  display: none;
  margin: 0;
  min-height: 0;
}

.status.error {
  color: var(--danger);
}

/* Compact mixer-style list: one shared card with thin row dividers rather
   than a stacked card per track, and a small "M" badge button (the
   standard DAW/mixer convention) instead of a full-width text button, so
   3-4 voices fit in noticeably less space. Lives inside .voice-mix-panel
   now, which handles showing/hiding it as a whole. */
.tracks-panel {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  /* Vertical breathing room lives here, not on .voice-mix-panel itself --
     that outer element needs to reach a true zero height when collapsed
     (see animateExpandablePanel() in app.js), which padding would prevent. */
  margin: 14px 0 10px;
}

.tracks-panel:empty {
  box-shadow: none;
}

/* --- Training mode: real-time pitch feedback for the practiced voice ----
   Shown only once a voice is picked in "Twój głos", entirely independent
   of mute state -- muting voices (including your own) never affects it. */

.training-toggle-btn {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-muted);
}

.pb-mic {
  width: 44px;
  height: 44px;
  min-height: 44px;
  font-size: 1.1rem;
  flex-shrink: 0;
}

/* Green, not red -- red reads as "error" or "on air" elsewhere in this UI
   (Stop, danger messages); actively practicing is a positive, in-progress
   state, and green already means "active/audible" for Play and the mixer's
   speaker icons. State lives in icon color, not a filled background. */
.training-toggle-btn.active {
  color: var(--play);
}

.track-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 16px;
  border-bottom: 1px solid var(--border);
  transition: background 0.15s ease;
}

.track-row:last-child {
  border-bottom: none;
}

.track-name {
  flex: 1;
  font-size: 0.92rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.track-row.is-muted .track-name {
  color: var(--text-faint);
}

.track-controls {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

/* Volume slider: sits left of the speaker button, its own gain control
   independent of (but linked to) mute -- see the `input` handler in
   buildTracksPanel() for how dragging to/from 0% syncs with `muted`. The
   track itself is a wedge (thin at 0%, thick at 100%) via clip-path, so the
   thumb's height at its current position is a visual cue for the level
   even at a glance, not just its horizontal offset.
   Selectors are scoped under .track-controls (not just .volume-slider) so
   they reliably beat the generic input[type="range"] rules used by the
   playback/tempo sliders elsewhere -- an attribute selector matches classes'
   specificity, so a bare .volume-slider rule can lose that fight. */
.track-controls input.volume-slider {
  width: 54px;
  height: 16px;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: pointer;
  vertical-align: middle;
}

.track-controls input.volume-slider::-webkit-slider-runnable-track {
  height: 14px;
  background: var(--play);
  clip-path: polygon(0% 42%, 100% 2%, 100% 100%, 0% 58%);
  border-radius: 2px;
}

.track-controls input.volume-slider::-moz-range-track {
  height: 14px;
  background: var(--play);
  clip-path: polygon(0% 42%, 100% 2%, 100% 100%, 0% 58%);
  border-radius: 2px;
}

.track-controls input.volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 8px;
  height: 16px;
  margin-top: -1px;
  border-radius: 2px;
  background: var(--surface);
  border: 2px solid var(--accent);
}

.track-controls input.volume-slider::-moz-range-thumb {
  width: 8px;
  height: 16px;
  border-radius: 2px;
  background: var(--surface);
  border: 2px solid var(--accent);
}

.track-controls input.volume-slider.muted::-webkit-slider-runnable-track,
.track-controls input.volume-slider.muted::-moz-range-track {
  background: var(--text-faint);
}

.track-controls input.volume-slider.muted::-webkit-slider-thumb,
.track-controls input.volume-slider.muted::-moz-range-thumb {
  border-color: var(--text-faint);
}

.track-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
  user-select: none;
  position: relative;
  transition: color 0.15s ease;
}

/* State lives in icon color, not a filled background -- several of these
   in a row read as a clean row of controls rather than a wall of colored
   blocks. Speaker: green while audible (the default "on" state), gray
   once muted -- matches the on/off language used everywhere else now. */
.track-btn.speaker-btn {
  color: var(--play);
}

.track-btn.speaker-btn.muted {
  color: var(--text-faint);
}

/* Solo: mutes every other voice, un-solos (unmutes everyone) on a second
   click -- a distinct color from both speaker and mic-select so a row
   with more than one active control still reads at a glance. */
.track-btn.solo-btn {
  color: var(--text-faint);
}

.track-btn.solo-btn.active {
  color: var(--pause);
}

/* Mic-select: which voice "Twój głos" currently practices against. */
.track-btn.mic-select-btn {
  color: var(--text-faint);
}

.track-btn.mic-select-btn.active {
  color: var(--accent);
}

/* Eye: whether this voice's staff is drawn in the notation at all --
   visible reuses the same green "on" language as the speaker icon (both
   are "this voice is fully present" states, just for sound vs. notation),
   hidden fades to the --text-faint "off" language every control here uses. */
.track-btn.eye-btn {
  color: var(--play);
}

.track-btn.eye-btn.hidden-voice {
  color: var(--text-faint);
}

.track-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* --- Reading view switch: exactly one of Nuty/Tekst is ever visible (see
   setReadingView() in app.js), each filling the full width/height instead
   of two independently-collapsible panels competing for space. */

.reading-switch {
  display: flex;
  width: calc(100% - 40px);
  max-width: 1000px;
  margin: 16px auto 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px;
  gap: 4px;
}

.reading-tab {
  flex: 1;
  text-align: center;
  padding: 8px 12px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-muted);
  background: transparent;
  border-radius: calc(var(--radius-sm) - 2px);
  min-height: 36px;
}
.reading-tab.active {
  background: var(--surface);
  color: var(--accent);
  box-shadow: var(--shadow);
}


/* Needed because .reading-tab's own background/color (author rules) would
   otherwise beat the native [hidden] UA rule regardless of specificity. */
.reading-tab[hidden] {
  display: none;
}

/* --- Expandable wrapper: shared by Lyrics and Notation, toggled by the
   reading switch above -- whichever isn't the active tab collapses away
   entirely rather than the two sharing the page at reduced size each. --- */

.expandable-wrapper {
  position: relative;
  max-width: 1000px;
}

.expandable-wrapper.collapsed {
  display: none;
}

#lyrics-wrapper {
  margin: 10px auto 0;
}

#notation-wrapper {
  margin: 20px auto;
}

main#sheet-container {
  background: var(--surface);
  padding: 20px;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  min-height: 300px;
  overflow-y: auto;
  overflow-x: hidden;
  scroll-behavior: smooth;
  transition: height 0.25s ease;
}

.placeholder {
  color: var(--text-muted);
  text-align: center;
  margin-top: 100px;
}

/* --- Lyrics (Spotify-style word-by-word follow) ------------------------ */

.lyrics-panel {
  height: 180px;
  overflow-y: auto;
  overflow-x: hidden;
  scroll-behavior: smooth;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px;
  text-align: center;
  font-size: 1.35rem;
  line-height: 2.1;
}

.lyrics-panel:empty {
  display: none;
  margin: 0;
}

/* --- Pitch monitor ("Wykres") -------------------------------------------- */

#pitch-wrapper {
  margin: 10px auto 0;
}

.pitch-panel {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
}

#pitch-canvas {
  display: block;
  width: 100%;
  height: 300px;
  border-radius: var(--radius-sm);
}

.pitch-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
  margin-top: 12px;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.pl-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.pl-swatch {
  width: 14px;
  height: 4px;
  border-radius: 2px;
  display: inline-block;
}

.pl-swatch.pl-target {
  background: var(--accent);
}

.pl-swatch.pl-other {
  background: var(--text-faint);
}

/* --- Pitch practice stats: a full-screen overlay (not the header dropdown
   settings live in) since it needs real room for a chart and must be
   reachable from both the library and player screens. Same slide/backdrop
   language as .settings-panel, just fixed to the whole viewport instead of
   anchored under the header. --- */
.pitch-stats-panel {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  transform: translateY(12px);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.2s ease, opacity 0.2s ease, visibility 0s linear 0.2s;
}

.pitch-stats-panel.open {
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
  transition: transform 0.2s ease, opacity 0.2s ease, visibility 0s linear 0s;
}

.pitch-stats-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  padding-top: max(16px, env(safe-area-inset-top));
  font-weight: 700;
  font-size: 1.05rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.pitch-stats-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 18px 20px calc(24px + env(safe-area-inset-bottom));
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}

.ps-switch-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

.ps-switch {
  display: flex;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px;
  gap: 4px;
  width: fit-content;
}

.ps-tab {
  padding: 7px 14px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  background: transparent;
  border: none;
  border-radius: calc(var(--radius-sm) - 2px);
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
}
.ps-tab.active {
  background: var(--bg);
  color: var(--accent);
  box-shadow: var(--shadow);
}
.ps-tab[disabled] { opacity: 0.4; cursor: not-allowed; }

.ps-song-context {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-top: 8px;
}

.ps-summary-row { display: flex; gap: 10px; flex-wrap: wrap; margin: 16px 0 20px; }
.ps-stat-chip {
  flex: 1;
  min-width: 150px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
}
.ps-stat-chip .ps-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-faint);
  display: block;
  margin-bottom: 4px;
}
.ps-stat-chip .ps-value { font-size: 1.15rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.ps-stat-chip.weak .ps-value { color: #f5a623; }
.ps-stat-chip .ps-sub { font-size: 0.76rem; color: var(--text-muted); }

.ps-empty {
  color: var(--text-muted);
  font-size: 0.92rem;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  padding: 24px;
  text-align: center;
}

/* Sits inside a card that already has its own background/border (unlike
   .ps-empty above, which is the card itself) -- just the message, no
   second nested box. */
.ps-empty-inline { color: var(--text-muted); font-size: 0.85rem; margin: 8px 0; }

.ps-chart-area { position: relative; background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow); padding: 16px 14px 12px; }
.ps-chart-grid {
  display: grid;
  gap: 3px;
  align-items: end;
  height: 200px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 2px;
}
.ps-bar-col { display: flex; flex-direction: column; justify-content: flex-end; height: 100%; position: relative; }
.ps-bar-stack { width: 100%; display: flex; flex-direction: column-reverse; border-radius: 2px 2px 0 0; overflow: hidden; min-height: 0; cursor: pointer; }
.ps-seg { width: 100%; }
.ps-bar-labels { display: grid; gap: 3px; margin-top: 6px; }
.ps-bar-labels span { text-align: center; font-size: 0.7rem; font-weight: 600; color: var(--text-muted); white-space: nowrap; overflow: hidden; }
.ps-bar-labels span.weakest { color: #f5a623; }

.ps-tooltip {
  position: absolute; background: var(--text); color: #fff; font-size: 0.76rem; padding: 7px 10px;
  border-radius: 6px; pointer-events: none; opacity: 0; transform: translate(-50%, -100%);
  white-space: nowrap; z-index: 5; transition: opacity 0.1s; line-height: 1.5;
}
.ps-tooltip.show { opacity: 1; }
.ps-tooltip b { font-variant-numeric: tabular-nums; }

.ps-legend { display: flex; gap: 16px; flex-wrap: wrap; margin: 14px 2px; font-size: 0.82rem; color: var(--text-muted); }
.ps-legend-item { display: flex; align-items: center; gap: 6px; }
.ps-swatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }

.ps-trend-panel { background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow); padding: 14px 14px 10px; }
.ps-subchart-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-faint);
  margin: 14px 0 6px;
}
.ps-subchart-label:first-of-type { margin-top: 2px; }
.ps-legend-compact { margin: 8px 2px 4px; gap: 12px; font-size: 0.76rem; }
.ps-trend-head { margin-bottom: 6px; }
.ps-trend-head h3 { font-size: 0.88rem; font-weight: 700; color: var(--text); margin: 0; }
/* Sits directly above its own card (not inside one, unlike the trend/
   timeline panels above and below it) -- needs its own top margin so it
   reads as the start of a new section, not a caption trailing the last one. */
.ps-dist-head { margin: 20px 2px 8px; }

/* --- Song list view (the panel's default screen) --- */
.ps-song-list-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-faint);
}
.ps-list-head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 18px 2px 8px;
}
.ps-sort-select {
  font-size: 0.8rem;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 5px 8px;
}
.ps-song-list { display: flex; flex-direction: column; gap: 8px; }

.ps-song-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  cursor: pointer;
  font-family: inherit;
  color: var(--text);
}
.ps-song-row:hover { border-color: var(--accent); }
.ps-song-row-all {
  margin-bottom: 4px;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
}
.ps-song-row .ps-song-row-title { font-weight: 600; font-size: 0.92rem; }
.ps-song-row .ps-song-row-sub { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.ps-song-row .ps-song-row-stats { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.ps-song-row .ps-song-row-count {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 44px;
}
.ps-song-row .ps-song-row-acc {
  font-size: 1rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 40px;
}
.ps-song-row .ps-song-row-acc.low { color: #cd191b; }
.ps-song-row .ps-song-row-acc.mid { color: #f5a623; }
.ps-song-row .ps-song-row-acc.high { color: #2fb350; }

.ps-clear-all-btn { margin-top: 20px; width: 100%; }
.ps-clear-this-btn { margin-top: 18px; width: 100%; }

.ps-back-btn {
  background: none;
  border: none;
  color: var(--accent);
  font-weight: 600;
  font-size: 0.9rem;
  padding: 4px 0 14px;
  cursor: pointer;
  font-family: inherit;
}

@media (max-width: 600px) {
  .ps-bar-labels span { font-size: 0.6rem; }
}

.lyric-word {
  color: var(--text-faint);
  cursor: pointer;
  border-radius: 4px;
  transition: color 0.2s ease, background 0.15s ease;
}

.lyric-word.past {
  color: var(--text-muted);
}

.lyric-word.current {
  color: var(--danger);
  font-weight: 700;
}

/* Highlight for currently sounding notes */
.note-active {
  fill: var(--danger) !important;
}

/* Click-to-seek: every note in the sheet gets one of these (see
   attachSeekHandlers in app.js). Hover only recolors un-highlighted notes,
   since the active-note red highlight is set via inline style and wins. */
.seekable-note {
  cursor: pointer;
}

/* --- Hover-only styling, scoped to devices that actually support a
   hover state. Touch devices otherwise get "sticky hover" -- the
   simulated :hover from a tap doesn't clear until you tap somewhere
   else, so a button looks like it changed color and stayed that way.
   Every hover effect in this file lives here for exactly that reason. */
@media (hover: hover) and (pointer: fine) {
  .icon-btn:hover {
    background: #ebebef;
  }

  .loop-btn:hover {
    background: #ebebef;
  }

  .song-row:hover {
    background: var(--bg);
  }

  .back-to-library-btn:hover {
    color: var(--accent-dark);
  }

  .repeat-option.active:not(:disabled):hover {
    background: var(--surface);
    color: var(--accent);
    box-shadow: var(--shadow);
  }

  .repeat-option:not(.active):not(:disabled):hover {
    background: none;
    color: var(--text);
  }

  .repeat-count-btn:not(:disabled):hover {
    background: rgba(0, 0, 0, 0.06);
  }

  .pb-repeat:not(:disabled):hover {
    background: #ebebef;
  }

  .pb-mixer:not(:disabled):hover {
    background: #ebebef;
  }

  .play-btn:not(:disabled):hover {
    filter: brightness(0.93);
  }

  .pause-btn:not(:disabled):hover {
    filter: brightness(0.93);
  }

  .stop-btn:not(:disabled):hover {
    filter: brightness(0.93);
  }

  button:not(:disabled):hover {
    background: var(--accent-dark);
  }

  .training-toggle-btn:not(:disabled):hover {
    background: #ebebef;
  }

  .track-row:hover {
    background: #fafafc;
  }

  .track-btn:hover {
    background: #e8e8ed;
  }

  .reading-tab.active:not(:disabled):hover {
    background: var(--surface);
    color: var(--accent);
    box-shadow: var(--shadow);
  }

  .reading-tab:not(.active):not(:disabled):hover {
    background: none;
    color: var(--text);
  }

  .lyric-word:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
  }

  .seekable-note:hover path,
  .seekable-note:hover ellipse,
  .seekable-note:hover rect,
  .seekable-note:hover circle {
    fill: var(--accent);
    stroke: var(--accent);
  }
}

/* --- World 3: management (login, Utwory/Playlisty CRUD) ----------------- */

.settings-btn {
  border: none;
  border-radius: var(--radius-sm);
  padding: 10px 0;
  font-size: 0.85rem;
  font-weight: 700;
  text-align: center;
  touch-action: manipulation;
  /* Also used on the Pomoc section's <a> link -- buttons ignore these, but
     an anchor needs them explicitly to stop looking/behaving like a plain
     text link (underline, inline sizing) instead of a settings button. */
  text-decoration: none;
  cursor: pointer;
}

.settings-btn.primary {
  background: var(--accent);
  color: #fff;
}

.settings-btn.ghost {
  background: #f0f0f2;
  color: var(--text-muted);
}

.manager-login-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.manager-login-form[hidden] {
  display: none;
}

.manager-login-input {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  font-size: 0.9rem;
  min-height: 40px;
}

.manager-login-error,
.manager-form-error {
  color: var(--danger);
  font-size: 0.82rem;
  margin: 0;
}

.manager-logged-in {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.manager-logged-in[hidden] {
  display: none;
}

.manager-badge {
  background: color-mix(in srgb, var(--play) 14%, var(--surface));
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  font-size: 0.82rem;
  font-weight: 700;
  color: #1d5c33;
}

.manager-screen {
  max-width: 700px;
  margin: 0 auto;
  padding: 0 20px 32px;
}

.manager-view[hidden] {
  display: none;
}

.manager-toolbar {
  margin: 16px 0 10px;
}

.manager-add-btn {
  width: 100%;
  text-align: center;
  background: var(--danger);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  padding: 12px 0;
  font-size: 0.92rem;
  font-weight: 700;
  touch-action: manipulation;
}

.manager-list {
  display: flex;
  flex-direction: column;
}

.manager-list-empty {
  color: var(--text-muted);
  padding: 14px 4px;
}

.manager-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid var(--border);
  padding: 12px 4px;
}

.manager-row-text {
  min-width: 0;
}

.manager-row-name {
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manager-row-sub {
  font-size: 0.82rem;
  color: var(--text-muted);
}

.manager-row-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.manager-icon-btn {
  width: 34px;
  height: 34px;
  border: none;
  border-radius: var(--radius-sm);
  background: #f0f0f2;
  color: var(--text-muted);
  font-size: 0.95rem;
  touch-action: manipulation;
}

.manager-icon-btn:disabled {
  opacity: 0.35;
}

.manager-icon-btn.danger {
  color: var(--danger);
}

#manager-song-form-view form,
#manager-playlist-form-view form {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 16px;
}

.manager-form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.manager-form-field.stack {
  gap: 8px;
}

.manager-form-field[hidden] {
  display: none;
}

.manager-form-label {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

.manager-form-label .auto-hint {
  font-style: normal;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--play);
}

.manager-form-label .req-hint {
  font-style: normal;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-faint);
}

.manager-form-input {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
  font-size: 0.92rem;
  min-height: 40px;
  width: 100%;
}

.manager-upload-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: #f8f8fa;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: 9px 11px;
}

.manager-upload-file {
  font-size: 0.85rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.manager-upload-btn {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
  touch-action: manipulation;
}

.manager-auto-note {
  font-size: 0.78rem;
  color: var(--play);
  margin: 0;
}

.manager-audio-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.manager-add-file-btn {
  text-align: center;
  background: none;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: 9px 0;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent);
  touch-action: manipulation;
}

.manager-hint {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin: 0;
}

.manager-form-actions {
  display: flex;
  gap: 10px;
}

.manager-form-actions .settings-btn {
  flex: 1;
}

.manager-ordered-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  max-height: 320px;
  overflow-y: auto;
}

.manager-ordered-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}

.manager-ordered-row:last-child {
  border-bottom: none;
}

.manager-ordered-text {
  font-size: 0.88rem;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manager-ordered-list .manager-list-empty {
  padding: 12px 10px;
  font-size: 0.85rem;
}

.manager-playlist-add-row {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

.manager-playlist-add-row select {
  flex: 1;
  min-width: 0;
}

.manager-playlist-add-row .manager-add-file-btn {
  flex-shrink: 0;
  width: auto;
  padding: 0 16px;
}

.settings-btn.danger {
  background: var(--danger);
  color: #fff;
}

.manager-confirm-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.manager-confirm-box {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 20px;
  max-width: 340px;
  width: 100%;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.manager-confirm-box p {
  margin: 0;
  font-size: 0.95rem;
  color: var(--text);
}

.manager-confirm-actions {
  display: flex;
  gap: 10px;
}

.manager-confirm-actions .settings-btn {
  flex: 1;
}

#manager-toast {
  position: fixed;
  left: 50%;
  bottom: max(24px, calc(env(safe-area-inset-bottom) + 16px));
  transform: translate(-50%, 12px);
  background: var(--text);
  color: #fff;
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 0.86rem;
  font-weight: 600;
  box-shadow: var(--shadow);
  z-index: 110;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  max-width: calc(100vw - 40px);
  text-align: center;
}

#manager-toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}

#manager-toast.error {
  background: var(--danger);
}

.manager-init-error {
  position: sticky;
  top: 0;
  z-index: 200;
  background: var(--danger);
  color: #fff;
  padding: 10px 16px;
  font-size: 0.85rem;
  font-weight: 600;
  text-align: center;
}

@media (hover: hover) and (pointer: fine) {
  .manager-icon-btn:not(:disabled):hover {
    background: color-mix(in srgb, var(--accent) 12%, #f0f0f2);
  }

  .settings-btn.primary:not(:disabled):hover {
    background: var(--accent-dark);
  }

  .settings-btn.ghost:not(:disabled):hover {
    background: #e6e6e9;
  }

  .settings-btn.danger:not(:disabled):hover {
    background: #a01315;
  }
}

/* --- "Materiały" bottom sheet: PDF / cover / reference recordings for a
   song, opened from a "⋮" on a library row or next to the title in the
   player -- deliberately not another icon in the playback bar, which is
   already full (play/repeat/stop/tempo/mixer, plus mic during training). ---

   Same backdrop + sliding-panel mechanism as .settings-panel/.settings-
   backdrop, just anchored to the bottom of the viewport instead of under
   the header, and triggered from lower on the screen. */

.materials-kebab-btn {
  flex-shrink: 0;
}

.materials-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.42);
  z-index: 24;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}

.materials-backdrop.open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.25s ease, visibility 0s linear 0s;
}

.materials-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 25;
  background: var(--surface);
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.22);
  padding: 10px 20px calc(18px + env(safe-area-inset-bottom, 0px));
  max-height: 80vh;
  max-height: 80dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  transform: translateY(100%);
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
}

/* Needed because .materials-sheet's own display (an author rule further
   down the cascade) would otherwise beat the native [hidden] UA rule --
   same reason .transport-btn[hidden] exists above. */
.materials-sheet[hidden] {
  display: none;
}

.materials-sheet.open {
  transform: translateY(0);
}

.materials-handle {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  margin: 2px auto 14px;
}

.materials-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}

.materials-cover {
  width: 52px;
  height: 52px;
  border-radius: 10px;
  object-fit: cover;
  flex-shrink: 0;
}

.materials-header-text {
  min-width: 0;
  flex: 1;
}

.materials-title {
  font-weight: 700;
  font-size: 1rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.materials-composer {
  font-size: 0.85rem;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.materials-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}

.materials-row[hidden] {
  display: none;
}

.materials-row-icon {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--accent);
}

.materials-row-text {
  flex: 1;
  min-width: 0;
  font-size: 0.94rem;
  font-weight: 600;
}

.materials-row-link {
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 700;
  flex-shrink: 0;
}

/* .materials-row is normally an <a> (the PDF row); a plain action like
   "print" is a <button> instead, so the global button reset (solid accent
   background, its own padding/radius) needs undoing here to match. */
.materials-row-button {
  width: 100%;
  background: none;
  color: var(--text);
  border: none;
  border-radius: 0;
  min-height: 0;
  font-size: inherit;
  text-align: left;
}

/* Shared by the PDF section and the Nagrania (recordings) section -- both
   are just "a label plus some rows", hidden as a whole when empty. */
.materials-section[hidden] {
  display: none;
}

.materials-section-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 14px 0 4px;
}

.materials-audio-row {
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

.materials-audio-row:last-child {
  border-bottom: none;
}

.materials-audio-label {
  font-size: 0.9rem;
  font-weight: 600;
  margin-bottom: 6px;
}

.materials-audio-row audio {
  width: 100%;
  height: 36px;
}

/* --- Printable playlist: invisible in the app itself, the *only* thing
   shown on the page when actually printing -- filled in by printPlaylist()
   in app.js just before window.print(). Kept off-screen (not display:none)
   so the browser still lays it out and paginates it correctly for print,
   which a `display:none` element never gets.

   The actual song-list styling (.pp-*) lives in playlist-print.css instead
   of here, and is shared with the standalone tab openPlaylistList() opens --
   that tab has no other elements to hide, so it must NOT also load the
   body > *:not(.playlist-print-area) rule below, which is specific to
   this page's hidden-div-revealed-at-print-time mechanism. */
.playlist-print-area {
  position: absolute;
  left: -9999px;
  top: 0;
  width: 210mm;
  color: #000;
  background: #fff;
}

@media print {
  body > *:not(.playlist-print-area) {
    display: none !important;
  }

  .playlist-print-area {
    position: static;
    width: auto;
  }
}

@media (max-width: 640px) {
  header {
    padding: 10px 14px;
    padding-top: max(10px, env(safe-area-inset-top));
    gap: 8px;
  }

  header h1 {
    font-size: 1.05rem;
  }

  .app-logo {
    width: 22px;
    height: 22px;
  }

  .icon-btn {
    font-size: 1rem;
    min-width: 40px;
    min-height: 40px;
    padding: 6px 10px;
  }

  .settings-panel {
    padding: 12px 14px;
  }

  .settings-field {
    max-width: none;
  }

  .library-search,
  .library-section-label,
  .playlist-tiles,
  .song-list,
  .player-back-row,
  .training-result,
  .status,
  .reading-switch,
  .expandable-wrapper {
    margin-left: 10px;
    margin-right: 10px;
  }

  .voice-mix-panel,
  .repeat-panel,
  .manager-screen {
    padding-left: 10px;
    padding-right: 10px;
  }

  .player-back-row {
    margin-top: 10px;
  }

  .transport-btn {
    width: 46px;
    height: 46px;
    min-height: 46px;
  }

  .playback-bar {
    gap: 10px;
    padding: 8px 12px;
    padding-bottom: max(8px, env(safe-area-inset-bottom));
  }

  .pb-play-pause {
    width: 50px;
    height: 50px;
    min-height: 50px;
    font-size: 1.3rem;
  }

  .pb-stop {
    width: 40px;
    height: 40px;
    min-height: 40px;
  }

  .pb-mic,
  .pb-mixer,
  .pb-repeat {
    width: 40px;
    height: 40px;
    min-height: 40px;
  }

  .status {
    margin-top: 4px;
    padding: 0;
    text-align: center;
  }


  .track-row {
    padding: 8px 14px;
  }

  .track-btn {
    width: 38px;
    height: 38px;
  }

  .track-controls input.volume-slider {
    width: 40px;
  }

  .track-name {
    font-size: 0.88rem;
  }

  main#sheet-container {
    padding: 12px;
  }

  .lyrics-panel {
    height: 150px;
    font-size: 1.1rem;
    line-height: 1.9;
    padding: 14px;
  }

  .pitch-panel {
    padding: 12px;
  }

  .pitch-legend {
    gap: 10px;
    font-size: 0.76rem;
  }
}
