@font-face {
  font-family: 'Space Grotesk';
  src: url("../fonts/SpaceGrotesk.woff2") format("woff2");
  font-weight: 400 600;
  font-display: block;
}

:root {
  /* The ground everything sits on. */
  --ink:      #050507;

  /* Nothing above the ground has a colour of its own: it is white, held back.
     Four steps carry the whole interface — what you read, what you glance at,
     what is merely present, and what only separates. */
  --text:     rgba(255, 255, 255, 0.94);
  --read:     rgba(255, 255, 255, 0.62);
  --dim:      rgba(255, 255, 255, 0.44);
  --faint:    rgba(255, 255, 255, 0.20);
  --hairline: rgba(255, 255, 255, 0.09);
  --divider:  rgba(255, 255, 255, 0.055);
  --pane:     rgba(255, 255, 255, 0.035);

  /* A raised surface is not a lighter grey but a slightly different black:
     menus and panels lift off the ground by their edge and their shadow. */
  --raised:   rgb(9, 9, 12);
  --sunken:   rgba(0, 0, 0, 0.30);

  /* The one colour. Used for what is selected, what is playing, and nothing
     else — which is what makes it mean something. */
  /* MOTION's warm glow, which is its --glow-warm exactly. One colour for the
     whole window: what is selected, what is active, what is playing. */
  --glow:     #ffb27a;
  --glow-rgb: 255, 178, 122;
  /* Les mêmes canaux séparés par des espaces. La syntaxe rgb(r g b / a) refuse
     les virgules : rgb(255, 178, 122 / .3) est invalide et la règle entière est
     jetée. Deux écritures du même nombre, parce que CSS en demande deux. */
  --glow-channels: 255 178 122;
  --glow-dim: rgba(255, 178, 122, 0.55);
  --glow-soft:rgba(255, 178, 122, 0.10);
  --on-glow:  rgba(9, 11, 15, 0.92);

  /* The filter chain that tints a flat icon in the accent colour. There is no
     CSS filter that simply says "make this colour", so the chain has to be
     solved for; src/accent.js recomputes it whenever the colour changes. This
     is the answer for the default. */
  --glow-filter: brightness(0) saturate(100%) invert(63%) sepia(77%) saturate(486%)
                 hue-rotate(332deg) brightness(150%) contrast(49%);

  /* Kept for what has to stay legible as itself. */
  --danger:   #ff6b5e;
  --warning:  #ffab4a;
  --success:  #9ae6b4;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* La taille de tous les mots de cette fenetre, comme un facteur.
     Chaque font-size est ecrite « calc(Npx * var(--ui-font-scale, 1)) », donc
     ce seul nombre les deplace toutes. Un, c'est la taille dessinee : le
     reglage existe pour qui en a besoin, pas pour changer ce que voit tout le
     monde. Ecrit ici pour que la feuille se tienne toute seule, et remplace au
     demarrage par ce que la personne a choisi. */
  --ui-font-scale: 1;

  /* Et la graisse, qui est l'autre moitie de la plainte : « small AND THIN ».
     Deux cent onze declarations passent par ces deux-la — les seules graisses
     que cette fenetre emploie — donc « plus gras » les monte d'un cran ensemble
     et la hierarchie entre les deux tient.
     Space Grotesk est variable de 400 a 600 et pas au-dela : un cran de plus
     est tout ce que le fichier sait faire, et rien n'est donc synthetise. */
  --ui-weight-regular: 400;
  --ui-weight-medium: 500;

  --font:      'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
}

:root {
  --app-background: var(--ink);
  --sidebar-background: var(--ink);
  --header-background: var(--ink);
  --header-height: 58px;
  --header-top-gap: 2.5px;
  --footer-height: 50px;
  --midi-piano-height: 72px;
  --footer-left-gap: 2px;
  --sidebar-width: 220px;
  --sidebar-default-width: 220px;
  --tabbar-height: 30px;
  color-scheme: dark;
  font-family: var(--font);
  background: var(--app-background);
}

.app-closing-overlay {
  position: fixed;
  inset: 0;
  z-index: 100000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(53, 53, 53, 0.96);
  opacity: 0;
  pointer-events: auto;
}

.app-closing-overlay.is-visible {
  opacity: 1;
}

.app-closing-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  color: rgba(255, 255, 255, 0.74);
  font-size: calc(18px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0;
}

.app-closing-spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid rgba(var(--glow-rgb), 0.18);
  border-top-color: var(--glow);
  animation: app-closing-spin 620ms linear infinite;
}

.app-closing-label {
  line-height: 1;
}

@keyframes app-closing-spin {
  to { transform: rotate(360deg); }
}

*,
*::before,
*::after {
  box-sizing: border-box;
  user-select: none;
  -webkit-user-select: none;
}

.header-search-text {
  user-select: text;
  -webkit-user-select: text;
}

html,
body {
  width: 100%;
  min-width: 320px;
  min-height: 100%;
  margin: 0;
  background: var(--app-background);
}

body {
  min-height: 100vh;
  overflow: hidden;
}

#app {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 100vh;
  background: var(--app-background);
  overflow: hidden;
}

.app-titlebar {
  flex: 0 0 40px;
  min-height: 40px;
  width: 100%;
  background: var(--app-background);
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-app-region: drag;
  user-select: none;
}

.app-titlebar-title {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.55);
  letter-spacing: 0.02em;
  pointer-events: none;
}

.app-body {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  height: calc(100vh - 40px);
}

@supports (min-height: 100dvh) {
  body,
  #app {
    min-height: 100dvh;
  }

  .app-body {
    height: calc(100dvh - 40px);
  }
}

.app-sidebar {
  position: relative;
  flex: 0 0 var(--sidebar-width);
  width: var(--sidebar-width);
  min-width: 0;
  height: 100%;
  background: var(--sidebar-background);
  display: flex;
  flex-direction: column;
  transition:
    flex-basis 150ms ease,
    width 150ms ease;
  will-change: width, flex-basis;
}

/* Instruction zone at the bottom of the sidebar */
.sidebar-instruction-zone {
  margin-top: auto;
  flex-shrink: 0;
  overflow: hidden;
}

.sidebar-separator {
  height: 1px;
  background: rgba(255, 255, 255, 0.08);
  margin: 0 14px;
}

.sidebar-instruction {
  padding: 12px 16px 16px;
  min-height: 64px;
  display: flex;
  align-items: flex-start;
}

.sidebar-instruction-text {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.28);
  line-height: 1.5;
  transition: color 120ms ease;
  white-space: normal;
  overflow-wrap: break-word;
  word-break: break-word;
}

.sidebar-instruction-text.is-active {
  color: rgba(255, 255, 255, 0.55);
}

.sidebar-instruction-label {
  color: rgba(255, 255, 255, 0.82);
  font-weight: var(--ui-weight-regular, 400);
}

.sidebar-instruction-text.is-bright-label .sidebar-instruction-label {
  color: #fff;
  font-weight: var(--ui-weight-medium, 500);
}

.app-stage {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  background: var(--app-background);
}

.app-header {
  flex: 0 0 calc(var(--header-height) + var(--header-top-gap));
  min-height: calc(var(--header-height) + var(--header-top-gap));
  border-top: var(--header-top-gap) solid var(--sidebar-background);
  background: var(--header-background);
}

.app-main {
  --sl-pad-left: var(--main-content-pad-left);
  --sl-pad-right: var(--main-content-pad-right);
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--app-background);
  position: relative;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.app-main::-webkit-scrollbar {
  display: none;
  width: 0;
  height: 0;
}

.app-main-scrollbar {
  position: fixed;
  width: 12px;
  z-index: 30;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}

.app-main-scrollbar.is-visible {
  opacity: 1;
  pointer-events: auto;
}

.app-main-scrollbar-thumb {
  position: absolute;
  top: 0;
  right: 3px;
  width: 6px;
  min-height: 26px;
  border-radius: 2px;
  background: var(--sidebar-background);
  transform: translateY(0);
  transition: background 80ms ease;
}

.app-main-scrollbar:hover .app-main-scrollbar-thumb,
.app-main-scrollbar.is-dragging .app-main-scrollbar-thumb {
  background: var(--pane);
}

.app-stage.has-similarity-panel .app-main {
  margin-right: var(--similarity-panel-width);
}

/* ── Browser tabs ───────────────────────────────────────────── */
.app-tabbar {
  flex: 0 0 calc(var(--tabbar-height) + var(--header-top-gap));
  min-height: calc(var(--tabbar-height) + var(--header-top-gap));
  height: calc(var(--tabbar-height) + var(--header-top-gap));
  display: none;
  align-items: stretch;
  gap: 2px;
  background: linear-gradient(to bottom, var(--ink) 0, var(--ink) calc(100% - 2px), var(--ink) calc(100% - 2px), var(--ink) 100%);
  border-top: var(--header-top-gap) solid var(--sidebar-background);
  padding: 0 0 2px;
  overflow: hidden;
}

#app[data-tabs-enabled="true"] .app-tabbar {
  display: flex;
}

#app[data-tabs-enabled="true"] .app-header {
  flex-basis: var(--header-height);
  min-height: var(--header-height);
  border-top: 0;
}

.app-tabs {
  display: flex;
  align-items: stretch;
  gap: 2px;
  min-width: 0;
  flex: 0 1 auto;
  max-width: calc(100% - 30px);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.app-tabs::-webkit-scrollbar {
  display: none;
  width: 0;
  height: 0;
}

.app-tab {
  flex: 0 1 auto;
  min-width: 118px;
  max-width: 260px;
  height: 100%;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 0 12px 0 8px;
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: var(--raised);
  border: none;
  border-radius: 0;
  cursor: pointer;
  outline: none;
  white-space: nowrap;
  transition: background 80ms ease, color 80ms ease;
}

.app-tab:hover {
  color: var(--text);
}

.app-tab.is-active {
  background: var(--ink);
  color: var(--read);
}

.app-tab-close {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  color: inherit;
  background: transparent;
  border: none;
  border-radius: 2px;
  cursor: pointer;
  opacity: 0.78;
  outline: none;
}

.app-tab-close:hover,
.app-tab-close:focus-visible {
  opacity: 1;
  background: rgba(255,255,255,0.07);
}

.app-tab-close svg {
  width: 9px;
  height: 9px;
  display: block;
  pointer-events: none;
}

.app-tab-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
}

.app-tab-rename-input {
  min-width: 72px;
  width: 150px;
  color: var(--text);
}

.app-tab-add {
  flex: 0 0 24px;
  width: 24px;
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  color: var(--read);
  background: transparent;
  border: none;
  border-radius: 0;
  cursor: pointer;
  outline: none;
  opacity: 0.78;
  transition: opacity 80ms ease, color 80ms ease;
}

.app-tab-add:hover {
  color: var(--text);
  opacity: 1;
}

.app-tab-add svg {
  width: 12px;
  height: 12px;
  display: block;
  pointer-events: none;
}

.app-tab-ctx-menu {
  width: 150px;
}

.app-footer {
  flex: 0 0 var(--footer-height);
  min-height: var(--footer-height);
  margin-left: var(--footer-left-gap);
  background: var(--sidebar-background);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  position: relative;
}

.app-footer.is-midi-piano-visible {
  flex-basis: calc(var(--footer-height) + var(--midi-piano-height));
  min-height: calc(var(--footer-height) + var(--midi-piano-height));
}


/* Progress bar — thin line at the very top of the footer */
.footer-progress-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: rgba(255, 255, 255, 0.06);
  cursor: pointer;
  z-index: 10;
  opacity: 0;
  transition: opacity 200ms ease;
}

.footer-progress-bar.is-visible {
  opacity: 1;
}

.footer-progress-bar:hover {
  height: 5px;
  top: -1px;
}

.footer-progress-fill {
  height: 100%;
  width: 0%;
  background: var(--glow);
  border-radius: 0 2px 2px 0;
  pointer-events: none;
  transition: width 0.05s linear;
}

.footer-progress-bar.has-midi-progress > .footer-progress-fill:not(.footer-progress-fill--midi) {
  opacity: 0;
}

.footer-progress-fill--midi {
  position: absolute;
  inset: 0 auto 0 0;
  opacity: 0.5;
  transition: none;
}

.midi-piano-panel {
  display: none;
  flex: 0 0 var(--midi-piano-height);
  height: var(--midi-piano-height);
  padding: 9px 12px 8px 16px;
  background: var(--pane);
  border-bottom: 1px solid rgba(255, 255, 255, 0.055);
}

.app-footer.is-midi-piano-visible .midi-piano-panel {
  display: block;
}

.midi-piano-keys {
  --white-key-count: 52;
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--white-key-count), minmax(0, 1fr));
  width: 100%;
  height: 100%;
  min-width: 0;
  overflow: hidden;
  border-radius: 2px;
  background: var(--ink);
  cursor: pointer;
  touch-action: none;
}

.midi-piano-key {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  border: 0;
  padding: 0;
  outline: none;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: background 35ms linear, box-shadow 35ms linear, transform 35ms linear;
}

.midi-piano-key--white {
  position: relative;
  z-index: 1;
  height: 100%;
  background: var(--pane);
  border-left: 1px solid rgba(32, 32, 32, 0.62);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.midi-piano-key--white:first-child {
  border-left: none;
}

.midi-piano-key--black {
  position: absolute;
  top: 0;
  z-index: 3;
  width: clamp(7px, calc((100% / var(--white-key-count)) * 0.64), 16px);
  height: 62%;
  transform: translateX(-50%);
  background: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.045);
  border-top: 0;
  border-radius: 0 0 2px 2px;
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.24);
}

.midi-piano-key.is-active {
  background: var(--glow);
  box-shadow:
    inset 0 0 0 1px rgb(var(--glow-channels) / 0.42),
    0 0 12px rgb(var(--glow-channels) / calc(0.18 + (var(--key-velocity, 1) * 0.18)));
}

.midi-piano-key--white.is-active {
  transform: translateY(1px);
}

.midi-piano-key--black.is-active {
  background: var(--glow-dim);
  box-shadow:
    inset 0 0 0 1px rgb(var(--glow-channels) / 0.34),
    0 0 10px rgb(var(--glow-channels) / calc(0.12 + (var(--key-velocity, 1) * 0.14)));
  transform: translateX(-50%) translateY(1px);
}

/* Main footer row */
.footer-inner {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  padding: 0 10px 0 16px;
  gap: 0;
  min-width: 0;
}

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

.footer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 10px;
  background: var(--ink);
  border: none;
  border-radius: 2px;
  color: rgba(255, 255, 255, 0.45);
  cursor: pointer;
  transition:
    color 100ms ease,
    background 100ms ease;
  outline: none;
}

.footer-btn img {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(66%);
  transition: filter 100ms ease;
  user-select: none;
  pointer-events: none;
}

.footer-btn:hover img {
  filter: brightness(0) saturate(100%) invert(90%);
}

.footer-btn:focus-visible {
  outline: 1.5px solid rgba(255, 255, 255, 0.3);
  outline-offset: 1px;
}

.footer-btn[aria-pressed="true"] {
  background: var(--glow);
}

.footer-btn[aria-pressed="true"] img {
  opacity: 1;
  filter: brightness(0);
}

.footer-btn[data-footer-btn="midi"] {
  position: relative;
  margin-left: 10px;
  width: 34px;
  height: 22px;
  padding: 0;
  box-sizing: border-box;
}

.footer-btn[data-footer-btn="midi"]::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 4px;
  bottom: 4px;
  width: 1px;
  background: rgba(255, 255, 255, 0.14);
  pointer-events: none;
}

.footer-btn[data-footer-btn="midi"] img {
  width: 17px;
  height: 17px;
}

.footer-btn[data-footer-btn="midi"][aria-pressed="true"] {
  background: var(--glow);
}

/* Loop button locked (loop-default ON in settings).
   Behaviour only. How it looks is in theme.css with the rest of the transport:
   this used to paint a filled block behind the icon and set the icon itself to
   black, both !important, which was the one control down there that did not
   look like the others — and !important meant no later sheet could say so.

   pointer-events stays on, and that is deliberate rather than an oversight:
   the button does nothing when clicked, but it has to be hoverable or the
   tooltip explaining why never appears. */
.footer-btn--loop-locked {
  pointer-events: auto;
  cursor: default;
}


/* DAW Sync connector group */
.daw-sync-group {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  margin-left: 16px;
  position: relative;
}

/* Trait en L avec coin arrondi + fondus via mask */
.daw-sync-group::before {
  content: "";
  position: absolute;
  left: 26px;
  top: -8px;
  right: 0;
  bottom: -6px;
  border-top: 1px solid rgba(255, 255, 255, 0.15);
  border-left: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 2px 0 0 0;
  -webkit-mask-image: linear-gradient(to right, black 70%, transparent 100%),
                      linear-gradient(to bottom, black 70%, transparent 100%);
  -webkit-mask-size: 100% 1px, 1px 100%;
  -webkit-mask-position: 0 0, 0 0;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-composite: source-over;
  mask-image: linear-gradient(to right, black 70%, transparent 100%),
              linear-gradient(to bottom, black 70%, transparent 100%);
  mask-size: 100% 1px, 1px 100%;
  mask-position: 0 0, 0 0;
  mask-repeat: no-repeat;
  mask-composite: add;
  pointer-events: none;
}

.daw-sync-group::after {
  display: none;
}

/* Espace entre le trait vertical et le bouton Transpose */
.daw-sync-group button:nth-child(3) {
  margin-left: 12px;
}

/* Label CONNECTED / DISCONNECTED au milieu du trait horizontal */
.daw-sync-label {
  position: absolute;
  left: 26px;
  top: -8px;
  transform: translateX(30%) translateY(-50%);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.1em;
  color: rgba(192, 82, 74, 0.75);
  background: var(--sidebar-background);
  padding: 0 4px;
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
}

[data-daw-group].is-active .daw-sync-label {
  color: rgba(154, 230, 180, 0.9);
}

/* Active — lignes vertes */
[data-daw-group].is-active::before {
  border-color: rgba(154, 230, 180, 0.4);
}

/* Le petit bouton toggle rond */
.daw-sync-toggle {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  background: var(--pane);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  outline: none;
  transition: border-color 120ms ease, background 120ms ease;
  position: relative;
  z-index: 1;
}

.daw-sync-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(192, 82, 74, 0.75);
  transition: background 120ms ease;
  pointer-events: none;
  flex-shrink: 0;
}

.daw-sync-toggle:hover {
  border-color: rgba(255, 255, 255, 0.38);
  background: var(--pane);
}

.daw-sync-toggle[aria-pressed="true"] .daw-sync-dot {
  background: var(--success);
}

.daw-sync-toggle[aria-pressed="true"] {
  border-color: rgba(154, 230, 180, 0.55);
}

/* Disabled state for transpose/wrap when DAW sync is off */
.footer-btn--sync-dependent:disabled {
  opacity: 0.28;
  cursor: not-allowed;
  pointer-events: none;
}

.footer-btn--sync-dependent:disabled img {
  filter: brightness(0) saturate(0%) invert(60%);
}

.sidebar-resizer {
  position: absolute;
  top: 0;
  right: -5px;
  z-index: 3;
  width: 10px;
  height: 100%;
  cursor: ew-resize;
  touch-action: none;
  outline: none;
}

.sidebar-resizer::after {
  position: absolute;
  top: 0;
  left: 4px;
  width: 2px;
  height: 100%;
  content: "";
  background: transparent;
  transition:
    background-color 120ms ease,
    opacity 120ms ease;
}

.sidebar-resizer:hover::after,
.sidebar-resizer:focus-visible::after,
.sidebar-resizer.is-active::after {
  background: rgba(255, 255, 255, 0.28);
}

.sidebar-reveal {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 4;
  display: none;
  width: 18px;
  height: 100%;
  padding: 0;
  cursor: e-resize;
  background: transparent;
  border: 0;
}

.sidebar-reveal::after {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 100%;
  content: "";
  background: var(--sidebar-background);
  opacity: 0;
  transition:
    width 120ms ease,
    opacity 120ms ease;
}

.sidebar-reveal:hover::after,
.sidebar-reveal:focus-visible::after {
  width: 5px;
  opacity: 1;
}

#app[data-sidebar-collapsed="true"] .app-sidebar {
  flex-basis: 0;
  width: 0;
  pointer-events: none;
  overflow: hidden;
}

#app[data-sidebar-collapsed="true"] .sidebar-reveal {
  display: block;
}

body.is-resizing,
body.is-resizing * {
  cursor: ew-resize;
  user-select: none;
}

body.is-resizing .app-sidebar {
  transition: none;
}


@media (prefers-reduced-motion: reduce) {
  .app-sidebar,
  .sidebar-resizer::after,
  .sidebar-reveal::after {
    transition: none;
  }
}

/* Volume popup */
.volume-wrapper {
  position: relative;
  display: inline-flex;
}

.volume-popup {
  position: absolute;
  bottom: calc(100% + 28px);
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 140px;
  background: var(--pane);
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 100ms ease;
  box-shadow: 0 8px 32px rgba(0,0,0,0.55), 0 2px 8px rgba(0,0,0,0.3);
  z-index: 20;
}

/* pont invisible pour combler le gap entre bouton et popup + progress bar */
.volume-popup::after {
  content: "";
  position: absolute;
  bottom: -28px;
  left: -20px;
  right: -20px;
  height: 28px;
  z-index: 15;
}

.volume-wrapper:hover .volume-popup,
.volume-popup.is-dragging {
  opacity: 1;
  pointer-events: auto;
}

.volume-track {
  position: relative;
  width: 36px;
  height: 108px;
  background: transparent;
  border-radius: 2px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.volume-track-inner {
  position: relative;
  width: 4px;
  height: 100%;
  background: var(--ink);
  border-radius: 2px;
  pointer-events: none;
  overflow: hidden;
}

.volume-fill {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  background: var(--hairline);
  border-radius: 2px;
  height: 75%;
  pointer-events: none;
}

.footer-btn[data-footer-btn="volume"][aria-pressed="true"] ~ * .volume-fill,
.volume-wrapper:has([aria-pressed="true"]) .volume-fill {
  background: var(--glow);
}

/* ── LES PARAMETRES AVANCES ────────────────────────────────────────────────
 *
 * Un PANNEAU, pas une reglette : ce qu'on regle ici se lit et se pose, un
 * nombre de demi-tons et une tonalite de session, et ni l'un ni l'autre ne se
 * pousse a l'oeil.
 *
 * ET PAS UNE SEULE BOITE. La premiere version en avait quatorze — deux boutons
 * de pas et douze cases de tonalite, tous en rectangles gris — et c'est ce qui
 * la faisait ressembler a une boite de dialogue tombee d'un autre logiciel.
 * Cette fenetre n'a de boites nulle part : ses champs sont du texte pose sur un
 * trait, ses etiquettes sont du texte, et la seule chose qui remplit une
 * surface, c'est l'accent quand quelque chose est choisi. La note qui explique
 * ca est au-dessus de .feedback-input dans src/theme.css, et elle vaut ici mot
 * pour mot.
 *
 * Il reste donc : deux panneaux indicateurs, un nombre, douze lettres, un
 * trait. Tout le reste etait du remplissage.
 */
/* ── LA HAUTEUR ET LE VERROU ───────────────────────────────────────────────
 *
 * Un panneau derriere un diapason. Ils ont passe une heure ecrits a meme la
 * barre, nom et valeur : lisible sans rien ouvrir, et c'etait trop. Un pied de
 * fenetre qui porte deja le sample en cours, le volume, la boucle, le clavier
 * et le transport ne peut pas prendre quatre mots de plus et rester lisible
 * d'un coup d'oeil.
 *
 * Ce qui reste dehors, c'est l'icone, et elle s'allume quand quelque chose est
 * arme : panneau ferme, c'est la seule chose qui dit que la pre-ecoute n'est
 * plus le fichier tel qu'il est. */
.params-wrapper {
  position: relative;
  display: inline-flex;
  margin-right: 10px;
}

/* Arme, le dessin passe a l'accent — la meme chose que dit un bouton du bas
   allume, mais sur un SVG plein plutot que sur un PNG teinte. */
.params-wrapper [aria-pressed="true"] .transport-icon {
  fill: var(--glow);
}

/* ALIGNE SUR SON BORD DROIT, pas centre sur son bouton. Ce bouton vit au bout
   du transport, donc a quelques pixels du bord de la fenetre : un panneau de
   deux cents pixels centre sur lui sortirait de l'ecran par la droite. */
.params-popup {
  position: absolute;
  bottom: calc(100% + 22px);
  right: -6px;
  width: 206px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 100ms ease;
  z-index: 20;
}

/* Le vide entre le panneau et son bouton se traverse a la souris sans que le
   panneau se ferme : sans ca, viser une tonalite demande de ne pas deraper. */
.params-popup::after {
  content: "";
  position: absolute;
  bottom: -28px;
  left: -20px;
  right: -20px;
  height: 28px;
  z-index: 15;
}

.params-popup.is-open {
  opacity: 1;
  pointer-events: auto;
}

.params-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Le nom d'un reglage est un panneau indicateur, comme PLACES et COLLECTIONS :
   la meme loi, les memes valeurs. Son etat se lit au bout de la meme ligne,
   parce que « c'est allume sur quoi » est une question qu'on pose au titre. */
.params-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: calc(10px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.3);
}

.params-label-state {
  letter-spacing: 0.12em;
  color: var(--faint);
  transition: color 120ms ease;
}

.params-label-state.is-on {
  color: var(--glow);
}

/* ── LE PITCH SE COMPTE ────────────────────────────────────────────────────
   Un pas de moins, le nombre, un pas de plus. Les deux pas sont des SIGNES,
   pas des boutons : leur zone de clic est large, leur dessin ne l'est pas. */
.params-stepper {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.params-step {
  width: 30px;
  height: 26px;
  padding: 0;
  font-family: inherit;
  font-size: calc(15px * var(--ui-font-scale, 1));
  line-height: 1;
  /* --dim et pas --faint : vingt pour cent de blanc, c'est le ton d'une chose
     qu'on ne peut PAS toucher. Ces deux-la se cliquent, et une commande qu'on
     ne voit pas est une commande que personne ne trouve. */
  color: var(--dim);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color 120ms ease;
}

.params-step:hover { color: var(--text); }
.params-step:disabled { color: var(--faint); cursor: default; }

/* Le nombre est ce qu'on vient lire : il est le seul element fort du bloc. Des
   chiffres de largeur fixe, sinon passer de 9 a 10 fait sauter le « st ». */
.params-readout {
  min-width: 92px;
  cursor: ns-resize;
  text-align: center;
  font-size: calc(15px * var(--ui-font-scale, 1));
  letter-spacing: 0.02em;
  color: var(--read);
  font-variant-numeric: tabular-nums;
  user-select: none;
  outline: none;
  transition: color 120ms ease;
}

.params-readout.is-set { color: var(--glow); }

/* Eteint par le verrou : le meme gris que celui que le verrou emploie a cote de
   son propre nom pour dire « Off ». Le meme mot dans la meme couleur veut dire
   la meme chose, ce qui evite d'avoir a se demander lequel des deux parle.
   En capitales comme celui du verrou, et a la TAILLE DU NOMBRE : c'est la valeur
   du moment de ce champ, au meme titre que « +5 st ». */
.params-readout.is-off {
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  cursor: default;
}

.params-rule {
  height: 1px;
  background: linear-gradient(90deg,
    transparent 0,
    var(--divider) 4%,
    var(--divider) 96%,
    transparent 100%);
}

/* ── LES DOUZE TONALITES ───────────────────────────────────────────────────
 *
 * Elles ont passe une demi-heure en liste, une par ligne, parce que c'est la
 * forme de Loopcloud. Chez eux elle marche : c'est un menu qui s'ouvre sur
 * toute la hauteur de la fenetre. Ici, dans un panneau de deux cents pixels de
 * large, treize lignes d'UNE lettre font une tour de blanc haute comme la
 * moitie de l'ecran.
 *
 * Deux rangees de six : quarante pixels de haut, et les douze se lisent d'un
 * seul regard au lieu de se derouler. */
.params-keys {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 2px 0;
}

.params-key {
  height: 24px;
  padding: 0;
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.02em;
  color: var(--read);
  background: none;
  border: 0;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  /* Rien que la couleur : plus de fond a faire apparaitre, donc plus d'arrondi
     a dessiner ni de fond a animer. */
  transition: color 120ms ease;
}

/* AU SURVOL, LA LETTRE BLANCHIT, ET RIEN D'AUTRE.
   Il y a eu un fond gris derriere elle : un rectangle qui apparaissait sous le
   pointeur au milieu de onze lettres nues, dans un panneau qui n'a aucune autre
   boite. La couleur suffit a dire « celle-ci », et elle le dit deja partout
   ailleurs dans cette fenetre sans avoir besoin d'un cadre. */
.params-key:hover { color: var(--text); }

/* Choisie : la LETTRE prend l'accent. Meme raison, et la regle porte le survol
   avec elle : sans ca, repasser sur la tonalite posee la blanchirait, et
   l'accent qu'on vient de mettre semblerait s'etre eteint. */
.params-key.is-on,
.params-key.is-on:hover {
  color: var(--glow);
}

/* LA PHRASE SOUS LES CASES EST PARTIE.
   « Samples with no key play as they are » disait la limite du verrou, et elle
   la disait tout le temps, a quelqu'un qui n'ouvre ce panneau que pour poser
   une tonalite. Quatre lignes de gris sous douze lettres, relues a chaque
   ouverture pour une information qu'on retient la premiere fois. Elle est
   maintenant dans l'infobulle du diapason, avec le reste de ce que ce panneau
   fait : lisible quand on se demande, invisible quand on sait. */


/* Pendant qu'on tire une valeur, le curseur s'en va : c'est le nombre qu'on
   regarde, pas la fleche. Le volume et les boutons du clavier font deja ca. */
body.params-dragging,
body.params-dragging *,
body.volume-dragging,
body.volume-dragging *,
body.midi-dragging,
body.midi-dragging * {
  cursor: none !important;
}
/* ── Header ─────────────────────────────────────────────── */
.app-header {
  display: flex;
  align-items: flex-end;
  padding-bottom: 0;
}

.header-inner {
  display: flex;
  align-items: center;
  width: 100%;
  height: var(--header-height);
  /* Symmetric, on the inset the list starts at: see the note at the head of
     the sample list for why the list's own right side is not this number. */
  padding: 0 var(--main-content-pad-left);
  gap: 28px;
}

/* Search */
.header-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 0 240px;
  width: 240px;
  min-width: 0;
  justify-content: space-between;
}

.header-search-text {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--glow);
  white-space: nowrap;
  margin-bottom: -4px;
  cursor: text;
  outline: none;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}

.header-search-text:empty::before {
  content: attr(data-placeholder);
  display: inline-block;
  color: rgba(255, 255, 255, 0.25);
  pointer-events: none;
  font-weight: var(--ui-weight-regular, 400);
  opacity: 1;
  transform: translateY(0);
  transition:
    color 220ms ease,
    text-shadow 260ms ease,
    opacity 180ms ease,
    transform 180ms ease;
}

.header-search-text:focus {
  opacity: 0.85;
}

.header-search-bar {
  position: absolute;
  bottom: -6px;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: var(--pane);
  overflow: hidden;
}

.header-search-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, var(--glow) 0%, rgba(var(--glow-rgb), 0.35) 100%);
  box-shadow: 0 0 7px rgba(var(--glow-rgb), 0.36);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  transition:
    transform 280ms cubic-bezier(0.22, 1, 0.36, 1),
    opacity 180ms ease,
    box-shadow 280ms ease;
}

.header-search-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
}


.header-search-btn img {
  width: 14px;
  height: 14px;
  filter: brightness(0) saturate(0%) invert(66%);
  pointer-events: none;
  transition:
    filter 260ms ease,
    opacity 220ms ease;
}

.header-search:focus-within .header-search-bar::after,
.header-search.phrase-mode .header-search-bar::after,
.header-search.is-placeholder-describe .header-search-bar::after {
  opacity: 1;
  transform: scaleX(1);
}

.header-search:focus-within .header-search-text:empty::before,
.header-search.is-placeholder-describe .header-search-text:empty::before {
  color: rgba(var(--glow-rgb), 0.48);
  text-shadow: 0 0 7px rgba(var(--glow-rgb), 0.22);
}

.header-search:focus-within .header-search-btn img,
.header-search.phrase-mode .header-search-btn img,
.header-search.is-placeholder-describe .header-search-btn img {
  filter:
    var(--glow-filter)
    drop-shadow(0 0 4px rgba(var(--glow-rgb), 0.55));
}

.header-search.is-placeholder-swapping .header-search-text:empty::before {
  opacity: 0;
  transform: translateY(3px);
}

/* ── Phrase Search Toggle ─────────────────────────────────── */
.phrase-search-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  padding: 6px 2px 0px 2px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.28);
  border-radius: 3px;
  margin-left: 2px;
  flex-shrink: 0;
  transition: color 0.15s ease;
}

.phrase-search-toggle svg {
  width: 16px;
  height: 16px;
  transition: filter 0.15s ease;
}

.phrase-search-toggle:hover {
  color: rgba(255, 255, 255, 0.9);
  background: transparent;
}

.phrase-search-toggle:hover svg {
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.4));
}

.phrase-search-toggle.is-active {
  color: var(--glow);
  background: transparent;
}

.phrase-search-toggle.is-active svg {
  filter: drop-shadow(0 0 4px rgba(var(--glow-rgb), 0.6));
}

/* ── Phrase mode: search bar shimmer ─────────────────────────*/
.header-search.phrase-mode .header-search-bar {
  height: 1.5px;
}

.header-search.phrase-mode .header-search-text:empty::before {
  content: "a dark and groovy reese bass…";
  color: rgba(var(--glow-rgb), 0.35);
  font-style: italic;
  text-shadow: 0 0 7px rgba(var(--glow-rgb), 0.18);
}

.header-search.phrase-mode .header-search-text {
  color: var(--glow);
}

/* Controls */
.header-controls {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
}

.header-controls-center {
  display: flex;
  align-items: center;
  gap: 28px;
  flex: 1 1 auto;
  justify-content: center;
}

.header-controls-right {
  display: flex;
  align-items: center;
  gap: 13px;
  margin-left: auto;
}

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

.header-label {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  white-space: nowrap;
}

/* BPM box */
.header-bpm {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  font-family: inherit;
  color: var(--read);
  padding: 1px 3px;
  border: 1px solid var(--hairline);
  border-radius: 2px;
  min-width: 38px;
  width: 44px;
  text-align: center;
  background: transparent;
  outline: none;
  cursor: ns-resize;
}

.header-bpm:focus {
  cursor: text;
  border-color: var(--hairline);
}

/* Key / Scale selects */
.header-select {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: var(--ink);
  border: none;
  border-radius: 2px;
  padding: 1px 7px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 100ms ease;
  outline: none;
}

.header-select:hover {
  background: var(--pane);
}

.header-arrow {
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(66%);
  pointer-events: none;
  user-select: none;
}

/* Dropdowns */
.header-dropdown {
  position: relative;
}

.header-dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--pane);
  border-radius: 2px;
  padding: 3px;
  display: none;
  flex-direction: column;
  z-index: 100;
  min-width: 100%;
  white-space: nowrap;
}

.header-dropdown.is-open .header-dropdown-menu {
  display: flex;
}

.header-dropdown.is-open::after {
  content: "";
  position: absolute;
  top: calc(100% + 0px);
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-bottom: 7px solid var(--hairline);
  pointer-events: none;
  z-index: 101;
}

.header-dropdown-item {
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.5);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 3px 10px 3px 28px;
  text-align: left;
  justify-content: flex-start;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease;
  position: relative;
}

.header-dropdown-item::before {
  content: "";
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  background-image: url("../icons/check.png");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  filter: brightness(0) saturate(100%) invert(22%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%);
}

.header-dropdown-item:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--read);
}

.header-dropdown-item.is-selected {
  color: var(--read);
}

.header-dropdown-item.is-selected::before {
  background-image: url("../icons/check.png");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  filter: brightness(0) saturate(0%) invert(66%);
}

/* Key piano menu */
.key-menu {
  padding: 10px;
  width: 268px;
}

.key-tabs {
  display: flex;
  margin-bottom: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.1);
}

.key-tab {
  flex: 1;
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.35);
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 4px 0 8px;
  cursor: pointer;
  margin-bottom: -1px;
  transition: color 80ms ease, border-color 80ms ease;
}

.key-tab.is-active {
  color: var(--read);
  border-bottom-color: var(--read);
}

/* Piano layout:
   7 naturals = 7 equal columns
   accidentals row: 6 slots over those 7 cols
   Slot centers: between col1/2, col2/3, gap, col4/5, col5/6, col6/7
   = starts at 0.5 col offset, spaced every 1 col, with empty at position 3 */
.key-piano {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.key-piano-bottom {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
}

/* Top row: 6 slots but aligned over gaps between 7 naturals.
   We use a 14-col grid (2 per natural), slots start at col 2 */
.key-piano-top {
  display: grid;
  grid-template-columns: repeat(14, 1fr);
  gap: 0;
  padding: 0 calc(100% / 14);
}

.key-piano-top-cell {
  grid-column: span 2;
  padding: 0 2.5px;
  display: flex;
  align-items: center;
}

.key-piano-top-cell .key-btn {
  flex: 1;
}

.key-piano-top-cell--empty {
  pointer-events: none;
}

.key-btn {
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.45);
  background: var(--pane);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 3px;
  padding: 6px 0;
  width: 100%;
  text-align: center;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease;
}

.key-btn:hover {
  color: var(--read);
  background: var(--pane);
}

.key-btn.is-selected {
  color: var(--read);
  background: var(--pane);
  border-color: rgba(255,255,255,0.2);
}


.header-btn {
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: var(--ink);
  border: none;
  border-radius: 2px;
  padding: 1px 9px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 100ms ease, color 100ms ease;
  outline: none;
}

.header-btn:hover {
  background: var(--pane);
}

.header-btn[aria-pressed="true"] {
  background: var(--glow);
  color: var(--on-glow);
}

.header-btn--clear {
  background: var(--glow);
  color: var(--on-glow);
  font-weight: var(--ui-weight-medium, 500);
}

.header-btn--clear:hover {
  background: var(--glow);
}
/* Any button in key piano */
.key-btn--any {
  width: 100%;
  margin-top: 5px;
}

/* ── Subheader ───────────────────────────────────────────── */
.app-subheader {
  flex: 0 0 auto;
  background: var(--raised);
}

.subheader-inner {
  display: flex;
  align-items: center;
  height: 36px;
  /* Same inset as the header above, on both sides, and the same left edge as
     the list below. */
  padding: 0 var(--main-content-pad-left);
  gap: 6px;
}

/* Dropdown trigger */
.subheader-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.45);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 3px 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 100ms ease, background 100ms ease;
  outline: none;
}

.subheader-dropdown:not(:first-child) .subheader-trigger {
  padding-left: 8px;
}

.subheader-trigger:hover {
  color: var(--read);
  background: rgba(255,255,255,0.05);
}

.subheader-dropdown.is-open .subheader-trigger,
.subheader-dropdown.has-selection .subheader-trigger {
  color: var(--read);
}

.subheader-dropdown.has-selection .subheader-trigger {
  color: var(--glow);
}

/* Dropdown menu */
.subheader-dropdown {
  position: relative;
}

.subheader-dropdown[hidden] {
  display: none !important;
}

.subheader-dropdown:first-child {
  margin-left: -8px;
}

.subheader-menu {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  background: var(--pane);
  border-radius: 2px;
  padding: 3px;
  display: none;
  flex-direction: column;
  z-index: 100;
  min-width: 140px;
  white-space: nowrap;
}

.subheader-dropdown.is-open .subheader-menu {
  display: flex;
}

/* Legacy fallback arrow for non-scrollable subheader menus */
.subheader-menu:not(.subheader-menu--tags):not(.subheader-menu--source)::before {
  content: "";
  position: absolute;
  top: -7px;
  left: 16px;
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-bottom: 7px solid var(--hairline);
  pointer-events: none;
}

.subheader-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.5);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 5px 6px 5px 28px;
  text-align: left;
  justify-content: flex-start;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease;
  position: relative;
}

.subheader-item-label {
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.subheader-item::before {
  content: "";
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  background-image: url("../icons/check.png");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  filter: brightness(0) saturate(100%) invert(22%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%);
}

.subheader-item:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--read);
}

.subheader-item.is-selected {
  color: var(--read);
}

.subheader-item.is-selected::before {
  background-image: url("../icons/check.png");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  filter: brightness(0) saturate(0%) invert(66%);
}

.subheader-exclude-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  padding: 0;
  border: none;
  border-radius: 2px;
  background: transparent;
  color: rgba(255,255,255,0.34);
  opacity: 0;
  cursor: pointer;
  flex: 0 0 auto;
  transition: opacity 80ms ease, background 80ms ease, color 80ms ease;
}

.subheader-exclude-btn svg {
  width: 12px;
  height: 12px;
  display: block;
}

.subheader-item:hover .subheader-exclude-btn,
.subheader-item:focus-within .subheader-exclude-btn,
.subheader-item.is-excluded .subheader-exclude-btn {
  opacity: 1;
}

.subheader-exclude-btn:hover {
  background: rgba(220, 55, 55, 0.18);
  color: rgba(255, 98, 86, 0.96);
}

.subheader-item.is-excluded {
  color: rgba(255, 112, 100, 0.92);
}

.subheader-item.is-excluded::before {
  opacity: 0.38;
  filter: var(--glow-filter);
}

.subheader-item.is-excluded .subheader-exclude-btn {
  background: rgba(220, 55, 55, 0.12);
  color: rgba(255, 98, 86, 0.9);
}

/* Separator between dropdowns and type toggle */
.subheader-type {
  display: flex;
  align-items: center;
  margin-left: auto;
  background: var(--ink);
  border-radius: 2px;
  padding: 2px;
  gap: 1px;
}

/* Sample count — far right of subheader */
.subheader-sample-count {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.22);
  white-space: nowrap;
  flex-shrink: 0;
  padding-left: 12px;
  letter-spacing: 0.02em;
}

.subheader-type-btn {
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.38);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 2px 10px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 100ms ease, color 100ms ease;
  outline: none;
}

.subheader-type-btn:hover {
  color: rgba(255,255,255,0.7);
}

.subheader-type-btn.is-active {
  background: transparent;
  color: var(--glow);
}

/* Tags menu — wider, scrollable, grouped */
.subheader-menu--tags {
  min-width: 180px;
  max-height: 320px;
  overflow-y: auto;
  overflow-x: visible;
  /* La barre est celle du theme. Elle etait peinte ici en var(--app-background),
     c'est-a-dire DANS LA COULEUR DU FOND QU'ELLE RECOUVRE : mesure, pastille
     rgb(5,5,7) sur un menu rgb(9,9,12), donc invisible. Le theme pose une
     pastille blanche a 16 % sur « * », et c'est ce qu'on voit partout ailleurs
     dans la fenetre — mais une regle de classe bat l'universelle, quel que soit
     l'ordre des feuilles, et celle-ci la battait. */
}

.subheader-menu--source {
  min-width: 260px;
  max-width: 340px;
  max-height: 300px;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 5px 3px 6px;
  /* Meme barre, meme raison : voir le menu des tags juste au-dessus. */
}

.subheader-source-search-wrap {
  position: sticky;
  top: -5px;
  z-index: 2;
  padding: 5px 5px 6px;
  background: var(--app-background);
}

.subheader-source-search {
  width: 100%;
  height: 25px;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 2px;
  background: rgba(255,255,255,0.06);
  color: rgba(255,255,255,0.76);
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  outline: none;
  padding: 0 8px;
}

.subheader-source-search::placeholder {
  color: rgba(255,255,255,0.28);
}

.subheader-source-search:focus {
  border-color: rgba(var(--glow-rgb), 0.45);
  background: rgba(255,255,255,0.08);
}

.subheader-source-tree {
  min-height: 24px;
}

.subheader-source-row {
  --source-depth: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-height: 25px;
  padding: 4px 8px 4px calc(8px + var(--source-depth) * 14px);
  border-radius: 2px;
  color: rgba(255,255,255,0.55);
  cursor: pointer;
  user-select: none;
  transition: background 80ms ease, color 80ms ease;
}

.subheader-source-chevron {
  width: 10px;
  height: 10px;
  flex: 0 0 auto;
  color: rgba(255,255,255,0.34);
  opacity: 0.6;
  transition: transform 90ms ease, opacity 80ms ease;
}

.subheader-source-chevron svg {
  display: block;
  width: 10px;
  height: 10px;
}

.subheader-source-row.is-open .subheader-source-chevron {
  transform: rotate(90deg);
  opacity: 0.85;
}

.subheader-source-row:hover {
  background: rgba(255,255,255,0.08);
  color: var(--read);
}

.subheader-source-check {
  width: 13px;
  height: 13px;
  margin: 0;
  accent-color: var(--glow);
  flex: 0 0 auto;
  cursor: pointer;
}

.subheader-source-name {
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

.subheader-source-count {
  flex: 0 0 auto;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.28);
  font-variant-numeric: tabular-nums;
}

/* La barre de ces menus est celle du theme, et rien n'est redit ici.
   Il y avait quatre regles a cet endroit, et elles peignaient la pastille en
   var(--app-background) sur un menu dont le fond est var(--ink) : la meme
   couleur, ou presque, donc une barre qu'on ne voit pas. Le theme pose deja
   du blanc a 16 %, et 26 % au survol, pour TOUTES les barres de la fenetre —
   mais « .subheader-menu--tags::-webkit-scrollbar-thumb » bat « * », et
   l'ordre des feuilles n'y change rien. Les enlever, c'est la rendre. */

/* Arrow for all subheader menus — on the parent since scrollable menus clip ::before */
.subheader-dropdown.is-open::after {
  content: "";
  position: absolute;
  top: calc(100% + 3px);
  left: 16px;
  width: 0;
  height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-bottom: 7px solid var(--hairline);
  pointer-events: none;
  z-index: 101;
}

.subheader-group-label {
  display: block;
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.25);
  padding: 8px 10px 3px;
  pointer-events: none;
  user-select: none;
}

.subheader-group-label:first-child {
  padding-top: 3px;
}

/* ── BPM popup ───────────────────────────────────────────────── */
.bpm-menu {
  width: 260px;
  padding: 14px 14px 10px;
  flex-direction: column;
  gap: 10px;
}

.header-dropdown.is-open .bpm-menu {
  display: flex;
}

.bpm-mode-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 28px;
}

.bpm-radio-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.6);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  min-width: 70px;
}

.bpm-radio {
  accent-color: var(--glow);
  width: 14px;
  height: 14px;
  cursor: pointer;
  flex-shrink: 0;
}

.bpm-exact-input,
.bpm-range-input {
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 3px 6px;
  outline: none;
  width: 52px;
  text-align: center;
  transition: border-color 100ms ease;
}

.bpm-exact-input:focus,
.bpm-range-input:focus {
  border-color: var(--hairline);
}

.bpm-range-inputs {
  display: flex;
  align-items: center;
  gap: 6px;
}

.bpm-range-sep {
  font-size: calc(13px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.35);
}

.bpm-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4px;
  padding-top: 10px;
  border-top: 1px solid rgba(255,255,255,0.08);
}

.bpm-clear {
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.35);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  transition: color 80ms ease;
}

.bpm-clear:hover {
  color: rgba(255,255,255,0.6);
}

.bpm-save {
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.5);
  background: var(--pane);
  border: none;
  border-radius: 4px;
  padding: 4px 16px;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease;
}

.bpm-save:hover {
  background: var(--pane);
  color: var(--read);
}

/* ── Sample List ─────────────────────────────────────────── */

/*
  Grid columns (left → right):
  [left-pad] [pack 52px] [type-icon 28px] [name flex] [length 56px] [tempo 56px] [key 56px] [scale 56px] [right-pad]
*/
/* ══ ONE EDGE ON THE LEFT, AND ONE REASON FOR THE OTHER ONE ════════════════
   The search field, the filters and the list are three bars in one column, and
   each used to decide its own inset: twenty-four for the two bars above,
   thirty-eight for the list. So Pack began fourteen pixels right of
   Instruments - an edge nobody drew on purpose, visible the moment you look
   down the left side of the window. All three read --main-content-pad-left
   now, so the question cannot be answered twice.

   THE RIGHT IS NOT THE SAME NUMBER, and that is deliberate. The two bars are
   symmetric: twenty-four on both sides, so Clear and One-Shots sit as far from
   the edge as the search field and Instruments do on the other side. That is
   what the owner asked for, having seen both.

   The list cannot follow them out there. A row carries two controls against
   its right edge - the heart, and the similar mark that fades in on hover -
   and any column reaching past them is sitting under a button.

   The heart's ink stops on that same inset, so the gap on the right is the gap
   on the left: asked for in those words. Its box sits four short of it,
   because the button pads itself by four so its target is not tight around the
   drawing, and what is being aligned is the drawing.

   Everything else is written off that one number too. The similar mark sits
   six pixels from the heart - the owner picked that spacing in companion mode
   and asked for it here - and its box reaches thirty-nine past the inset,
   which is the furthest right the last column can go. Not margins somebody
   liked the look of: move the inset and all four follow, in both modes,
   because the companion strip is the same three lines with a different inset.
   The selftest checks the arithmetic rather than the numbers.

   The consequence, stated rather than hidden: the Scale column stops some
   forty pixels short of One-Shots above it. Closing that gap means moving
   those two controls somewhere else first.
   ═════════════════════════════════════════════════════════════════════════ */
:root {
  --similarity-panel-width: 326px;
  --main-content-pad-left: 24px;
  /* 17 + 22 pour la marque et sa boite, et DIX DE PLUS pour respirer : une
     valeur qui touche un bouton se lit comme une partie de lui. Mesure avant :
     quatorze pixels entre « Major » et les deux cercles, et le proprietaire l'a
     vu tout de suite. */
  --main-content-pad-right: calc(var(--main-content-pad-left) + 49px);
  --sl-pad-left: 24px;
  --sl-pad-right: 20px;
  --sl-col-pack: 52px;
  --sl-col-type: 28px;
  --sl-col-waveform: 140px;
  /* CHAQUE COLONNE FAIT LA LARGEUR DE SON MOT, ET C'EST CE QUI LES ESPACE.
   *
   * Elles faisaient cinquante-six toutes les quatre. Un pas regulier, sauf que
   * le mot est cale a gauche dans sa colonne : l'espace qu'on VOIT entre deux
   * titres est ce qui reste de la colonne apres le mot, plus l'ecart. Avec des
   * mots de 39, 48, 32 et 42, ca donnait 33, 24 et 40 — trois espaces
   * differents sous une regle parfaitement reguliere.
   *
   * Mesures dans le moteur, sort compris : Length 39, Tempo 48, Key 32, Scale
   * 42. Les colonnes valent ca, donc ce qui separe deux titres est l'ecart et
   * rien d'autre, le meme partout. Changer la police ou un libelle demande de
   * remesurer, et le controle qui suit garde au moins leur ordre.
   *
   * Les soixante pixels ainsi rendus vont au nom, qui est la colonne souple. */
  --sl-col-length: 39px;
  --sl-col-tempo: 48px;
  --sl-col-key: 33px;
  --sl-col-scale: 42px;
  --sl-col-gap: 16px;
  /* Key et Scale etaient a quatre l'une de l'autre quand toutes les autres
     paires sont a seize. Cet ecart-ci reste une variable a part parce que le
     mode compagnon retire la colonne Scale et doit emmener son ecart avec
     elle, sinon la rangee finit sur seize pixels de rien. */
  --sl-col-gap-key-scale: 16px;
  --sl-col-gap-tight: 10px;
}

.sample-list {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 0;
  padding-top: 12px;
  overflow-x: visible;
}

/* Shared grid template for header + rows — tags col removed */
.sample-list-header,
.sample-row {
  display: grid;
  grid-template-columns:
    var(--sl-pad-left)
    var(--sl-col-pack)
    4px
    var(--sl-col-type)
    8px
    1fr
    var(--sl-col-gap)
    var(--sl-col-waveform)
    var(--sl-col-gap)
    var(--sl-col-length)
    var(--sl-col-gap)
    var(--sl-col-tempo)
    var(--sl-col-gap)
    var(--sl-col-key)
    var(--sl-col-gap-key-scale)
    var(--sl-col-scale)
    var(--sl-pad-right);
  align-items: center;
  min-width: 0;
}

/* Column slot assignments (grid-column) */
/* Slot 1  = left-pad spacer */
/* Slot 2  = pack */
/* Slot 3  = 4px gap */
/* Slot 4  = type icon */
/* Slot 5  = 8px gap */
/* Slot 6  = name */
/* Slot 7  = gap */
/* Slot 8  = waveform */
/* Slot 9  = gap */
/* Slot 10 = length */
/* Slot 11 = gap */
/* Slot 12 = tempo */
/* Slot 13 = gap */
/* Slot 14 = key */
/* Slot 15 = gap */
/* Slot 16 = scale */
/* Slot 17 = gap */
/* Slot 18 = rating */
/* Slot 19 = right-pad spacer */

.sample-col--pack      { grid-column: 2;  justify-self: start; }
.sample-col--type      { grid-column: 4;  justify-self: start; }
.sample-col--name      { grid-column: 6;  justify-self: start; min-width: 0; width: 100%; }
.sample-col--waveform  { grid-column: 8;  justify-self: start; }
.sample-col--length    { grid-column: 10; justify-self: start; }
.sample-col--tempo     { grid-column: 12; justify-self: start; }
.sample-col--key       { grid-column: 14; justify-self: start; }
.sample-col--scale     { grid-column: 16; justify-self: start; }

/* Et la valeur se centre sous son titre, au lieu de se caler a gauche de lui.
 *
 * Une colonne fait la largeur de son MOT, et les valeurs sont plus courtes :
 * « 98 » sous « Tempo », « D » sous « Key ». Calees a gauche, elles laissent
 * derriere elles un vide proportionnel a la longueur du titre, donc les memes
 * trois espaces inegaux un rang plus bas. Centrees, ce qui depasse se partage
 * des deux cotes et la colonne de chiffres se lit comme une colonne. */
.sample-row .sample-col--length,
.sample-row .sample-col--tempo,
.sample-row .sample-col--key,
.sample-row .sample-col--scale { justify-self: center; }
.sample-col--rating    { display: none; }

/* ── Header row ── */
.sample-list-header {
  height: 28px;
  margin-bottom: 8px;
}

/* Toutes les cellules header alignées en bas — traits au même niveau */
.sample-list-header .sample-col--pack,
.sample-list-header .sample-col--type,
.sample-list-header .sample-col--name,
.sample-list-header .sample-col--length,
.sample-list-header .sample-col--tempo,
.sample-list-header .sample-col--key,
.sample-list-header .sample-col--scale,
.sample-list-header .sample-col--rating {
  height: 100%;
  display: flex;
  align-items: flex-end;
  padding-bottom: 4px;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border-bottom: 1px solid rgba(255, 255, 255, 0.4);
}

/* Waveform: no title, no border */
.sample-list-header .sample-col--waveform {
  height: 100%;
  display: flex;
  font-size: 0;
  border-bottom: none;
}

.sample-list-header .sample-col--rating {
  display: none;
}

.sample-list-header .sample-col--name {
  border-bottom: none;
}

.sample-list-header .sample-col--name .sample-sort-btn {
  position: relative;
}

.sample-list-header .sample-col--name .sample-sort-btn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 1px;
  background: rgba(255, 255, 255, 0.4);
}

/* The type-icon column header has no label */
.sample-list-header .sample-col--type {
  border-bottom: 1px solid rgba(255, 255, 255, 0.4);
}

/* Pack avatar */
.pack-avatar {
  width: 40px;
  height: 40px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: 600;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.82);
  flex-shrink: 0;
  user-select: none;
  pointer-events: none;
  position: relative;
  overflow: hidden;
}

.pack-avatar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255,255,255,0.18) 0%, transparent 60%);
  border-radius: inherit;
  pointer-events: none;
}

.sample-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--read);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  outline: none;
}

.sample-sort-arrow {
  width: 8px;
  height: 8px;
  filter: brightness(0) saturate(0%) invert(66%);
  pointer-events: none;
  user-select: none;
  transition: transform 200ms ease, filter 150ms ease;
}

/* Double arrow wrapper — shown by default */
.sample-sort-arrows {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0px;
  pointer-events: none;
  user-select: none;
}

.sample-sort-arrows .sample-sort-arrow {
  display: block;
  width: 6px;
  height: 6px;
}

/* Top arrow = pointing up (rotated 180°) */
.sample-sort-arrows .sample-sort-arrow--up {
  transform: rotate(180deg);
}

/* When sorting active: hide the double, show single arrow */
.sample-sort-btn.is-active .sample-sort-arrows {
  display: none;
}

.sample-sort-btn .sample-sort-arrow--single {
  display: none;
}

.sample-sort-btn.is-active .sample-sort-arrow--single {
  display: block;
  transform: rotate(0deg);
}

.sample-sort-btn.is-active.is-desc .sample-sort-arrow--single {
  transform: rotate(180deg);
}

/* Active sort column — texte + flèche en surbrillance */
.sample-sort-btn.is-active {
  color: rgba(255, 255, 255, 0.92);
}

.sample-sort-btn.is-active .sample-sort-arrow {
  filter: brightness(0) saturate(100%) invert(92%);
}

/* ── Sample rows ── */
.sample-row {
  height: 60px;
  border-bottom: 1px solid rgba(255,255,255,0.05);
  transition: background 80ms ease;
}

.sample-row:hover {
  background: rgba(255,255,255,0.03);
  cursor: grab;
}

/* Waveform keeps its own pointer cursor */
.sample-row:hover .sample-col--waveform {
  cursor: pointer;
}

.sample-row.is-context-selected,
.sample-row.is-similarity-source {
  background: var(--raised);
}

/* Name col: stacks name + tags */
.sample-row .sample-col--name {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  overflow: hidden;
}

/* Type icon (loop / oneshot) */
.sample-type-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(66%);
  pointer-events: none;
  display: block;
}

.sample-type-pending {
  width: 18px;
  height: 18px;
  /* The ring is the accent held back, not grey. Same treatment as the sidebar
     spinner: dim the same colour rather than introduce a second one.
     .sample-type-waiting below deliberately stays neutral, because it means the
     opposite thing: nothing is running on this file yet. */
  border: 2px solid rgba(var(--glow-rgb), 0.24);
  border-top-color: var(--glow);
  border-radius: 999px;
  display: block;
  animation: sidebar-spin 700ms linear infinite;
  pointer-events: none;
}

/* Waiting its turn.
 *
 * Three states share this column and the shapes have to say which is which
 * without a legend. Solid ring, accent, turning: this file is being worked on.
 * Broken ring, accent, turning slowly: its place is in the queue and will be.
 * Broken ring, grey, still (.sample-type-waiting): nobody has asked.
 *
 * So the accent says "this is going to happen", the gaps say "not yet", and the
 * speed says which of the two is happening now. */
.sample-type-queued {
  width: 18px;
  height: 18px;
  display: block;
  border: 2px dashed rgba(var(--glow-rgb), 0.42);
  border-radius: 999px;
  animation: sidebar-spin 2600ms linear infinite;
  pointer-events: none;
}

.sample-type-icon.is-type-pending {
  opacity: 0.28;
}

.sample-type-icon.is-type-empty {
  opacity: 0;
}

/* Name text — fade right instead of ellipsis */
.sample-name {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  white-space: nowrap;
  overflow: hidden;
  width: 100%;
  position: relative;
  -webkit-mask-image: linear-gradient(to right, black 75%, transparent 100%);
  mask-image: linear-gradient(to right, black 75%, transparent 100%);
}

/* Waveform */
.sample-waveform {
  width: 140px;
  height: 32px;
  display: flex;
  align-items: center;
  gap: 1.5px;
  flex-shrink: 0;
  cursor: pointer;
}

.sample-waveform svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Tempo / Key / Scale / Length text */
.sample-row .sample-col--tempo,
.sample-row .sample-col--key,
.sample-row .sample-col--scale,
.sample-row .sample-col--length {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
}

/* Rating dots */
.sample-rating {
  display: flex;
  align-items: center;
  gap: 4px;
}

.rating-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pane);
  border: none;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 80ms ease;
}

.rating-dot.is-filled {
  background: var(--read);
}

/* Favorite — overlay button on the right of the row, visible on hover or when favorited */
.sample-row {
  position: relative;
}

/* LE COEUR EST LA SUR TOUTES LES LIGNES, AIME OU NON.
 *
 * Il n'apparaissait qu'au survol, donc rien ne disait qu'une ligne PEUT etre
 * aimee : on decouvrait le bouton en passant dessus par hasard. Une commande
 * qui ne se montre qu'a ceux qui la connaissent deja n'est pas une commande.
 *
 * Ce qui distingue les deux etats n'est donc plus la presence mais le dessin :
 * creux quand ce n'est pas aime, plein quand ca l'est. Voir plus bas. */
.sample-favorite {
  position: absolute;
  /* LE COEUR EST AUSSI LOIN DU BORD DROIT QUE LA POCHETTE L'EST DU GAUCHE.
   *
   * Moins quatre, parce que le bouton porte quatre pixels de retrait pour que
   * sa zone cliquable ne soit pas collee au dessin : son ENCRE s'arrete donc
   * exactement sur le retrait. C'est le dessin qu'on aligne, pas la boite — la
   * pochette, elle, commence a --main-content-pad-left tout court.
   *
   * Ecrit en fonction du retrait et pas en dur : le mode compagnon a le sien,
   * quatorze, et cette ligne y est deja juste. */
  right: calc(var(--main-content-pad-left) - 4px);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  padding: 4px;
  cursor: pointer;
  outline: none;
  z-index: 2;
}

.sample-similar-btn {
  position: absolute;
  /* Il suit le coeur a six pixels : l'ecart que le proprietaire a choisi en
     mode compagnon et redemande ici, ce qui fait qu'un seul jeu de trois lignes
     sert aux deux modes. Plus dix-sept parce que ce bouton-ci porte trois
     pixels autour de son dessin, et le coeur quatre autour du sien. */
  right: calc(var(--main-content-pad-left) + 17px);
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  outline: none;
  opacity: 0;
  transition: opacity 120ms ease, transform 120ms ease;
  z-index: 2;
}

.sample-row:hover .sample-similar-btn,
.sample-row.is-context-selected .sample-similar-btn,
.sample-row.is-similarity-source .sample-similar-btn {
  opacity: 1;
}

.sample-similar-btn:hover {
  transform: translateY(-50%) scale(1.04);
}

/* ── DEUX CERCLES EGAUX, L'UN DEVANT L'AUTRE ────────────────────────────────
 *
 * Le dessin d'origine posait un anneau sur un disque, de deux tailles (9 et 10)
 * et a deux hauteurs (3 et 2). Trois desaccords sur seize pixels, et surtout un
 * cercle qu'on voyait A TRAVERS l'autre : les quatre arcs se croisaient au
 * milieu et la marque avait l'air transparente. C'est le mot du proprietaire,
 * et c'est ce que ce dessin corrige.
 *
 * Les deux cercles restent entiers et egaux. Celui de gauche passe DEVANT :
 * tout ce que le second aurait dessine a moins de 8,4 de son centre est retire,
 * donc l'arc qui traversait le premier n'existe plus et il reste une coupure
 * franche entre les deux. C'est une occlusion, pas une fusion — deux cercles,
 * pas un.
 *
 * DEUX CHOSES ONT ETE MESUREES PLUTOT QUE CHOISIES.
 *
 * Le masque est un DISQUE et pas une bande le long du premier cercle. Une bande
 * de 5,6 ne coupe qu'aux croisements : l'arc du second qui plonge dans le
 * premier depasse de 2,8 et il en restait un eclat vertical au milieu, c'est-a-
 * dire exactement l'effet de transparence qu'on enleve. Vu a l'ecran avant
 * d'etre corrige.
 *
 * Et le trait vaut 2,3 sur une grille de 24, soit 1,53 px a la taille reelle,
 * pas 2. Rendu a 16 px et agrandi pixel par pixel, un trait de 2 avec un disque
 * de 7,4 rend deux anneaux qui se rejoignent dans une bouillie grise : a cette
 * taille la coupure est mangee par l'anticrenelage. A 2,3 et 8,4 les deux
 * cercles se lisent et la coupure aussi.
 *
 * Pose en MASQUE et pas en image de fond, pour que la couleur reste celle du
 * theme : le fond est une couleur pleine que le masque decoupe, donc le survol
 * n'a qu'a changer cette couleur et la marque suit l'accent de la vue. En
 * data: URI plutot qu'en fichier, parce qu'une URL dans une feuille de style se
 * resout contre la feuille et pas contre le document — le piege qui avait fait
 * disparaitre les icones des places le jour ou elles ont recu une couleur.
 * ────────────────────────────────────────────────────────────────────────── */
.sample-similar-glyph,
.sample-ctx-similar-icon {
  --similar-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cdefs%3E%3Cmask id='c'%3E%3Crect width='24' height='24' fill='%23fff'/%3E%3Ccircle cx='7.35' cy='12' r='8.4' fill='%23000'/%3E%3C/mask%3E%3C/defs%3E%3Cg fill='none' stroke='%23000' stroke-width='2.3'%3E%3Ccircle cx='16.65' cy='12' r='6.05' mask='url(%23c)'/%3E%3Ccircle cx='7.35' cy='12' r='6.05'/%3E%3C/g%3E%3C/svg%3E");
  -webkit-mask-image: var(--similar-mark);
  mask-image: var(--similar-mark);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* PLUS CLAIRS QUE LA LIGNE QU'ILS ACCOMPAGNENT, ET C'EST VOULU.
 *
 * L'interface tient sur quatre degres de blanc — 0,94 pour ce qu'on lit, 0,62
 * pour ce qu'on releve, 0,44 pour ce qui est present, 0,20 pour ce qui separe —
 * et ces deux-la sont a 0,8, qui n'en est aucun. Ils ont ete mis a 0,62 avec la
 * duree et la tonalite d'a cote, regarde a l'ecran, et remis ici : le
 * proprietaire prefere les voir. Ce sont les deux seules choses de la rangee
 * sur lesquelles on CLIQUE, et non des valeurs a lire.
 *
 * Le seul invariant qui reste est qu'ils portent le meme degre tous les deux,
 * et le controle ne garde que celui-la. */
.sample-similar-glyph {
  width: 16px;
  height: 16px;
  display: block;
  background-color: rgba(255, 255, 255, 0.8);
}

/* Survole : voila ce que ce bouton fera. Clique : voila ce qu'il fait.
 *
 * La ligne d'ou part la recherche portait la marque en blanc, comme toutes les
 * autres — donc rien a l'ecran ne disait laquelle des lignes le panneau est en
 * train de repondre. C'est le meme accent que partout ailleurs, celui de la
 * place ou de la collection ouverte, et il passe par la meme variable : un
 * dossier bleu donne une marque bleue sans que rien ici ne le sache. */
.sample-similar-btn:hover .sample-similar-glyph,
.sample-row.is-similarity-source .sample-similar-glyph {
  background-color: var(--glow);
}

/* CREUX QUAND CE N'EST PAS AIME, PLEIN QUAND CA L'EST.
 *
 * Le contour n'est pas un second coeur : c'est favorite.png moins sa propre
 * erosion, calcule par scripts/make-place-icons.py. Un coeur dessine a la main
 * a des lobes plus etroits et une pointe plus aigue que celui-ci — compare cote
 * a cote avant d'ecrire ceci — et l'application en aurait montre deux, le plein
 * dans la barre laterale et les menus, l'autre dans la rangee. Ici la
 * silhouette exterieure est la meme au pixel pres : le controle ligne par ligne
 * du script trouve zero difference de bord.
 *
 * Et la MEME couleur que la marque « sons proches » d'a cote, demandee comme
 * telle : blanc a 80 %. Le filtre ne sert qu'a rendre le png blanc, l'opacite
 * fait le reste, et ce nombre est celui de la marque — voir la note au-dessus
 * d'elle pour pourquoi c'est 0,8 et pas le degre des lectures.
 *
 * L'URL est relative a CETTE feuille et pas au document — src/styles.css est un
 * dossier plus bas que icons/, d'ou le ../. C'est le piege qui avait fait
 * disparaitre les icones des places le jour ou elles ont recu une couleur. */
.sample-favorite img {
  content: url("../icons/favorite-outline.png");
  width: 14px;
  height: 14px;
  pointer-events: none;
  filter: brightness(0) saturate(0%) invert(100%);
  opacity: 0.8;
  transition: filter 120ms ease, opacity 120ms ease;
}

/* Survoler le bouton lui-meme montre ce qu'il fera, comme pour son voisin. */
.sample-favorite:hover img {
  filter: var(--glow-filter);
  opacity: 1;
}

.sample-favorite.is-favorited img {
  content: url("../icons/favorite.png");
  filter: var(--glow-filter);
  opacity: 1;
}

/* La meme marque dans le menu contextuel, a quatorze pixels : le meme masque,
   pose plus haut dans la feuille. Deux dessins d'une meme icone est une icone
   qu'on ne reconnait pas. */
.sample-ctx-similar-icon {
  width: 14px;
  height: 14px;
  display: inline-block;
  background-color: rgba(255, 255, 255, 0.76);
}

.sidebar-ctx-item:hover .sample-ctx-similar-icon {
  background-color: var(--glow);
}

/* Tags inline under sample name */
.sample-tags-row {
  position: relative;
  overflow: hidden;
  width: 100%;
}

.sample-tags-scroll {
  display: flex;
  flex-direction: row;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.sample-tags-scroll::-webkit-scrollbar {
  display: none;
}

/* Fade-out on the right side — mask so it works on any bg */
.sample-tags-fade {
  display: none;
}

/* LES TAGS COMMENCENT OU COMMENCE LE NOM.
 *
 * .sample-name n'a aucun retrait horizontal, donc son texte demarre au bord de
 * la colonne. Chaque pastille de tag porte `padding: 1px 4px`, donc le texte du
 * PREMIER tag demarrait quatre pixels plus loin : deux lignes l'une sous
 * l'autre qui ne partent pas du meme endroit, ce qui se voit d'autant mieux que
 * la seconde est plus petite et plus pale.
 *
 * La rangee est decalee de ces quatre pixels et les recupere en largeur, plutot
 * que d'enlever le retrait de la premiere pastille. Une pastille sans retrait a
 * gauche porte au survol un fond colle a son texte d'un cote et pas de l'autre,
 * et la rangee est en `overflow: hidden` — le fond serait rogne. En decalant la
 * boite entiere, chaque pastille garde ses quatre pixels des deux cotes et le
 * survol reste symetrique. */
.sample-tags-row {
  position: relative;
  overflow: hidden;
  margin-left: -4px;
  width: calc(100% + 4px);
  -webkit-mask-image: linear-gradient(to right, black 80%, transparent 100%);
  mask-image: linear-gradient(to right, black 80%, transparent 100%);
}

/* Tags — no background, hover gets one */
.sample-tag {
  display: inline-flex;
  align-items: center;
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.38);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 1px 4px;
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 80ms ease, color 80ms ease;
}

.sample-tag:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--read);
}
/* ── Sample playback states ──────────────────────────────────── */

.sample-row:hover .sample-col--type {
  cursor: pointer;
}

.sample-col--type svg {
  display: block;
  flex-shrink: 0;
  transition: opacity 80ms ease;
}

.sample-row.is-playing,
.sample-row.is-focused {
  background: var(--ink);
}

.sample-row.is-midi-source {
  --midi-level: 0;
  background: rgb(var(--glow-channels) / calc(0.045 + (var(--midi-level) * 0.18)));
  transition: background 35ms linear, box-shadow 35ms linear;
}

/* The MIDI source row no longer recolours its filename.
 *
 * It used to be painted in the accent here, and only here: the row playing
 * under the keyboard, in MIDI mode, and nowhere else in the application. Which
 * would have been odd on its own, but theme.css also sets hover, focus and
 * playing to --text at the same specificity and loads afterwards — so the
 * accent showed only while the pointer was somewhere else, and vanished under
 * it. One name in the list behaving differently from every other name in the
 * list, and inconsistently at that.
 *
 * The row keeps its accent wash and its waveform; it is the name that reads as
 * a name again. */

.sample-row.is-midi-source .waveform-bars rect {
  fill: rgb(var(--glow-channels) / calc(0.74 + (var(--midi-level) * 0.26)));
}

.sample-row.has-midi-progress .waveform-bars-played {
  opacity: 0;
}

.waveform-bars-midi {
  pointer-events: none;
  opacity: 1;
}

.waveform-bars-midi rect {
  fill: currentColor;
}

/* L'anneau qui tourne a la place de l'icone du type, pendant que le fichier est
   lu et decode. Le meme anneau que partout ailleurs dans l'application : accent,
   meme rotation, et la taille exacte des icones qu'il remplace pour que la
   colonne ne bouge pas d'un pixel en changeant d'etat. */
.sample-type-loading {
  display: block;
  position: relative;
  width: 20px;
  height: 20px;
}

.sample-type-loading::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  border: 1.5px solid rgba(var(--glow-rgb), 0.22);
  border-top-color: var(--glow);
  animation: sidebar-spin 700ms linear infinite;
}

/* ── Play icon focus ring (playing state) ──────────────────── */
.sample-col--type {
  position: relative;
}

.sample-col--type::before {
  content: "";
  position: absolute;
  inset: -4px;
  border: 2px solid var(--glow);
  border-radius: 8px;
  pointer-events: none;
  transition: opacity 250ms ease;
  opacity: 0;
}

.sample-row.is-playing .sample-col--type::before,
.sample-row.is-focused .sample-col--type::before {
  opacity: 1;
}
/* DAW locked header fields (Key/Scale when Transpose on, Tempo when Wrap on) */
.header-field.is-daw-locked {
  pointer-events: none;
}

.header-field.is-daw-locked .header-label {
  opacity: 0.35;
}

.header-field.is-daw-locked .header-select {
  position: relative;
  color: transparent;
  opacity: 0.35;
  cursor: not-allowed;
}

.header-field.is-daw-locked .header-select img {
  opacity: 0;
}

.header-field.is-daw-locked .header-select::before {
  content: "DAW";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.05em;
  color: var(--glow);
  opacity: 1;
  background: var(--ink);
  border-radius: 2px;
}

/* Sidebar section label — same style as sample-list-header columns */
.sidebar-section-label {
  position: relative;
  display: flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--sl-pad-left, 24px);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  flex-shrink: 0;
  margin-bottom: 8px;
}

.sidebar-add-btn + .sidebar-section-label--browser {
  margin-top: 20px;
}

.sidebar-section-label::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: var(--sl-pad-left, 24px);
  right: var(--sl-pad-left, 24px);
  height: 1px;
  background: rgba(255, 255, 255, 0.4);
}

/* Page visibility — hide browser-only elements on other pages */
.app-stage:not([data-page="browser"]) .app-header,
.app-stage:not([data-page="browser"]) .app-subheader,
.app-stage:not([data-page="browser"]) .app-main {
  display: none;
}

/* Keep footer pinned to bottom when content is hidden */
.app-stage:not([data-page="browser"]) .app-footer {
  margin-top: auto;
}

/* ── Sidebar page switcher ───────────────────────────────── */
.sidebar-page-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 14px 0 14px var(--sl-pad-left, 24px);
  font-family: inherit;
  font-size: calc(16px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  outline: none;
  flex-shrink: 0;
  transition: color 80ms ease;
  position: relative;
}

.sidebar-page-btn:hover {
  color: var(--text);
}

.sidebar-page-btn.is-page-switcher-disabled {
  cursor: default;
}

.sidebar-page-btn.is-page-switcher-disabled:hover {
  color: var(--read);
}

/* L'ECHANGE DU MOT, QUAND ON PASSE EN VUE ARBORESCENCE ET QU'ON EN REVIENT.
 *
 * Le mot sort vers le haut puis le suivant entre par le bas : le sens du
 * mouvement dit qu'une chose en a remplace une autre, la ou une simple
 * disparition-reapparition se lit comme un clignotement — le meme defaut qu'on
 * vient de chasser sur les icones de la barre.
 *
 * 110 ms de chaque cote. La meme valeur est nommee dans main.js
 * (SIDEBAR_PAGE_NAME_FADE_MS) parce que le script attend la fin de la sortie
 * avant d'ecrire le nouveau mot : une attente plus courte que la transition
 * echangerait le texte pendant qu'il s'efface, et on verrait les deux.
 *
 * inline-block parce qu'un transform ne s'applique pas a une boite en ligne, et
 * will-change nulle part : deux proprietes sur un mot de douze pixels ne valent
 * pas une couche de composition permanente. */
.sidebar-page-name {
  display: inline-block;
  transition: opacity 110ms ease, transform 110ms ease;
}

.sidebar-page-name.is-leaving {
  opacity: 0;
  transform: translateY(-4px);
}

/* Pose sans transition, le temps d'un calcul de style, puis retiree : c'est ce
   qui fait entrer le mot par le bas au lieu de le faire apparaitre sur place. */
.sidebar-page-name.is-entering {
  opacity: 0;
  transform: translateY(4px);
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .sidebar-page-name,
  .sidebar-page-name.is-leaving,
  .sidebar-page-name.is-entering {
    transition: none;
    opacity: 1;
    transform: none;
  }
}

.sidebar-page-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(75%);
  opacity: 0.7;
}

.sidebar-page-btn:hover .sidebar-page-icon {
  opacity: 1;
}

.sidebar-page-btn.is-page-switcher-disabled:hover .sidebar-page-icon {
  opacity: 0.7;
}

/* Popup */

/* Checkmark */

/* Disabled page option (Home — coming soon) */

/* Soon badge */

/* Browser-only sidebar sections */
.sidebar-section-label--browser {
  display: none;
}

#app[data-page="browser"] .sidebar-section-label--browser {
  display: flex;
}

.sidebar-add-btn {
  display: none;
}

#app[data-page="browser"] .sidebar-add-btn {
  display: flex;
}

/* Sidebar scrollable zones (Places / Collections / Favorites) */
.sidebar-scroll-zone {
  position: relative;
  flex-shrink: 0;
  max-height: 180px;
  overflow: hidden;
  width: 100%;
}

.sidebar-scroll-inner {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  max-height: 180px;
  width: 100%;
  scrollbar-width: none;
  -ms-overflow-style: none;
  gap: 2px;
}

.sidebar-scroll-zone--sm,
.sidebar-scroll-zone--sm .sidebar-scroll-inner {
  max-height: 90px;
}

.sidebar-scroll-zone--xs,
.sidebar-scroll-zone--xs .sidebar-scroll-inner {
  max-height: 60px;
}

/* Collections zone — 8 items visible (~32px each) */
.sidebar-scroll-zone--collections,
.sidebar-scroll-zone--collections .sidebar-scroll-inner {
  max-height: 256px;
}

.sidebar-item-btn--sticky {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--sidebar-background);
}

.sidebar-item-btn--sticky.is-selected {
  background: var(--ink);
}

.sidebar-scroll-fade {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 36px;
  background: linear-gradient(to bottom, transparent, var(--sidebar-background));
  pointer-events: none;
}

.sidebar-item-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 4px 0 4px var(--sl-pad-left, 24px);
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  outline: none;
  flex-shrink: 0;
  transition: color 80ms ease, background 80ms ease;
  position: relative;
}

.sidebar-item-btn:hover {
  color: var(--text);
}

.sidebar-item-btn.is-selected {
  background: var(--ink);
  color: var(--text);
  width: var(--sidebar-width);
  min-width: 100%;
}

.sidebar-item-btn.is-selected img {
  opacity: 1;
}

.sidebar-item-btn img {
  display: block;
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(75%);
  opacity: 0.7;
  transition: opacity 80ms ease;
}

.sidebar-item-btn:hover img {
  opacity: 0.9;
}

/* Label — single line, fade right always, padding droite */
.sidebar-folder-label {
  white-space: nowrap;
  overflow: hidden;
  flex: 1 1 auto;
  min-width: 0;
  padding-right: 16px;
  -webkit-mask-image: linear-gradient(to right, black 72%, transparent 100%);
  mask-image: linear-gradient(to right, black 72%, transparent 100%);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.sidebar-folder-label::-webkit-scrollbar { display: none; }

/* :not(.sidebar-folder-icon) as well: collections wrap their icon in a span
   now, the same way places always have, and this rule would otherwise give the
   icon the label's flex and stretch it across the button. */
.sidebar-item-btn[data-collection-id] > span:not(.sidebar-analyzing-spinner):not(.sidebar-folder-icon),
.sidebar-item-btn[data-favorite-id] > span:not(.sidebar-analyzing-spinner):not(.sidebar-folder-icon) {
  white-space: nowrap;
  overflow: hidden;
  flex: 1 1 auto;
  min-width: 0;
  padding-right: 16px;
  -webkit-mask-image: linear-gradient(to right, black 72%, transparent 100%);
  mask-image: linear-gradient(to right, black 72%, transparent 100%);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.sidebar-item-btn[data-collection-id] > span:not(.sidebar-analyzing-spinner):not(.sidebar-folder-icon)::-webkit-scrollbar,
.sidebar-item-btn[data-favorite-id] > span:not(.sidebar-analyzing-spinner):not(.sidebar-folder-icon)::-webkit-scrollbar { display: none; }

/* Sidebar add button */
.sidebar-add-btn {
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 0 8px var(--sl-pad-left, 24px);
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  outline: none;
  transition: color 80ms ease;
}

.sidebar-add-btn:hover {
  color: var(--read);
}

.sidebar-add-btn img {
  display: block;
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(75%);
  opacity: 0.7;
  transition: opacity 80ms ease;
}

.sidebar-add-btn:hover img {
  opacity: 0.9;
}
/* ══════════════════════════════════════════════════════════════
   WAVEFORM — grey bars + blue played bars (clipPath)
══════════════════════════════════════════════════════════════ */

/* ── CE QU'ON REGARDE PENDANT QUE LA BIBLIOTHEQUE ARRIVE ──────────────────
 *
 * Une fenetre noire ne dit pas si elle travaille ou si elle est cassee, et sur
 * soixante-dix-huit mille samples le premier rendu n'est pas instantane. Un
 * anneau qui tourne et un mot, au centre, disent le necessaire.
 *
 * IL Y AVAIT DES BLOCS GRIS A LA FORME DES RANGEES, et ils promettaient une
 * geometrie : quatre places, huit rangees, une pochette a gauche, une forme
 * d'onde a droite. Une promesse pareille se tient au pixel ou elle se voit, et
 * elle etait a retenir a chaque fois qu'une colonne bougeait. Un anneau ne
 * promet rien d'autre que d'attendre, et ca, ca se tient tout seul.
 *
 * Il couvre toute la zone, en-tete des colonnes comprise : des titres de
 * colonnes au-dessus du vide annoncent des donnees que personne n'a encore
 * lues. Tout arrive ensemble au moment ou il part.
 *
 * Et la barre laterale ne montre rien. Ses deux sections portent deja
 * display:none tant que la page n'est pas declaree, donc elle est noire ; une
 * deuxieme attente a cote de la premiere n'aurait rien dit de plus. */
.boot-loading {
  position: absolute;
  inset: 0;
  /* Au-dessus de l'anneau de la recherche, qui peut monter pendant le premier
     rendu : les deux se posent au meme endroit, et c'est celui qui porte le
     mot qui doit rester lisible jusqu'au contenu. */
  z-index: 11;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: var(--app-background);
  pointer-events: none;
  transition: opacity 140ms ease;
}

/* L'ANNEAU EST CELUI DE LA RECHERCHE, A L'IDENTIQUE — .search-spinner, plus
   haut, et la meme rotation. Une seule attente a reconnaitre dans la fenetre,
   qu'on ouvre l'application ou qu'on tape trois mots. */
.boot-loading-spinner {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid rgba(var(--glow-rgb), 0.20);
  border-top-color: var(--glow);
  animation: search-spin 600ms linear infinite;
}

/* LE MOT EST ECRIT COMME ON L'ECRIT, pas en capitales espacees. Il y a eu une
   version qui empruntait la voix de l'en-tete des colonnes — dix pixels,
   capitales, lettres ecartees — et Arnaud a repondu non tout de suite. Les
   capitales de l'en-tete sont celles d'une etiquette de colonne : un mot fixe,
   toujours le meme, qu'on ne relit jamais. Celui-ci est une phrase, elle dit ce
   qui se passe maintenant, et une phrase se lit en minuscules.
   Le corps et le gris sont ceux de l'ancien chargement en tete de liste : la
   meme phrase au meme moment, elle n'avait aucune raison de changer d'habit. */
.boot-loading-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.28);
}

/* CELUI-CI PEUT S'EFFACER. Les blocs d'avant ne pouvaient pas : ils etaient
   dans le flux, au-dessus de ce qu'ils annoncaient, et garder leur place le
   temps d'un fondu poussait la vraie liste de trois cents pixels vers le bas.
   L'anneau est pose par-dessus, hors du flux : rien ne bouge pendant qu'il
   s'en va, et ce qu'il cachait est deja en place dessous. */
.boot-loading.is-gone {
  opacity: 0;
}

.sample-waveform {
  position: relative;
  overflow: hidden;
}

.sample-waveform.is-loading {
  border-radius: 3px;
  background: var(--pane);
  cursor: default;
}

.sample-waveform.is-loading::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.08) 40%,
    rgba(255, 255, 255, 0.16) 50%,
    rgba(255, 255, 255, 0.08) 60%,
    transparent 100%
  );
  background-size: 200% 100%;
  animation: sidebar-shimmer 1.4s ease-in-out infinite;
}

.sample-waveform.is-pending {
  border-radius: 3px;
  background: rgba(64, 64, 64, 0.28);
  cursor: default;
}

.sample-waveform.is-unavailable {
  border-radius: 3px;
  background: rgba(64, 64, 64, 0.45);
  cursor: default;
}

/* Grey bars (unplayed) */
.waveform-bars rect {
  fill: var(--faint);
}

/* Blue bars (played) — clipped to progress via SVG clipPath */
.waveform-bars-played rect {
  fill: var(--glow);
}

/* Rows partiellement/entièrement écoutées : gris légèrement plus clair */
.sample-row.is-played .waveform-bars rect {
  fill: var(--faint);
}

/* Smooth clip rect transition for ultra-fluid feel */
.waveform-clip-rect {
  /* width is set via JS each rAF — no CSS transition needed, rAF is already smooth */
}

/* ══════════════════════════════════════════════════════════════
   ANALYSIS PROGRESS BAR
══════════════════════════════════════════════════════════════ */

.analysis-progress-wrap {
  display: none;
  flex-direction: column;
  gap: 6px;
  padding: 12px 16px 16px;
  width: 100%;
}

.analysis-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.6);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.analysis-error-detail {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  line-height: 1.45;
  color: var(--danger);
  overflow-wrap: anywhere;
}

.analysis-bar-track {
  width: 100%;
  height: 4px;
  background: rgba(255,255,255,0.1);
  border-radius: 2px;
  overflow: hidden;
}

.analysis-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--glow);
  border-radius: 2px;
  transition: width 200ms ease;
}

/* Indeterminate (loading) mode — animated sweep */
@keyframes analysis-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(400%); }
}

.analysis-bar-track.is-indeterminate {
  position: relative;
  overflow: hidden;
}

.analysis-bar-track.is-indeterminate .analysis-bar-fill {
  width: 25% !important;
  transition: none;
  animation: analysis-sweep 1.4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  transform-origin: left center;
}

.analysis-pct {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.38);
  text-align: right;
}

/* Row that holds pct + ETA side by side */
.analysis-bar-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.analysis-eta {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(var(--glow-rgb), 0.55);
  text-align: right;
  white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════
   AI CONFIRM MODAL — analysis options checkboxes
   ══════════════════════════════════════════════════════════════ */

.ai-confirm-options {
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ai-confirm-options-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.25);
}

.ai-confirm-options-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 12px;
}

.ai-confirm-option-item {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
}

.ai-confirm-option-item--wide {
  grid-column: 1 / -1;
}

.ai-confirm-option-item input[type="checkbox"] {
  display: none;
}

.ai-option-checkmark {
  width: 14px;
  height: 14px;
  border-radius: 2px;
  border: 1.5px solid rgba(255, 255, 255, 0.18);
  background: transparent;
  flex-shrink: 0;
  position: relative;
  transition: border-color 120ms ease, background 120ms ease;
}

.ai-confirm-option-item input[type="checkbox"]:checked + .ai-option-checkmark {
  background: var(--glow);
  border-color: var(--glow);
}

.ai-confirm-option-item input[type="checkbox"]:checked + .ai-option-checkmark::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 1px;
  width: 5px;
  height: 8px;
  border: 1.5px solid var(--hairline);
  border-top: none;
  border-left: none;
  transform: rotate(45deg);
}

.ai-option-name {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.6);
  transition: color 120ms ease;
}

.ai-confirm-option-item:hover .ai-option-name {
  color: rgba(255, 255, 255, 0.82);
}

.ai-confirm-option-item:hover .ai-option-checkmark {
  border-color: rgba(var(--glow-rgb), 0.5);
}

.ai-option-warning {
  margin-top: 1px;
  padding: 7px 9px;
  border-radius: 2px;
  background: rgba(var(--glow-rgb), 0.08);
  color: rgba(255, 232, 145, 0.78);
  font-size: calc(11px * var(--ui-font-scale, 1));
  line-height: 1.45;
}

.ai-option-warning[hidden] {
  display: none;
}

/* ══════════════════════════════════════════════════════════════
   SIDEBAR FOLDER — ANALYZING STATE
   Spinner on the right + shimmer sweep on icon & text only
══════════════════════════════════════════════════════════════ */

.sidebar-item-btn.is-analyzing {
  position: relative;
}

.sidebar-folder-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

@keyframes sidebar-shimmer {
  0%   { background-position: 200% center; }
  100% { background-position: -200% center; }
}

/* Shimmer directement sur la couleur du texte — pas de boîte overlay */
.sidebar-item-btn.is-analyzing .sidebar-folder-label {
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.4) 20%,
    rgba(255, 255, 255, 1) 50%,
    rgba(255, 255, 255, 0.4) 80%
  );
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: sidebar-shimmer 2s linear infinite;
}

/* Hidden until the place is actually analysing. */
.sidebar-analyzing-spinner {
  display: none;
}

/* z-index 2, which is what was already winning: this rule and its keyframes were
   written out twice, identically apart from that number, and the later copy took
   effect. One copy now.
   
   In the accent colour, like every other spinner in the application. The closing
   one, the search one, the rescan one, the sample list one and the AI dialogue
   all read var(--glow); this was the only grey thing left turning. */
.sidebar-item-btn.is-analyzing .sidebar-analyzing-spinner {
  display: block;
  position: relative;
  z-index: 2;
  flex-shrink: 0;
  margin-left: auto;
  margin-right: 10px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1.5px solid rgba(var(--glow-rgb), 0.22);
  border-top-color: var(--glow);
  animation: sidebar-spin 700ms linear infinite;
}

/* Selected sits on a darker background, so the ring takes a little more of the
   same colour rather than switching to a different one. */
.sidebar-item-btn.is-analyzing.is-selected .sidebar-analyzing-spinner {
  border-color: rgba(var(--glow-rgb), 0.34);
  border-top-color: var(--glow);
  border-width: 2px;
}

/* The sweep across the label follows too, on a selected row.
   
   background-image, not the background shorthand: the shorthand would reset the
   200% background-size that the sweep animates and leave the label a flat block. */
.sidebar-item-btn.is-analyzing.is-selected .sidebar-folder-label {
  background-image: linear-gradient(
    90deg,
    rgba(var(--glow-rgb), 0.45) 20%,
    rgba(var(--glow-rgb), 1) 50%,
    rgba(var(--glow-rgb), 0.45) 80%
  );
}

@keyframes sidebar-spin {
  to { transform: rotate(360deg); }
}

/* ── Import queue badge ─────────────────────────────────────── */
.sidebar-import-queue-badge {
  display: none;
  flex-shrink: 0;
  margin-left: -3px;
  margin-right: 9px;
  width: 13px;
  height: 13px;
  opacity: 0.72;
  pointer-events: none;
  position: relative;
}

.sidebar-import-queue-badge.is-visible {
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  cursor: default;
}

.sidebar-import-queue-badge.is-visible::before {
  content: "";
  display: block;
  width: 13px;
  height: 13px;
  background-color: rgba(255, 255, 255, 0.62);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.5 4h9M3.5 8h9M3.5 12h7' stroke='currentColor' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.5 4h9M3.5 8h9M3.5 12h7' stroke='currentColor' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
  -webkit-mask-size: 100%;
  mask-size: 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.sidebar-item-btn.is-import-queued .sidebar-folder-icon,
.sidebar-item-btn.is-import-queued .sidebar-folder-label {
  opacity: 0.62;
}

.sidebar-item-btn.is-import-queued:hover .sidebar-folder-icon,
.sidebar-item-btn.is-import-queued:hover .sidebar-folder-label,
.sidebar-item-btn.is-import-queued.is-selected .sidebar-folder-icon,
.sidebar-item-btn.is-import-queued.is-selected .sidebar-folder-label {
  opacity: 1;
}

/* ── Sidebar new-files badge (blue dot) ─────────────────────── */
.sidebar-new-badge {
  display: none;
  flex-shrink: 0;
  /* When spinner is hidden, push badge to far right */
  margin-left: auto;
  margin-right: 10px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--glow);
  box-shadow: 0 0 5px rgba(74, 158, 255, 0.6);
  position: relative;
  cursor: default;
}

.sidebar-new-badge.is-visible {
  display: block;
}

/* When spinner is also active, it already grabbed margin-left:auto — badge sits just after it */
.sidebar-item-btn.is-analyzing .sidebar-new-badge.is-visible {
  display: block;
  margin-left: 4px;
}

/* Tooltip is rendered via JS as a body-level element to escape overflow:hidden */

/* Floating tooltip for new-badge (appended to body to escape overflow:hidden) */
.sidebar-new-badge-tooltip {
  position: fixed;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  white-space: nowrap;
  padding: 5px 9px;
  border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  pointer-events: none;
  z-index: 99999;
  opacity: 0;
  transition: opacity 120ms ease;
}

/* ── Offline badge (disconnected external drive) ─────────────── */

.sidebar-offline-badge {
  display: none;
  flex-shrink: 0;
  margin-left: auto;
  margin-right: 9px;
  width: 13px;
  height: 13px;
  opacity: 0.55;
  pointer-events: none;
}

.sidebar-offline-badge.is-visible {
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  cursor: default;
}

/* Drive icon SVG rendered via background-image / inline mask */
.sidebar-offline-badge.is-visible::before {
  content: "";
  display: block;
  width: 13px;
  height: 13px;
  /* Hard-drive icon: cylinder shape via clip + box */
  background-color: rgba(255, 255, 255, 0.55);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cellipse cx='8' cy='4.5' rx='6' ry='2.5' stroke='currentColor' stroke-width='1.4'/%3E%3Cpath d='M2 4.5v7C2 12.88 4.69 14 8 14s6-1.12 6-2.5v-7' stroke='currentColor' stroke-width='1.4'/%3E%3Ccircle cx='11' cy='11' r='1' fill='currentColor'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cellipse cx='8' cy='4.5' rx='6' ry='2.5' stroke='currentColor' stroke-width='1.4'/%3E%3Cpath d='M2 4.5v7C2 12.88 4.69 14 8 14s6-1.12 6-2.5v-7' stroke='currentColor' stroke-width='1.4'/%3E%3Ccircle cx='11' cy='11' r='1' fill='currentColor'/%3E%3C/svg%3E");
  -webkit-mask-size: 100%;
  mask-size: 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* Connected external drive: same icon, but normal sidebar text color */
.sidebar-item-btn.is-external-drive:not(.is-offline) .sidebar-offline-badge.is-visible {
  opacity: 0.78;
}

.sidebar-item-btn.is-external-drive:not(.is-offline) .sidebar-offline-badge.is-visible::before {
  background-color: var(--read);
}

.sidebar-item-btn.is-external-drive:not(.is-offline):hover .sidebar-offline-badge.is-visible::before,
.sidebar-item-btn.is-external-drive:not(.is-offline).is-selected .sidebar-offline-badge.is-visible::before {
  background-color: var(--text);
}

.sidebar-item-btn.is-missing-folder .sidebar-offline-badge.is-visible::before {
  background: url("../icons/folder.png") center / contain no-repeat;
  -webkit-mask-image: none;
  mask-image: none;
}

/* When the permanent drive icon is present, the blue "new files" dot no longer owns the far-right space. */
.sidebar-item-btn.is-external-drive .sidebar-new-badge.is-visible {
  margin-left: 4px;
}

/* Folder button when its drive is offline */
.sidebar-item-btn.is-offline {
  opacity: 0.38;
  cursor: not-allowed;
  pointer-events: auto; /* keep for hover tooltip to work */
}

/* Don't let hover/selected states override offline look */
.sidebar-item-btn.is-offline:hover,
.sidebar-item-btn.is-offline.is-selected {
  background: transparent;
  box-shadow: none;
}

/* Ensure offline badge appears at far right even with spinner present */
.sidebar-item-btn.is-offline .sidebar-offline-badge.is-visible {
  margin-left: auto;
}

/* If analyzing spinner is showing at same time (rare), offset */
.sidebar-item-btn.is-analyzing .sidebar-offline-badge.is-visible {
  margin-left: 4px;
}


/* ══════════════════════════════════════════════════════════════
   SIDEBAR RENAME INPUT (inline collection / favorite creation)
══════════════════════════════════════════════════════════════ */

.sidebar-item-btn--renaming {
  cursor: default;
}

.sidebar-rename-input {
  background: transparent;
  border: none;
  outline: none;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.85);
  width: 100%;
  padding: 0;
  caret-color: var(--glow);
}

.sidebar-rename-input::placeholder {
  color: rgba(255,255,255,0.3);
}
/* ════════════════════════════════════════════════════════════════
   PAGINATION BAR
   ════════════════════════════════════════════════════════════════ */

.pagination-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 12px 0 16px;
  background: var(--app-background);
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  flex-shrink: 0;
  user-select: none;
}

/* Boutons prev / next */
.pagination-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 7px;
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  transition: background 120ms ease;
}

.pagination-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.06);
}

.pagination-btn:disabled {
  opacity: 0.2;
  cursor: not-allowed;
}

/* Icône pagination.png */
.pagination-icon {
  width: 14px;
  height: 14px;
  object-fit: contain;
  opacity: 0.5;
  transition: opacity 120ms ease;
  display: block;
}

/* prev = icône normale (pointe à gauche), next = miroir (pointe à droite) */
.pagination-icon--next {
  transform: scaleX(-1);
}

.pagination-btn:hover:not(:disabled) .pagination-icon {
  opacity: 0.9;
}

/* Conteneur des numéros de pages */
.pagination-pages {
  display: flex;
  align-items: center;
  gap: 2px;
  margin: 0 4px;
}

/* Numéros de pages individuels */
.pagination-page-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 30px;
  border-radius: 7px;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.45);
  font-family: var(--font);
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease;
  padding: 0 4px;
}

.pagination-page-btn:hover {
  color: rgba(255, 255, 255, 0.9);
  background: rgba(255, 255, 255, 0.06);
}

.pagination-page-btn.is-active {
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.95);
  font-weight: var(--ui-weight-medium, 500);
  cursor: default;
}
/* ════════════════════════════════════════════════════════════════
   Backdrop partagé — un seul fond noir pour tous les modals
   ════════════════════════════════════════════════════════════════ */
.shared-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 199;
  background: rgba(0, 0, 0, 0.32);
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.shared-modal-backdrop.is-open {
  opacity: 1;
}

/* Coquille positionnée pour chaque modal (sans fond propre) */
.modal-shell {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.modal-shell.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* ════════════════════════════════════════════════════════════════
   Import Folder Modal
   ════════════════════════════════════════════════════════════════ */

.import-modal {
  background: var(--raised);
  border-radius: 2px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.55);
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 0;
  overflow: hidden;
  transform: translateY(-6px) scale(0.98);
  transition: transform 140ms ease;
}

.import-modal-backdrop.is-open .import-modal {
  transform: translateY(0) scale(1);
}

.import-modal-title {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.35);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 14px 16px 10px;
}

/* Options */
.import-modal-options {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 0 8px 8px;
}

.import-modal-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 10px;
  border-radius: 2px;
  cursor: pointer;
  transition: background 80ms ease;
  user-select: none;
}

.import-modal-option:hover {
  background: rgba(255, 255, 255, 0.05);
}

.import-modal-option.is-selected {
  background: rgba(var(--glow-rgb), 0.08);
}

/* Radio dot */
.import-modal-option-radio {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  margin-top: 1px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.25);
  background: transparent;
  position: relative;
  transition: border-color 100ms ease;
}

.import-modal-option.is-selected .import-modal-option-radio {
  border-color: var(--glow);
}

.import-modal-option.is-selected .import-modal-option-radio::after {
  content: "";
  position: absolute;
  inset: 2.5px;
  border-radius: 50%;
  background: var(--glow);
}

/* Text */
.import-modal-option-content {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.import-modal-option-label {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.75);
  transition: color 80ms ease;
}

.import-modal-option.is-selected .import-modal-option-label {
  color: rgba(255, 255, 255, 0.92);
}

.import-modal-option-desc {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.32);
  line-height: 1.4;
}

/* Actions */
.import-modal-actions {
  display: flex;
  gap: 6px;
  padding: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.import-modal-btn {
  flex: 1;
  padding: 7px 12px;
  border: none;
  border-radius: 2px;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  cursor: pointer;
  outline: none;
  transition: background 100ms ease, color 100ms ease;
}

.import-modal-btn--cancel {
  background: rgba(255, 255, 255, 0.07);
  color: rgba(255, 255, 255, 0.55);
}

.import-modal-btn--cancel:hover {
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.75);
}

#settings-close-btn-footer {
  background: var(--glow);
  color: var(--on-glow);
  font-weight: var(--ui-weight-medium, 500);
}

#settings-close-btn-footer:hover {
  background: var(--glow);
  color: var(--on-glow);
}

.import-modal-btn--add {
  background: var(--glow);
  color: var(--on-glow);
  font-weight: var(--ui-weight-medium, 500);
}

.import-modal-btn--add:hover {
  background: var(--glow);
}

.import-modal-btn--add:disabled {
  background: rgba(var(--glow-rgb), 0.25);
  color: rgba(26, 26, 26, 0.45);
  cursor: default;
}

/* ════════════════════════════════════════════════════════════════
   AI CONFIRM MODAL
   ════════════════════════════════════════════════════════════════ */

.ai-confirm-modal {
  background: var(--raised);
  border-radius: 2px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.65);
  width: 340px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(-8px) scale(0.97);
  transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.modal-shell.is-open .ai-confirm-modal {
  transform: translateY(0) scale(1);
}

.ai-confirm-title {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.32);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 14px 16px 10px;
}

.ai-confirm-body {
  padding: 4px 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 80px;
}

/* ── Spinner row ─────────────────────────────────────────────── */
.ai-confirm-spinner-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
}

.ai-confirm-spinner {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid rgba(var(--glow-rgb), 0.18);
  border-top-color: var(--glow);
  animation: ai-spin 700ms linear infinite;
  flex-shrink: 0;
}

@keyframes ai-spin {
  to { transform: rotate(360deg); }
}

.ai-confirm-estimating-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.52);
}

/* ── Estimation result ───────────────────────────────────────── */
.ai-confirm-estimate-result {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ai-confirm-stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.ai-confirm-stat-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.38);
}

.ai-confirm-stat-value {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.78);
  text-align: right;
}

.ai-confirm-note {
  margin: 4px 0 0;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.28);
  line-height: 1.5;
}

.ai-confirm-actions {
  display: flex;
  gap: 6px;
  padding: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* ════════════════════════════════════════════════════════════════
   AI ANALYSIS PROGRESS — sidebar bottom bar
   ════════════════════════════════════════════════════════════════ */

/* The existing .analysis-progress-wrap is reused — these styles
   add the step label + cancel button below the bar */

.analysis-progress-stage {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.35);
  margin-top: 3px;
  white-space: normal;
  overflow: hidden;
  max-width: 100%;
  line-height: 1.4;
}

.analysis-progress-cancel {
  margin-top: 5px;
  padding: 3px 0;
  background: none;
  border: none;
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.25);
  cursor: pointer;
  text-align: left;
  transition: color 100ms ease;
}

.analysis-progress-cancel:hover {
  color: rgba(255, 255, 255, 0.55);
}

/* Pause sits on the same line as Cancel and reads the same, because they are
   two words under a progress bar and not a toolbar. */
.analysis-progress-pause {
  margin-top: 5px;
  padding: 3px 0;
  background: none;
  border: none;
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.25);
  cursor: pointer;
  text-align: left;
  transition: color 100ms ease;
}

.analysis-progress-pause:hover {
  color: rgba(255, 255, 255, 0.55);
}

/* Held: the word says Resume, and the accent says the run is not moving. The
   bar above it stops travelling too — see .analysis-progress-wrap.is-held. */
.analysis-progress-pause.is-held {
  color: var(--glow);
}

.analysis-progress-pause:disabled {
  cursor: default;
  opacity: 0.5;
}

/* Armed: Cancel has become Confirm and means it. The one red thing down here,
   because it is the only irreversible thing down here. */
.analysis-progress-cancel.is-armed,
.analysis-progress-cancel.is-armed:hover {
  color: var(--danger, #ff6b5e);
}

/* A held run must not look like a running one.
   The indeterminate stripe is the bar's way of saying "working, no idea how
   long", and left travelling under a Resume button it says the opposite of
   what the button does. */
.analysis-progress-wrap.is-held .analysis-bar-track.is-indeterminate .analysis-bar-fill {
  /* The animation is on the fill, not on the track. Pausing the track paused
     nothing at all — it has no animation of its own. */
  animation-play-state: paused;
}

.analysis-progress-wrap.is-held .analysis-bar-fill {
  opacity: 0.55;
}
/* ════════════════════════════════════════════════════════════════
   TITLEBAR
════════════════════════════════════════════════════════════════ */

.app-titlebar-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: default;
  pointer-events: none;
  -webkit-app-region: drag;
}

.app-titlebar-text {
  font-family: var(--font);
  font-size: calc(16px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.7);
  letter-spacing: 0.02em;
  line-height: 1;
}

.app-titlebar-icon {
  width: 20px;
  height: 20px;
  object-fit: contain;
  display: block;
}

.settings-open-proxy[hidden] {
  display: none !important;
}

/* ════════════════════════════════════════════════════════════════
   SETTINGS MODAL — DA identique aux autres modals de l'app
════════════════════════════════════════════════════════════════ */

.settings-modal {
  display: flex;
  width: 620px;
  height: 400px;
  background: var(--raised);
  border: 1px solid rgba(255,255,255,0.075);
  border-radius: 4px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.65);
  overflow: hidden;
  transform: translateY(-8px) scale(0.97);
  transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.modal-shell.is-open .settings-modal {
  transform: translateY(0) scale(1);
}

/* ── Sidebar ── */
.settings-sidebar {
  flex: 0 0 160px;
  width: 160px;
  /* PAS DE FOND A ELLE. C'est la meme fenetre des deux cotes.
     Elle etait assombrie de dix-huit pour cent, ce qui faisait deux surfaces la
     ou il n'y a qu'un panneau, et le regard lisait deux fenetres accolees. Le
     fond des mots choisis etant parti, cette teinte restait la seule chose qui
     decoupait encore la fenetre en blocs. Le filet degrade suffit a dire ou
     l'une finit et l'autre commence, et c'est tout ce qu'il y a a dire. */
  display: flex;
  flex-direction: column;

  /* Trente-deux en haut, comme les pages : voir .settings-section. Les deux colonnes
     commencent sur la meme ligne, sous la bande que la croix occupe. */
  padding: 32px 0 12px;
  flex-shrink: 0;

  /* Pour le filet ci-dessous, qui est dessine et non borde. */
  position: relative;
}

/* LE FILET DE LA COLONNE SORT DU NOIR ET Y RETOURNE.
 *
 * C'etait un border-right : un trait qui commence net en haut de la fenetre et
 * s'arrete net en bas, ce que rien d'autre dans Sonus ne fait. La barre
 * laterale de l'application trace le sien en degrade — voir .app-sidebar::after
 * dans theme.css, d'ou viennent les 15 et 85 pour cent — parce qu'un bord ne
 * peut pas s'eteindre et qu'un trait qui s'eteint ne coupe pas la fenetre en
 * deux. Les memes chiffres ici : c'est le meme geste, dans la meme application. */
.settings-sidebar::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 1px;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent 0,
    var(--divider) 15%,
    var(--divider) 85%,
    transparent 100%);
}

.settings-nav {
  display: flex;
  flex-direction: column;
  padding: 0 6px;
  gap: 1px;
}

.settings-nav-item {
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.45);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 5px 8px;
  text-align: left;
  cursor: pointer;
  transition: background 80ms ease, color 80ms ease;
  outline: none;
}

.settings-nav-item:hover {
  background: rgba(255, 255, 255, 0.06);
  color: rgba(255, 255, 255, 0.75);
}

.settings-nav-item.is-active {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.88);
}

/* ── Content ── */
.settings-content {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  position: relative;
}

.settings-close-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 10;
  width: 22px;
  height: 22px;
  background: transparent;
  border: none;

  /* SANS LE REMPLISSAGE QUE LE SYSTEME DONNE A UN BOUTON.
     Il vaut « 1px 6px » tant qu'on ne le dit pas, donc la boite utile d'un
     bouton de vingt-deux pixels n'en faisait que dix de large — et l'icone,
     etant un element flexible, y etait ECRASEE : dix de large sur douze de
     haut, une croix aplatie. C'est ce qui se lisait comme une icone sale. */
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;

  /* PAS DE CARRE AU SURVOL, JUSTE LA CROIX QUI BLANCHIT.
     C'est ce que fait le reste de cette fenetre — une tonalite du verrou de
     hauteur blanchit sous le pointeur sans rien encadrer — et un rectangle
     apparaissant sous une icone de vingt-deux pixels attire l'oeil sur le
     rectangle plutot que sur la croix. */
  color: var(--dim);
  cursor: pointer;
  transition: color 80ms ease;
}

/* Vingt-huit pour cent de blanc a dix pixels : les diagonales se rendaient en
   quelques taches grises, ce qui se lit comme une icone abimee plutot que
   comme une icone discrete. Le gris de cette fenetre, puis le blanc plein. */
.settings-close-btn svg { width: 12px; height: 12px; flex: 0 0 auto; display: block; }

.settings-close-btn:hover { color: var(--text); }

/* Sans carre au survol, le clavier n'avait plus rien pour se reperer : il y
   avait « outline: none » et le fond disait ou on etait. Un anneau le dit
   maintenant, et seulement au clavier. */
.settings-close-btn:focus-visible {
  color: var(--text);
  outline: 1px solid rgba(var(--glow-rgb), 0.7);
  outline-offset: 2px;
}

/* ── Sections ── */
.settings-section {
  display: none;
  flex-direction: column;
  padding: 0;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* La barre est celle du theme. Elle etait redite ici, pastille peinte en
     var(--sidebar-background) — la couleur du panneau lui-meme — et une regle
     de classe bat l'universelle : cette barre-la etait invisible, exactement
     comme celle des menus deroulants. */
}

.settings-section.is-active {
  display: flex;
}

/* PLUS DE TITRE QUI REPETE LA COLONNE.
 *
 * Chaque page en portait un — GENERAL, AUDIO, MIDI — juste a droite du mot
 * qu'on venait de cliquer pour arriver la, et dans la meme casse et la meme
 * graisse que les titres de groupe qui le suivaient. Sur General, cela donnait
 * GENERAL puis PLAYBACK l'un sous l'autre : deux etiquettes de meme rang dont
 * la premiere ne disait rien. On sait ou on est, la colonne le montre en
 * accent. La page commence donc par son premier groupe.
 *
 * TRENTE-DEUX, ET CE CHIFFRE EST MESURE. La croix est posee en absolu a dix pixels
 * du coin et fait vingt-deux de haut : elle occupe donc la bande 10-32 de
 * chaque page, quelle que soit la page. Le titre enleve, la premiere ligne
 * remontait dedans — sur Audio, Machines, About et AI Tools, qui n'ont pas
 * d'intertitre pour la retenir, le premier bouton chevauchait la croix de sept
 * pixels. Trente-deux laisse la bande a la croix seule et la page
 * commence dessous. La colonne de gauche prend le meme chiffre, donc les deux
 * partent sur la meme ligne et la bande se lit comme une barre de titre vide,
 * ce qu'elle est. */
.settings-section {
  padding-top: 32px;
}

/* ── Groups ── */
.settings-group {
  padding: 0 16px 16px;
  flex-shrink: 0;
}

/* LE TRAIT ENTRE DEUX GROUPES S'ETEINT A SES DEUX BOUTS.
 *
 * C'etait un border-top, donc un trait qui bute sur les deux bords de la
 * fenetre. Les separations de l'application sont des degrades — le pied de la
 * fenetre principale trace le sien ainsi, transparent, plein a 4 %, plein
 * jusqu'a 96 %, transparent — et c'est ce que Sonus montre partout ailleurs.
 * Un pseudo-element, parce qu'un bord ne sait pas s'eteindre. */
.settings-group + .settings-group {
  position: relative;
  padding-top: 14px;
}

.settings-group + .settings-group::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg,
    transparent 0,
    var(--divider) 4%,
    var(--divider) 96%,
    transparent 100%);
}

/* UN TITRE DE GROUPE SEPARE DEUX GROUPES. UNE PAGE QUI N'EN A QU'UN N'EN PORTE
   DONC PAS.
   Avec le titre de page enleve, le premier titre de groupe se retrouve tout en
   haut, la ou le titre de page se tenait. Sur une page a plusieurs groupes il
   se lit comme ce qu'il est, un intertitre. Sur une page qui n'en a qu'un, il
   se lit comme le titre de la page — c'est-a-dire exactement ce qu'on vient
   d'enlever, avec un autre mot : OUTPUT au-dessus de la seule ligne de la page
   Audio, INSTALLATION au-dessus de la seule de AI Tools. Machines et About
   n'en ont jamais eu et se lisaient tres bien.
   La seule exception est Shortcuts, dont la ligne de titre porte Reset : elle
   existe pour ce mot-la, pas pour se nommer. */
.settings-group-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.22);
  margin-bottom: 8px;
}

/* ── Rows ── */
.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 36px;
  padding: 7px 0;
  gap: 12px;
}

.settings-row[hidden] {
  display: none !important;
}

/* UN TRAIT ENTRE DEUX LIGNES VISIBLES, ET NULLE PART AILLEURS.
 *
 * Chaque ligne portait son trait dessous, et la derniere en etait dispensee par
 * :last-of-type. Sauf que « la derniere » se compte dans le document et non a
 * l'ecran : dans Tabs, « Auto-save tabs » n'existe que quand les onglets sont
 * allumes, donc tant qu'ils sont eteints la derniere ligne du document etait
 * une ligne cachee, et « Enable tabs » gardait un trait qui ne separait plus
 * rien. Un trait sous la derniere chose visible n'a rien en dessous : il ne
 * separe pas, il souligne.
 *
 * Dit dans l'autre sens, le probleme disparait au lieu d'etre rattrape : une
 * ligne visible precedee d'une autre ligne visible porte un trait AU-DESSUS
 * d'elle. La premiere n'en a jamais, la derniere non plus, et cacher ou montrer
 * une ligne recalcule tout sans qu'une seule ligne de code s'en mele. */
.settings-row:not([hidden]) ~ .settings-row:not([hidden]) {
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.settings-row--tall {
  align-items: flex-start;
  min-height: 44px;
  padding-top: 9px;
}

.settings-row-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}

.settings-row-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.72);
}

.settings-row-desc {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.28);
  line-height: 1.4;
}

.settings-row-control {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

/* MIDI settings */
.midi-layout-switch {
  display: flex;
  padding: 2px;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 2px;
  flex-shrink: 0;
}

.midi-layout-btn {
  font: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.38);
  background: transparent;
  border: none;
  border-radius: 2px;
  padding: 5px 8px;
  cursor: pointer;
  transition: color 100ms ease, background 100ms ease;
}

.midi-layout-btn:hover {
  color: rgba(255, 255, 255, 0.72);
}

.midi-layout-btn.is-active {
  background: var(--glow);
  color: var(--glow-soft);
}

.settings-value-badge.midi-polyphony-value,
.settings-value-badge.midi-latency-value {
  min-width: 70px;
  text-align: center;
  border: none;
  cursor: ns-resize;
  color: var(--glow);
  outline: none;
}

.settings-value-badge.midi-polyphony-value:hover,
.settings-value-badge.midi-polyphony-value.is-dragging,
.settings-value-badge.midi-latency-value:hover,
.settings-value-badge.midi-latency-value.is-dragging {
  background: rgba(var(--glow-rgb), 0.13);
  color: var(--glow);
}

.settings-value-badge.midi-polyphony-value:focus-visible,
.settings-value-badge.midi-latency-value:focus-visible {
  outline: 1px solid rgba(var(--glow-rgb), 0.7);
  outline-offset: 2px;
}

.settings-group--midi-envelope {
  padding-bottom: 12px;
}

/* UN TITRE DE GROUPE, ET AU BOUT DE SA LIGNE CE QUI DEFAIT LE GROUPE.
   Ne s'appelait midi-group-heading que parce que l'enveloppe MIDI l'avait
   demande la premiere. C'est la forme generale : le titre a gauche, un mot a
   droite qui agit sur tout ce qui est dessous — remettre l'enveloppe, chercher
   les appareils, rendre les touches d'origine. Ecrire une ligne de reglage
   pour ce mot-la, c'est ranger une commande parmi des choses a regler. */
.settings-group-head {
  min-height: 22px;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.settings-group-head .settings-group-label {
  margin-bottom: 0;
}

/* ET LE PREMIER TITRE D'UNE PAGE LAISSE LE COIN A LA CROIX.
   Elle est posee en absolu a dix pixels du bord, donc elle occupe exactement la
   bande ou tombe la premiere ligne. Mesure : le mot « Reset » finissait a 693
   et la croix commence a 678 — ils se chevauchaient de quinze pixels. Seule la
   ligne de titre monte assez haut pour la rencontrer ; les lignes de reglage
   commencent plus bas et gardent leur pleine largeur. */
.settings-section > .settings-group:first-child > .settings-group-head {
  padding-right: 24px;
}

.midi-reset-btn,
.midi-detect-btn {
  height: 22px;
  padding: 0 8px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.04);
  color: rgba(255, 255, 255, 0.48);
  font: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 100ms ease, background 100ms ease, color 100ms ease;
}

.midi-reset-btn:hover,
.midi-detect-btn:hover {
  background: rgba(var(--glow-rgb), 0.1);
  border-color: rgba(var(--glow-rgb), 0.3);
  color: var(--glow);
}

.midi-detect-btn:disabled {
  opacity: 0.46;
  cursor: default;
}

.midi-reset-btn:focus-visible,
.midi-detect-btn:focus-visible {
  outline: 1px solid rgba(var(--glow-rgb), 0.72);
  outline-offset: 2px;
}

.midi-envelope-editor {
  padding: 8px 9px 9px;
  background: rgba(0, 0, 0, 0.13);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 2px;
}

.midi-envelope-graph {
  display: block;
  width: 100%;
  height: 122px;
  background: var(--ink);
  border-radius: 2px;
  touch-action: none;
}

.midi-envelope-grid line {
  stroke: rgba(255, 255, 255, 0.045);
  stroke-width: 1;
}

.midi-envelope-grid line.is-major {
  stroke: rgba(255, 255, 255, 0.12);
}

.midi-envelope-grid text {
  fill: rgba(255, 255, 255, 0.26);
  font-family: var(--font-mono);
  font-size: calc(11px * var(--ui-font-scale, 1));
  text-anchor: middle;
}

.midi-envelope-area {
  fill: rgba(var(--glow-rgb), 0.12);
}

.midi-envelope-line {
  fill: none;
  stroke: var(--glow);
  stroke-width: 2;
  stroke-linejoin: round;
}

.midi-envelope-point {
  fill: var(--glow);
  stroke: var(--ink);
  stroke-width: 2;
  cursor: pointer;
  transition: r 100ms ease;
}

.midi-envelope-point.is-fixed {
  cursor: default;
  fill: rgba(var(--glow-rgb), 0.52);
}

.midi-envelope-point:hover,
.midi-envelope-point.is-dragging {
  r: 5.5px;
}

.midi-envelope-point.is-fixed:hover {
  r: 4px;
}

.midi-envelope-knobs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  align-items: end;
  gap: 4px;
  padding-top: 10px;
}

.midi-knob-field {
  display: grid;
  justify-items: center;
  gap: 4px;
}

.midi-knob {
  --ratio: 0;
  --angle: calc(-135deg + (var(--ratio) * 270deg));
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.13);
  background: var(--raised);
  cursor: ns-resize;
  outline: none;
  touch-action: none;
}

.midi-knob::before {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  background: conic-gradient(from 225deg, var(--glow) calc(var(--ratio) * 270deg), rgba(255, 255, 255, 0.09) 0deg 270deg, transparent 270deg);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
}

.midi-knob::after {
  content: "";
  position: absolute;
  left: calc(50% - 1px);
  top: 4px;
  width: 2px;
  height: 10px;
  border-radius: 1px;
  background: var(--glow);
  transform-origin: 1px 13px;
  transform: rotate(var(--angle));
}

.midi-knob:focus-visible {
  outline: 1px solid rgba(var(--glow-rgb), 0.8);
  outline-offset: 4px;
}

.midi-knob.is-dragging {
  border-color: rgba(var(--glow-rgb), 0.55);
}

.midi-knob-value {
  font-family: var(--font-mono);
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: var(--glow);
}

.midi-knob-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.34);
}

.settings-group--midi-devices {
  padding-bottom: 14px;
}

.midi-device-status {
  min-height: 28px;
  margin: 0;
  color: rgba(255, 255, 255, 0.4);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  line-height: 1.45;
}

.midi-device-status.is-ready {
  color: rgba(var(--glow-rgb), 0.78);
}

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

.midi-device-list {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 7px;
}

.midi-device-item {
  min-height: 32px;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 5px 8px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.035);
}

.midi-device-dot {
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--success);
  box-shadow: 0 0 8px rgba(57, 208, 119, 0.35);
}

.midi-device-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.midi-device-name {
  min-width: 0;
  color: rgba(255, 255, 255, 0.77);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.midi-device-meta {
  color: rgba(255, 255, 255, 0.3);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

/* ── Value badge (même style que les autres badges de l'app) ── */
.settings-value-badge {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.07);
  border-radius: 2px;
  padding: 2px 7px;
  white-space: nowrap;
  font-family: var(--font-mono);
}

/* ── Toggle ── */
.settings-toggle {
  position: relative;
  width: 32px;
  height: 18px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.12);
  border: none;
  cursor: pointer;
  padding: 0;
  transition: background 120ms ease;
  outline: none;
  flex-shrink: 0;
}

.settings-toggle[aria-pressed="true"] {
  background: var(--glow);
}

.settings-toggle-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 12px;
  height: 12px;
  border-radius: 1px;
  background: rgba(255, 255, 255, 0.7);
  transition: transform 120ms ease;
  pointer-events: none;
}

.settings-toggle[aria-pressed="true"] .settings-toggle-knob {
  transform: translateX(14px);
  background: var(--ink);
}

/* ── AI Status pill ── */
.settings-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px 2px 6px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.settings-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  flex-shrink: 0;
  transition: background 200ms ease, box-shadow 200ms ease;
}

.settings-status-pill.is-ready .settings-status-dot {
  background: var(--success);
  box-shadow: 0 0 0 2px rgba(154, 230, 180, 0.2);
}

.settings-status-pill.is-missing .settings-status-dot {
  background: rgba(192, 82, 74, 0.85);
}

.settings-status-text {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.38);
}

.settings-status-pill.is-ready .settings-status-text {
  color: rgba(154, 230, 180, 0.9);
}

.settings-status-pill.is-missing .settings-status-text {
  color: rgba(192, 82, 74, 0.8);
}

/* ── Actions row ── */
.settings-actions-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0 2px;
}

/* Bouton danger — même style que les boutons cancel/add des autres modals
   mais teinté rouge pour l'action destructive */
.settings-action-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  padding: 7px 12px;
  border-radius: 2px;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background 80ms ease, color 80ms ease;
  outline: none;
  flex-shrink: 0;
}

.settings-action-btn--danger {
  background: rgba(255, 255, 255, 0.07);
  color: rgba(220, 100, 90, 0.85);
}

.settings-action-btn--danger:hover {
  background: rgba(220, 90, 80, 0.15);
  color: rgba(240, 120, 108, 1);
}

.settings-action-btn--danger[data-confirm-pending] {
  background: rgba(220, 90, 80, 0.2);
  color: rgba(240, 130, 118, 1);
}

.settings-action-btn--primary {
  background: var(--glow);
  color: var(--glow-soft);
  border: 1.5px solid transparent;
  font-weight: var(--ui-weight-medium, 500);
}

.settings-action-btn--primary:hover {
  background: var(--glow);
  border-color: transparent;
  color: var(--glow-soft);
}

.settings-action-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  pointer-events: none;
}

.settings-action-hint {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.2);
  line-height: 1.4;
}

/* ── Inline feedback (remplace les alert() natifs) ── */
.settings-inline-feedback {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.45);
  padding: 6px 0 0;
  line-height: 1.4;
}

.settings-inline-feedback.is-success { color: rgba(154, 230, 180, 0.9); }
.settings-inline-feedback.is-error   { color: rgba(210, 85, 75, 0.9);   }
.settings-inline-feedback:empty      { display: none; }

/* ── Footer actions (même pattern que les autres modals) ── */
.settings-modal-actions {
  display: flex;
  gap: 6px;
  padding: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  flex-shrink: 0;
}

/* ════════════════════════════════════════════════════════════════
   FEEDBACK MODAL
════════════════════════════════════════════════════════════════ */

.feedback-modal {
  position: relative;
  width: 500px;
  max-width: calc(100vw - 48px);
  background: var(--raised);
  border-radius: 2px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.65);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transform: translateY(-8px) scale(0.97);
  transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.modal-shell.is-open .feedback-modal {
  transform: translateY(0) scale(1);
}

.feedback-modal-header {
  padding: 15px 46px 12px 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.feedback-modal-kicker {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.25);
  margin-bottom: 4px;
}

.feedback-modal-title {
  font-size: calc(16px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.82);
}

.feedback-close-btn {
  top: 11px;
  right: 11px;
}

.feedback-form {
  padding: 14px 16px 8px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.feedback-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.feedback-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.26);
}

.feedback-input,
.feedback-select,
.feedback-textarea {
  width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.055);
  color: rgba(255, 255, 255, 0.78);
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  outline: none;
  transition: border-color 90ms ease, background 90ms ease;
}

.feedback-input,
.feedback-select {
  height: 32px;
  padding: 0 9px;
}

.feedback-select {
  cursor: pointer;
  color-scheme: dark;
}

.feedback-select option {
  background: var(--raised);
  color: rgba(255, 255, 255, 0.86);
}

.feedback-select option:checked {
  background: var(--glow);
  color: var(--ink);
}

.feedback-textarea {
  min-height: 126px;
  max-height: 180px;
  resize: vertical;
  padding: 8px 9px;
  line-height: 1.45;
}

.feedback-input:focus,
.feedback-select:focus,
.feedback-textarea:focus {
  border-color: rgba(var(--glow-rgb), 0.32);
  background: rgba(255, 255, 255, 0.075);
}

.feedback-status {
  min-height: 17px;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.36);
}

.feedback-status.is-success {
  color: rgba(154, 230, 180, 0.9);
}

.feedback-status.is-error {
  color: rgba(210, 85, 75, 0.9);
}

.feedback-actions {
  display: flex;
  gap: 6px;
  padding-top: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* ── debug CSS legacy (kept for cleanup, no-op) ── */
.header-debug-group {
  display: none;
}
/* ── old debug btn (kept, hidden) ── */
.header-debug-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
  padding: 0 8px 0 6px;
  border-left: 1px solid rgba(255, 255, 255, 0.07);
}

.header-debug-btn {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px 3px 6px;
  background: rgba(255, 80, 60, 0.10);
  border: 1px solid rgba(255, 80, 60, 0.22);
  border-radius: 4px;
  color: rgba(255, 120, 100, 0.85);
  font-family: inherit;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
  white-space: nowrap;
}

.header-debug-btn img {
  width: 12px;
  height: 12px;
  opacity: 0.7;
  filter: sepia(1) saturate(3) hue-rotate(-20deg);
  flex-shrink: 0;
}

.header-debug-btn:hover {
  background: rgba(255, 80, 60, 0.20);
  border-color: rgba(255, 80, 60, 0.45);
  color: rgba(255, 140, 120, 1);
}

.header-debug-btn:hover img {
  opacity: 1;
}

.header-debug-btn:active {
  background: rgba(255, 80, 60, 0.28);
}

.header-debug-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}


/* ════════════════════════════════════════════════════════════════
   FOOTER — NOW PLAYING
════════════════════════════════════════════════════════════════ */

.footer-now-playing {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
  overflow: hidden;
  padding: 0 8px;
}

.footer-now-playing.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* Inner group — takes only as much space as its content needs */
.fnp-group {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
}

/* Pack avatar */
.fnp-avatar {
  width: 34px;
  height: 34px;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: 600;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.82);
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
}

.fnp-avatar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255,255,255,0.18) 0%, transparent 60%);
  border-radius: inherit;
  pointer-events: none;
}

/* Text: name + tags — shrinks but doesn't grow beyond content */
/* LE MEME ALIGNEMENT QUE DANS LA LISTE, ET LE MEME OBSTACLE UN CRAN PLUS LOIN.
 *
 * Voir la note sur .sample-tags-row : le nom demarre au bord de la colonne et
 * le texte du premier tag quatre pixels plus loin, parce qu'une pastille porte
 * `padding: 1px 4px`. Deux lignes l'une sous l'autre qui ne partent pas du meme
 * endroit, et ici elles sont a treize et onze pixels, donc ca se voit.
 *
 * La rangee se decale de ces quatre pixels, comme dans la liste. Mais ce
 * bloc-ci est en `overflow: hidden` — un enfant tire vers la gauche s'y fait
 * rogner, et c'est precisement le fond de survol de la premiere pastille qui y
 * passerait. Le retrait et la marge negative ci-dessous ne bougent rien : ils
 * se compensent exactement pour le CONTENU, et ne servent qu'a reculer de
 * quatre pixels le bord ou le rognage a lieu. */
.fnp-text {
  min-width: 0;
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  gap: 3px;
  overflow: hidden;
  margin-left: -4px;
  padding-left: 4px;
}

.fnp-name {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--read);
  white-space: nowrap;
  overflow: hidden;
  max-width: 300px;
  -webkit-mask-image: linear-gradient(to right, black 80%, transparent 100%);
  mask-image: linear-gradient(to right, black 80%, transparent 100%);
}

/* Et les quatre pixels sont repris en largeur, pour que le bord droit de la
   rangee reste sous celui du nom : c'est la que le fondu est pose. */
.fnp-tags-row {
  position: relative;
  display: flex;
  align-items: center;
  height: 15px;
  overflow: hidden;
  margin-left: -4px;
  max-width: 304px;
}

.fnp-tags-scroll {
  display: flex;
  align-items: center;
  gap: 1px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  flex-wrap: nowrap;
  padding-right: 16px;
}

.fnp-tags-scroll::-webkit-scrollbar { display: none; }

.fnp-tags-scroll .sample-tag {
  font-size: calc(11px * var(--ui-font-scale, 1));
  padding: 1px 4px;
  white-space: nowrap;
}

.fnp-tags-fade {
  position: absolute;
  right: 0;
  top: 0;
  width: 20px;
  height: 100%;
  background: linear-gradient(to right, transparent, var(--sidebar-background));
  pointer-events: none;
}

/* ════════════════════════════════════════════════════════════════
   DAW BRIDGE MODAL
   ════════════════════════════════════════════════════════════════ */

.daw-bridge-modal {
  background: var(--raised);
  border-radius: 2px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.65);
  width: 340px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(-8px) scale(0.97);
  transition: transform 160ms cubic-bezier(0.22, 1, 0.36, 1);
}

.modal-shell.is-open .daw-bridge-modal {
  transform: translateY(0) scale(1);
}

.daw-bridge-modal-title {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.32);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 14px 16px 10px;
}

.daw-bridge-modal-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 4px 16px 14px;
}

/* ── Status indicator row ── */
.daw-bridge-status-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.18);
  border-radius: 2px;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.daw-bridge-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  background: rgba(192, 82, 74, 0.85);
  box-shadow: 0 0 0 2px rgba(192, 82, 74, 0.2);
  transition: background 200ms ease, box-shadow 200ms ease;
}

.daw-bridge-status-dot.is-connected {
  background: var(--success);
  box-shadow: 0 0 0 2px rgba(154, 230, 180, 0.25);
  animation: daw-bridge-pulse 2s ease-in-out infinite;
}

@keyframes daw-bridge-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(154, 230, 180, 0.25); }
  50%       { box-shadow: 0 0 0 5px rgba(154, 230, 180, 0.08); }
}

.daw-bridge-status-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.1em;
  color: rgba(192, 82, 74, 0.85);
  transition: color 200ms ease;
  user-select: none;
}

.daw-bridge-status-label.is-connected {
  color: rgba(154, 230, 180, 0.95);
}

/* ── Description text ── */
.daw-bridge-desc {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.45);
  line-height: 1.55;
  margin: 0;
}

.daw-bridge-desc strong {
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255, 255, 255, 0.68);
}

/* ── Connected info panel ── */
.daw-bridge-info {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  background: rgba(154, 230, 180, 0.05);
  border: 1px solid rgba(154, 230, 180, 0.15);
  border-radius: 2px;
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 220ms ease, max-height 220ms ease, padding 220ms ease;
  padding-top: 0;
  padding-bottom: 0;
}

.daw-bridge-info[aria-hidden="false"] {
  opacity: 1;
  max-height: 80px;
  padding: 10px 12px;
}

.daw-bridge-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.daw-bridge-info-label {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.35);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.daw-bridge-info-value {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(154, 230, 180, 0.9);
  font-variant-numeric: tabular-nums;
}

/* ── Port hint ── */
.daw-bridge-hint {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.22);
  margin: 0;
  line-height: 1.4;
}

.daw-bridge-hint code {
  font-family: var(--font-mono);
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.06);
  padding: 1px 4px;
  border-radius: 2px;
}

/* ── Actions footer ── */
.daw-bridge-modal-actions {
  display: flex;
  gap: 6px;
  padding: 8px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}
/* ════════════════════════════════════════════════════════════════
   SEARCH SPINNER OVERLAY
   Shows in app-main center while search is debouncing
════════════════════════════════════════════════════════════════ */

.search-spinner-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transition: none;
  z-index: 10;
  background: var(--app-background);
  visibility: hidden;
}

.search-spinner-overlay.is-visible {
  opacity: 1;
  visibility: visible;
}

.search-spinner {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid rgba(var(--glow-rgb), 0.20);
  border-top-color: var(--glow);
  animation: search-spin 600ms linear infinite;
}

@keyframes search-spin {
  to { transform: rotate(360deg); }
}

/* Empty state message when no results */
.sample-list-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
  gap: 8px;
  opacity: 0;
  animation: fade-in-empty 200ms ease forwards;
}

@keyframes fade-in-empty {
  to { opacity: 1; }
}

.sample-list-empty-title {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.38);
}

.sample-list-empty-sub {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.2);
}

/* ── Tags dropdown empty state ───────────────────────────────── */
/* LA MEME PHRASE QUE LES DEUX AUTRES FILTRES.
   Instruments et Genres disent depuis toujours ce qu'ils attendent quand ils
   n'ont rien ; Scale, lui, proposait sept modes dont cinq ne rendaient jamais
   rien, ce qui est la version menteuse de la meme situation. Meme voix, meme
   italique, meme gris : c'est la meme phrase, elle ne va pas s'habiller
   autrement parce que le menu est plus haut dans la fenetre. */
.header-dropdown-empty,
.subheader-tags-empty {
  display: block;
  padding: 10px 12px 8px;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.22);
  font-style: italic;
  line-height: 1.45;
  white-space: normal;
  max-width: 180px;
}
/* ════════════════════════════════════════════════════════════════
   SIDEBAR CONTEXT MENU
   ════════════════════════════════════════════════════════════════ */

.sidebar-ctx-menu {
  position: fixed;
  z-index: 9000;
  width: 190px;
  background: rgba(5, 5, 7, 0.98);
  border: 1px solid rgba(255,255,255,0.075);
  border-radius: 4px;
  padding: 5px;
  box-shadow: 0 14px 42px rgba(0,0,0,0.48), 0 2px 8px rgba(0,0,0,0.28);
  display: none;
  flex-direction: column;
  gap: 1px;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 120ms ease, transform 120ms ease;
  pointer-events: none;
  backdrop-filter: blur(18px);
}

/* Arrow pointing UP (menu below element) */
.sidebar-ctx-menu::before {
  content: "";
  position: absolute;
  top: -6px;
  left: var(--arrow-x, 50%);
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 6px solid rgba(5, 5, 7, 0.98);
  pointer-events: none;
}

/* Arrow pointing DOWN (menu above element) */
.sidebar-ctx-menu[data-flip="up"]::before {
  top: auto;
  bottom: -6px;
  border-bottom: none;
  border-top: 6px solid rgba(5, 5, 7, 0.98);
}

.sidebar-ctx-menu.is-open {
  display: flex;
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* ── Menu items (Rename / Delete) ── */
.sidebar-ctx-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 30px;
  padding: 6px 9px;
  font-family: var(--font);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(232,229,228,0.72);
  background: transparent;
  border: none;
  border-radius: 2px;
  cursor: pointer;
  text-align: left;
  line-height: 1.15;
  transition: background 80ms ease, color 80ms ease, box-shadow 80ms ease;
  outline: none;
}

.sidebar-ctx-item:hover {
  background: rgba(255,255,255,0.055);
  color: rgba(255,255,255,0.95);
}

.sidebar-ctx-item span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Icon in menu items */
.sidebar-ctx-item-icon {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(70%);
  opacity: 0.78;
  pointer-events: none;
  transition: opacity 80ms ease;
}

.sidebar-ctx-item:hover .sidebar-ctx-item-icon {
  opacity: 1;
  filter: brightness(0) saturate(0%) invert(90%);
}

/* Delete — yellow-orange like the Clear button */
.sidebar-ctx-item--delete {
  color: var(--glow);
}

.sidebar-ctx-item--delete .sidebar-ctx-item-icon {
  filter: brightness(0) saturate(100%) invert(78%) sepia(60%) saturate(600%) hue-rotate(5deg) brightness(105%);
  opacity: 1;
}

.sidebar-ctx-item--delete:hover {
  background: rgba(var(--glow-rgb), 0.08);
  color: var(--glow);
}

.sidebar-ctx-item--delete:hover .sidebar-ctx-item-icon {
  filter: brightness(0) saturate(100%) invert(90%) sepia(60%) saturate(600%) hue-rotate(5deg) brightness(110%);
}

/* ── Change Color section ── */
.sidebar-ctx-color-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 9px 7px;
  border-radius: 2px;
}

/* "Change Color" header row — icon + label, same alignment as Rename/Delete */
.sidebar-ctx-color-header {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(232,229,228,0.68);
  user-select: none;
  pointer-events: none;
}

/* Color grid — 9 columns = 4 compact rows for the 36 swatches. */
.sidebar-ctx-color-grid {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  gap: 3px;
  margin-left: 22px;
}

/* Swatches — rounded squares, no scale on hover */
.sidebar-ctx-swatch {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 2px;
  border: none;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  outline: none;
  position: relative;
  transition: box-shadow 80ms ease;
}

.sidebar-ctx-swatch:hover {
  box-shadow: 0 0 0 1px rgba(255,255,255,0.72);
}

/* Selected — just a small black dot at center, no border/outline */
.sidebar-ctx-swatch.is-selected::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(0,0,0,0.8);
  pointer-events: none;
}

/* Legacy — hide old submenu if present */
.sidebar-ctx-submenu {
  display: none !important;
}

/* Legacy separator — hidden */
.sidebar-ctx-separator {
  display: none;
}

/* Legacy swatch--none — hidden */
.sidebar-ctx-swatch--none {
  display: none;
}

/* Legacy danger item */
.sidebar-ctx-item--danger {
  color: var(--glow);
}

.sidebar-ctx-item--danger img:not(.sidebar-ctx-item-icon) {
  filter: brightness(0) saturate(100%) invert(78%) sepia(60%) saturate(600%) hue-rotate(5deg) brightness(105%);
  opacity: 1;
}

.sidebar-ctx-item--danger:hover {
  background: rgba(var(--glow-rgb), 0.08);
  color: var(--glow);
}

/* ── Sidebar color accent — a background gradient on .sidebar-item-btn, set by
      applyItemColor() in src/main.js. Nothing to declare here; the note in that
      function says why it is not the inset box-shadow it looks like. ── */

/* ════════════════════════════════════════════════════════════════
   SAMPLE CONTEXT MENU
   Identique au .sidebar-ctx-menu — background sidebar, même tout
════════════════════════════════════════════════════════════════ */

.sample-ctx-menu {
  position: fixed;
  z-index: 9000;
  width: 232px;
  background: rgba(5, 5, 7, 0.98);
  border: 1px solid rgba(255,255,255,0.075);
  border-radius: 4px;
  padding: 5px;
  box-shadow: 0 14px 42px rgba(0,0,0,0.48), 0 2px 8px rgba(0,0,0,0.28);
  display: none;
  flex-direction: column;
  gap: 1px;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 120ms ease, transform 120ms ease;
  pointer-events: none;
  backdrop-filter: blur(18px);
}

/* Arrow pointing UP (menu below click point) */
.sample-ctx-menu::before {
  content: "";
  position: absolute;
  top: -6px;
  left: var(--arrow-x, 50%);
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 6px solid rgba(5, 5, 7, 0.98);
  pointer-events: none;
}

/* Arrow pointing DOWN (menu above click point) */
.sample-ctx-menu[data-flip="up"]::before {
  top: auto;
  bottom: -6px;
  border-bottom: none;
  border-top: 6px solid rgba(5, 5, 7, 0.98);
}

.sample-ctx-menu.is-open {
  display: flex;
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* "Add to Collection" row — div styled as button, holds sub-popup */
.sample-ctx-collections-row {
  cursor: default;
  outline: none;
  position: relative;
}

/* Arrow icon — left-aligned like the other menu icons, rotated toward sub-popup */
.sample-ctx-arrow-icon {
  transform: rotate(90deg);
  opacity: 0.58;
}

/* Sub-popup — appears on hover to the LEFT of main menu */
.sample-ctx-sub-popup {
  display: none;
  position: absolute;
  right: calc(100% + 7px);
  top: -5px;
  width: 220px;
  background: rgba(5, 5, 7, 0.98);
  border: 1px solid rgba(255,255,255,0.075);
  border-radius: 4px;
  padding: 5px;
  box-shadow: 0 14px 42px rgba(0,0,0,0.48), 0 2px 8px rgba(0,0,0,0.28);
  flex-direction: column;
  z-index: 9100;
  max-height: 320px;
  gap: 1px;
  backdrop-filter: blur(18px);
}

.sample-ctx-collections-row.is-sub-open .sample-ctx-sub-popup {
  display: flex;
}

/* Invisible bridge — left side, keeps hover alive while moving to sub-popup */
.sample-ctx-collections-row.is-sub-open::after {
  content: "";
  position: absolute;
  left: -7px;
  top: 0;
  width: 7px;
  height: 100%;
}

/* "Add to a New Collection" button — top of sub-popup, no size overrides */
.sample-ctx-new-coll-btn {
  flex-shrink: 0;
}

/* Separator between "new collection" and the list */
.sample-ctx-sub-separator {
  height: 1px;
  background: rgba(255,255,255,0.07);
  margin: 4px 2px;
  flex-shrink: 0;
}

/* Scrollable collections list */
.sample-ctx-collections-list {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  flex: 1 1 auto;
}

/* Check icon — right-aligned, same size as sidebar-ctx-item-icon */
.sample-ctx-check-icon {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  margin-left: auto;
  filter: brightness(0) saturate(0%) invert(80%);
  opacity: 0.9;
  pointer-events: none;
}

/* Collection already contains this sample — red tint on hover to signal removal */
.sample-ctx-col-btn.is-in-collection {
  color: rgba(255, 255, 255, 0.75);
}
.sample-ctx-col-btn.is-in-collection:hover {
  background: rgba(220, 60, 60, 0.13);
  color: rgba(255, 100, 100, 1);
}
.sample-ctx-col-btn.is-in-collection:hover .sidebar-ctx-item-icon {
  filter: var(--glow-filter);
}
.sample-ctx-col-btn.is-in-collection:hover .sample-ctx-check-icon {
  filter: var(--glow-filter);
  opacity: 1;
}

/* Force icon size inside sample ctx menu to match sidebar ctx menu exactly */
.sample-ctx-menu .sidebar-ctx-item-icon,
.sample-ctx-sub-popup .sidebar-ctx-item-icon {
  width: 13px;
  height: 13px;
}

/* Empty state */
.sample-ctx-empty {
  padding: 8px 9px;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.3);
  font-style: italic;
  font-family: var(--font);
}


/* ── New Collection Modal input ── */
.new-coll-input {
  width: 100%;
  background: rgba(255,255,255,0.07);
  border: none;
  border-radius: 2px;
  padding: 8px 10px;
  font-family: var(--font);
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.85);
  outline: none;
  box-sizing: border-box;
  transition: background 80ms ease;
}

.new-coll-input::placeholder {
  color: rgba(255,255,255,0.25);
}

.new-coll-input:focus {
  background: rgba(255,255,255,0.11);
}

.new-coll-follow-option {
  margin-top: 10px;
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 9px 10px;
  border-radius: 3px;
  background: rgba(255,255,255,0.045);
  cursor: pointer;
  user-select: none;
}

.new-coll-follow-option[hidden] {
  display: none;
}

.new-coll-follow-option input {
  display: none;
}

.new-coll-follow-check {
  width: 14px;
  height: 14px;
  margin-top: 1px;
  border-radius: 2px;
  border: 1.5px solid rgba(255,255,255,0.2);
  background: transparent;
  flex-shrink: 0;
  position: relative;
  transition: border-color 120ms ease, background 120ms ease;
}

.new-coll-follow-option input:checked + .new-coll-follow-check {
  background: var(--glow);
  border-color: var(--glow);
}

.new-coll-follow-option input:checked + .new-coll-follow-check::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 1px;
  width: 5px;
  height: 8px;
  border: 1.5px solid var(--hairline);
  border-top: none;
  border-left: none;
  transform: rotate(45deg);
}

.new-coll-follow-copy {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.new-coll-follow-title {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255,255,255,0.78);
}

.new-coll-follow-desc {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  line-height: 1.35;
  color: rgba(255,255,255,0.38);
}

/* ════════════════════════════════════════════════════════════════
   Toast / Action Feedback Bar
   ════════════════════════════════════════════════════════════════ */

#samply-toast-container {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 99999;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-bottom: 18px;
  gap: 8px;
}

.samply-toast {
  pointer-events: all;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--raised);
  border: 1px solid rgba(255,255,255,0.10);
  border-top-color: rgba(255,255,255,0.16);
  border-radius: 5px;
  padding: 0 6px 0 14px;
  height: 38px;
  min-width: 240px;
  max-width: 460px;
  box-shadow:
    0 4px 24px rgba(0,0,0,0.55),
    0 1px 4px rgba(0,0,0,0.4),
    inset 0 1px 0 rgba(255,255,255,0.055);
  font-family: var(--font);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.82);
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;

  /* Animation */
  transform: translateY(20px);
  opacity: 0;
  transition:
    transform 280ms cubic-bezier(0.22, 1, 0.36, 1),
    opacity 220ms ease;
  will-change: transform, opacity;
}

.samply-toast.is-visible {
  transform: translateY(0);
  opacity: 1;
}

.samply-toast.is-hiding {
  transform: translateY(12px);
  opacity: 0;
  transition:
    transform 240ms cubic-bezier(0.4, 0, 1, 1),
    opacity 200ms ease;
}

.samply-toast-icon {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  opacity: 0.7;
  /* On utilise un SVG inline via filter pour coller aux icônes de l'app */
}

.samply-toast-dot {
  flex-shrink: 0;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(255,255,255,0.30);
}

.samply-toast-dot--like {
  background: var(--danger);
}

.samply-toast-dot--collection {
  background: var(--glow);
}

.samply-toast-dot--delete {
  background: var(--warning);
}

.samply-toast-dot--new {
  background: var(--success);
}

.samply-toast-text {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

.samply-toast-text strong {
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255,255,255,0.95);
}

/* The one thing in a toast the user can press. It reads as the accent, like
   Analyze in the AI dialogue, because it is the same kind of offer. */
.samply-toast-action {
  flex-shrink: 0;
  border: none;
  background: transparent;
  color: var(--glow);
  font: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.01em;
  cursor: pointer;
  padding: 4px 8px;
  margin-left: 4px;
  border-radius: 3px;
  transition: background 120ms ease;
}

.samply-toast-action:hover {
  background: rgba(255,255,255,0.08);
}

.samply-toast-close {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: none;
  background: transparent;
  cursor: pointer;
  border-radius: 3px;
  opacity: 0.38;
  transition: opacity 120ms ease, background 120ms ease;
  padding: 0;
  margin-left: 2px;
}

.samply-toast-close:hover {
  opacity: 0.75;
  background: rgba(255,255,255,0.08);
}

.samply-toast-close svg {
  width: 10px;
  height: 10px;
  display: block;
}
/* ════════════════════════════════════════════════════════════════
   RESCAN MODAL
════════════════════════════════════════════════════════════════ */

.rescan-modal {
  min-width: 380px;
}

.rescan-scope-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}

.rescan-option-row {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 5px;
  transition: background 0.12s;
}
.rescan-option-row:hover {
  background: rgba(255,255,255,0.05);
}
.rescan-option-row.is-disabled {
  opacity: 0.38;
  cursor: not-allowed;
  pointer-events: none;
}
.rescan-option-separator {
  height: 1px;
  margin: 6px 8px;
  background: rgba(255,255,255,0.08);
}
.rescan-option-badge {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: #fff;
  background: var(--glow);
  border-radius: 12px;
  padding: 1px 6px;
  margin-left: auto;
  flex-shrink: 0;
}

.rescan-radio {
  accent-color: var(--glow);
  width: 14px;
  height: 14px;
  cursor: pointer;
}

.rescan-option-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.8);
}

.rescan-file-selector {
  position: relative;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 8px;
  padding: 6px 0;
  background: rgba(0,0,0,0.15);
  overflow-y: auto;
}

.rescan-file-loading {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 76px;
  color: rgba(255,255,255,0.52);
  font-size: calc(12px * var(--ui-font-scale, 1));
  background: rgba(24,24,24,0.92);
}

.rescan-file-loading[hidden] {
  display: none;
}

.rescan-file-loading-spinner {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border: 2px solid rgba(var(--glow-rgb), 0.22);
  border-top-color: var(--glow);
  border-radius: 50%;
  animation: rescan-file-spin 0.7s linear infinite;
}

@keyframes rescan-file-spin {
  to { transform: rotate(360deg); }
}

.rescan-file-list {
  padding: 0 8px;
}

.rescan-file-list.is-loading {
  min-height: 76px;
  visibility: hidden;
}

.rescan-file-group {
  padding: 0;
  margin-bottom: 4px;
}

.rescan-group-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 0 3px calc(var(--rescan-depth, 0) * 14px);
  cursor: pointer;
}

.rescan-group-toggle {
  width: 14px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  padding: 0;
  background: transparent;
  color: rgba(255,255,255,0.45);
  cursor: pointer;
  transform: rotate(0deg);
  transition: transform 0.12s ease, color 0.12s ease;
}

.rescan-group-toggle svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.6;
}

.rescan-group-toggle.is-expanded {
  color: rgba(255,255,255,0.75);
  transform: rotate(90deg);
}

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

.rescan-group-check {
  accent-color: var(--glow);
  cursor: pointer;
}

.rescan-group-name {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255,255,255,0.5);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  flex: 1;
}

.rescan-group-count {
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.3);
}

.rescan-file-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 0 3px calc(16px + var(--rescan-depth, 0) * 14px);
  cursor: pointer;
  border-radius: 3px;
  transition: background 0.1s;
}
.rescan-file-row:hover {
  background: rgba(255,255,255,0.04);
}

.rescan-file-check {
  accent-color: var(--glow);
  cursor: pointer;
  flex-shrink: 0;
}

.rescan-file-name {
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.7);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rescan-file-status {
  font-size: calc(11px * var(--ui-font-scale, 1));
  padding: 1px 6px;
  border-radius: 12px;
  font-weight: var(--ui-weight-medium, 500);
  flex-shrink: 0;
}
.rescan-file-status.is-ai    { background: rgba(var(--glow-rgb), 0.15); color: var(--glow); }
.rescan-file-status.is-basic { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.5); }
.rescan-file-status.is-none  { background: rgba(255,100,80,0.1); color: rgba(255,100,80,0.6); }
.rescan-file-status.is-skip  { background: rgba(74,147,227,0.14); color: var(--glow); }

.rescan-group-statuses {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.rescan-show-more {
  margin: 6px 0 4px calc(16px + var(--rescan-depth, 0) * 14px);
  border: 0;
  background: transparent;
  color: var(--glow);
  font-size: calc(11px * var(--ui-font-scale, 1));
  cursor: pointer;
}

.rescan-show-more:hover {
  color: var(--glow);
}

/* ── Sidebar ctx rescan item — même style que rename ── */
.sidebar-ctx-item--rescan {
  color: rgba(255,255,255,0.72);
}
.sidebar-ctx-item--rescan:hover {
  background: rgba(255,255,255,0.055);
  color: rgba(255,255,255,0.95);
}
.sidebar-ctx-item--rescan .sidebar-ctx-item-icon {
  width: 13px;
  height: 13px;
}

.sidebar-ctx-item--relink {
  color: rgba(255,255,255,0.78);
}

.sidebar-ctx-item--relink:hover {
  background: rgba(var(--glow-rgb), 0.07);
  color: rgba(255,255,255,0.96);
}

.sidebar-ctx-item--relink .sidebar-ctx-item-icon {
  width: 13px;
  height: 13px;
}

.folder-relink-backdrop {
  position: fixed;
  inset: 0;
  z-index: 760;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 22px;
  background: rgba(0,0,0,0.34);
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

.folder-relink-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.folder-relink-modal {
  width: min(420px, calc(100vw - 44px));
  padding: 22px;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 5px;
  background: var(--raised);
  box-shadow: 0 18px 60px rgba(0,0,0,0.38);
  transform: translateY(8px) scale(0.98);
  transition: transform 150ms ease;
}

.folder-relink-backdrop.is-open .folder-relink-modal {
  transform: translateY(0) scale(1);
}

.folder-relink-kicker {
  margin-bottom: 10px;
  color: var(--glow);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.folder-relink-title {
  color: rgba(255,255,255,0.9);
  font-size: calc(18px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  line-height: 1.15;
}

.folder-relink-copy {
  margin: 12px 0 14px;
  color: rgba(255,255,255,0.5);
  font-size: calc(13px * var(--ui-font-scale, 1));
  line-height: 1.45;
}

.folder-relink-path {
  min-height: 34px;
  padding: 9px 10px;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 3px;
  background: rgba(255,255,255,0.04);
  color: rgba(255,255,255,0.48);
  font-size: calc(11px * var(--ui-font-scale, 1));
  line-height: 1.35;
  word-break: break-all;
}

.folder-relink-error {
  margin-top: 10px;
  color: var(--danger);
  font-size: calc(12px * var(--ui-font-scale, 1));
  line-height: 1.35;
}

.folder-relink-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 18px;
}

.folder-relink-btn {
  height: 38px;
  border: 0;
  border-radius: 2px;
  cursor: pointer;
  font-family: inherit;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
}

.folder-relink-btn:disabled {
  cursor: default;
  opacity: 0.6;
}

.folder-relink-btn--cancel {
  background: rgba(255,255,255,0.08);
  color: rgba(255,255,255,0.62);
}

.folder-relink-btn--cancel:hover:not(:disabled) {
  background: rgba(255,255,255,0.12);
  color: rgba(255,255,255,0.82);
}

.folder-relink-btn--choose {
  background: var(--glow);
  color: var(--ink);
}

.folder-relink-btn--choose:hover:not(:disabled) {
  background: var(--glow);
}

/* ════════════════════════════════════════════════════════════════
   SETTINGS — LIBRARY OVERVIEW
════════════════════════════════════════════════════════════════ */

.settings-library-folders {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.settings-library-folder-card {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  padding: 12px 14px 10px;
}

.settings-library-folder-header {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 3px;
}

.settings-library-folder-icon {
  width: 14px;
  height: 14px;
  opacity: 0.7;
  flex-shrink: 0;
}

.settings-library-folder-name {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255,255,255,0.9);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.settings-library-folder-mode {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  padding: 2px 7px;
  border-radius: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  flex-shrink: 0;
}
.settings-library-folder-mode--ai    { background: rgba(var(--glow-rgb), 0.15); color: var(--glow); }
.settings-library-folder-mode--basic { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.5); }

.settings-library-folder-path {
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.3);
  margin-bottom: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.settings-library-folder-stats {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.settings-library-stat {
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.45);
}

.settings-library-stat-num {
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255,255,255,0.7);
}
.settings-library-stat-num--ok   { color: var(--success); }
.settings-library-stat-num--ai   { color: var(--glow); }
.settings-library-stat-num--warn { color: rgba(255,120,80,0.8); }

.settings-library-stat--warn {
  color: rgba(255,120,80,0.6);
}

.settings-library-folder-actions {
  display: flex;
  gap: 6px;
  margin-bottom: 6px;
}

.settings-action-btn--small {
  font-size: calc(12px * var(--ui-font-scale, 1));
  padding: 4px 10px;
}

/* ── Settings AI installed model list ────────────────────────── */
.settings-ai-model-list-empty {
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.28);
  padding: 10px 0 4px;
}

.settings-ai-installed-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 12px;
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: 5px;
  margin-bottom: 6px;
}
.settings-ai-installed-row.is-active {
  border-color: rgba(255,255,255,0.14);
  background: rgba(255,255,255,0.065);
}

.settings-ai-installed-info {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.settings-ai-installed-name {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  color: rgba(255,255,255,0.82);
  white-space: nowrap;
}

.settings-ai-installed-badge {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.05em;
  padding: 2px 6px;
  border-radius: 2px;
  background: rgba(154,230,180,0.15);
  color: var(--success);
  border: 1px solid rgba(154,230,180,0.25);
  white-space: nowrap;
}

.settings-ai-installed-size {
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.32);
  white-space: nowrap;
}

.settings-library-toggle-files {
  background: none;
  border: none;
  color: rgba(255,255,255,0.35);
  font-size: calc(11px * var(--ui-font-scale, 1));
  cursor: pointer;
  padding: 2px 0;
  transition: color 0.12s;
}
.settings-library-toggle-files:hover {
  color: rgba(255,255,255,0.65);
}

.settings-library-file-list {
  margin-top: 8px;
  border-top: 1px solid rgba(255,255,255,0.06);
  padding-top: 8px;
  max-height: 200px;
  overflow-y: auto;
}

.settings-library-subgroup {
  margin-bottom: 8px;
}

.settings-library-subdir {
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 3px;
  padding: 0 2px;
}

.settings-library-file-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 2px;
  border-radius: 3px;
}

.settings-library-file-name {
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: rgba(255,255,255,0.6);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.settings-library-file-status {
  font-size: calc(11px * var(--ui-font-scale, 1));
  padding: 1px 6px;
  border-radius: 12px;
  font-weight: var(--ui-weight-medium, 500);
  flex-shrink: 0;
}
.settings-library-file-status.is-ai    { background: rgba(var(--glow-rgb), 0.15); color: var(--glow); }
.settings-library-file-status.is-basic { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.4); }
.settings-library-file-status.is-none  { background: rgba(255,100,80,0.08); color: rgba(255,100,80,0.55); }

.settings-library-empty {
  padding: 12px 4px;
}
/* ════════════════════════════════════════════════════════════════
   DRAG & DROP — Splice-style
   ════════════════════════════════════════════════════════════════ */

/* Ghost avatar qui suit la souris — construit en JS */
#samply-drag-ghost {
  box-shadow: 0 8px 24px rgba(0,0,0,0.55), 0 2px 6px rgba(0,0,0,0.4);
  cursor: grabbing;
  user-select: none;
}

#samply-folder-drag-ghost {
  position: fixed;
  z-index: 99999;
  display: none;
  align-items: center;
  gap: 8px;
  max-width: 260px;
  padding: 7px 10px;
  background: rgba(5,5,7,0.96);
  border: 1px solid rgba(var(--glow-rgb), 0.45);
  border-radius: 4px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.46);
  color: rgba(255,255,255,0.86);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -50%);
  backdrop-filter: blur(14px);
  opacity: 0.8;
}

#samply-folder-drag-ghost img {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(78%);
}

#samply-folder-drag-ghost span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#samply-folder-drag-ghost small {
  flex-shrink: 0;
  color: rgba(var(--glow-rgb), 0.9);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

/* Row en cours de drag → légèrement assombrie */
.sample-row.is-dragging {
  opacity: 0.45;
  pointer-events: none;
}

.tree-folder-row.is-folder-dragging {
  opacity: 0.48;
}

/* Sidebar collection en drop-over → contour bleu clair */
.sidebar-item-btn.drag-over,
#collections-likes-btn.drag-over {
  outline: 2px solid var(--glow);
  outline-offset: -2px;
  background: rgba(var(--glow-rgb), 0.08) !important;
  border-radius: 5px;
}

/* LA MEME MARQUE POUR LES DEUX ZONES.
   Un dossier de l'arborescence se lache maintenant dans Places aussi, ou il
   devient un raccourci vers ce qu'on a deja. Le selecteur ne nommait que les
   collections, et la zone du haut ne s'allumait pas : on visait sans savoir si
   on avait vise juste. */
.sidebar-scroll-zone.folder-drop-empty {
  background: rgba(var(--glow-rgb), 0.045);
  box-shadow: inset 0 0 0 1px rgba(var(--glow-rgb), 0.34);
}

.sidebar-scroll-zone.folder-drop-empty::after {
  content: "";
  position: absolute;
  left: var(--sl-pad-left, 24px);
  right: 16px;
  bottom: 5px;
  height: 2px;
  border-radius: 999px;
  background: var(--glow);
  box-shadow: 0 0 12px rgba(var(--glow-rgb), 0.55);
  pointer-events: none;
  z-index: 3;
}

.sidebar-item-btn.folder-drop-before::before,
.sidebar-item-btn.folder-drop-after::after {
  content: "";
  position: absolute;
  left: var(--sl-pad-left, 24px);
  right: 16px;
  height: 2px;
  border-radius: 999px;
  background: var(--glow);
  box-shadow: 0 0 12px rgba(var(--glow-rgb), 0.55);
  pointer-events: none;
  z-index: 4;
}

.sidebar-item-btn.folder-drop-before::before {
  top: -2px;
}

.sidebar-item-btn.folder-drop-after::after {
  bottom: -2px;
}

/* Disable text selection globale pendant le drag */
body.is-sample-dragging,
body.is-tree-folder-dragging {
  user-select: none;
  cursor: grabbing !important;
}
/* ── Footer Transport Controls (Prev / Play-Stop / Next) ─────── */
.footer-transport {
  display: flex;
  align-items: center;
  gap: 0px;
  flex-shrink: 0;
  margin-left: auto;
  padding-right: 6px;
}

.footer-transport-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: transparent;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  padding: 0;
  outline: none;
  transition: opacity 100ms ease;
}

.footer-transport-btn:disabled {
  opacity: 0.18;
  cursor: not-allowed;
}

.footer-transport-btn:focus-visible {
  outline: 1.5px solid rgba(255, 255, 255, 0.3);
  outline-offset: 1px;
}

.transport-icon {
  width: 18px;
  height: 18px;
  fill: rgba(255, 255, 255, 0.45);
  flex-shrink: 0;
  pointer-events: none;
  transition: fill 120ms ease;
  display: block;
}

.footer-transport-btn:hover:not(:disabled) .transport-icon {
  fill: rgba(255, 255, 255, 0.92);
}

/* Play button slightly bigger */
.footer-transport-btn--play {
  width: 36px;
  height: 36px;
}

.footer-transport-btn--play .transport-icon {
  width: 20px;
  height: 20px;
}

/* Playing state — accent color on play/stop icon */
.footer-transport-btn--play.is-playing .transport-icon {
  fill: var(--glow);
}

.footer-transport-btn--play:hover:not(:disabled).is-playing .transport-icon {
  fill: var(--glow);
}

/* ════════════════════════════════════════════════════════════════
   STACK PAGE — styles to append to styles.css
   ════════════════════════════════════════════════════════════════ */

/* ── Hide/show pages ──────────────────────────────────────────── */
/* Hide global footer in stack mode — stack has its own footer */

/* Make the stack page fill the main area */

/* ── Stack footer ──────────────────────────────────────────────── */

/* ── Mode bar ─────────────────────────────────────────────────── */

/* ── Controls row ─────────────────────────────────────────────── */

/* ── Center controls ──────────────────────────────────────────── */

/* Genre dropdown */

/* Genre popup */

/* BPM input */

/* Key dropdown — reuse header-dropdown but need to position correctly */

/* ── Right controls ────────────────────────────────────────────── */

/* Save dropdown group */

/* Saves popup */

/* ── Tracks area ───────────────────────────────────────────────── */

/* ── Individual track row ──────────────────────────────────────── */

/* Color strip */

/* Type selector */

/* Pack cover */

/* pack-avatar inside stack cover fills the whole cell */

/* Sample name */

/* Waveform */

/* Waveform playhead clip — smooth transition driven by RAF */

/* Sliders */

/* Action buttons */

/* ── Add track button ──────────────────────────────────────────── */

/* ── Color picker ──────────────────────────────────────────────── */

/* ── Sidebar stack section ─────────────────────────────────────── */
/* When on stack page, sidebar "Saved" section is visible */

/* ── Scrollbar styling for tracks wrapper ──────────────────────── */

/* Genre list scrollbar */
.import-progress-surface {
  position: absolute;
  inset: 0;
  z-index: 8;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--app-background);
}

.app-main.has-import-progress-surface {
  overflow: hidden;
}

.import-progress-panel {
  width: min(420px, calc(100% - 56px));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.import-progress-kicker {
  color: var(--glow);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  text-transform: uppercase;
}

.import-progress-title {
  color: rgba(255, 255, 255, 0.84);
  font-size: calc(18px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

.import-progress-detail {
  min-height: 18px;
  color: rgba(255, 255, 255, 0.38);
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

.import-progress-track {
  width: 100%;
  height: 4px;
  border-radius: 2px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.1);
}

.import-progress-fill {
  width: 0;
  height: 100%;
  border-radius: 2px;
  background: var(--glow);
  transition: width 180ms ease;
}

.import-progress-percent {
  height: 12px;
  color: rgba(255, 255, 255, 0.34);
  font-size: calc(11px * var(--ui-font-scale, 1));
  text-align: right;
}

.import-progress-surface.is-indeterminate .import-progress-track {
  position: relative;
}

.import-progress-surface.is-indeterminate .import-progress-fill {
  width: 28% !important;
  animation: analysis-sweep 1.4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  transition: none;
}

.import-progress-surface.is-result .import-progress-title {
  color: var(--glow);
}

/* ═══════════════════════════════════════════════════════════════
   TREE VIEW — Sidebar toggle button + arborescence panel
   ═══════════════════════════════════════════════════════════════ */

/* ── Sidebar page header row (Browser + toggle btn) ── */
.sidebar-page-header {
  display: flex;
  align-items: center;
  width: 100%;
  flex-shrink: 0;
  position: relative;
}

/* The existing sidebar-page-btn fills the row */
.sidebar-page-header .sidebar-page-btn {
  flex: 1 1 auto;
}

/* ── Toggle button ── */
.sidebar-tree-toggle {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin-right: 10px;
  background: transparent;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  outline: none;
  -webkit-app-region: no-drag;
}

.sidebar-tree-toggle-icon {
  display: block;
  width: 14px;
  height: 14px;
  object-fit: contain;
  /* #b6b2b1 — même couleur que le texte Browser */
  filter: brightness(0) saturate(0%) invert(66%);
  transition: filter 80ms ease;
}

/* hover (off et on identiques) : #e0dedd — même surbrillance que Browser hover */
.sidebar-tree-toggle:hover .sidebar-tree-toggle-icon,
.sidebar-tree-toggle[aria-pressed="true"]:hover .sidebar-tree-toggle-icon {
  filter: brightness(0) saturate(0%) invert(88%);
}

/* on state : même couleur que off, pas de jaune */
.sidebar-tree-toggle[aria-pressed="true"] .sidebar-tree-toggle-icon {
  filter: brightness(0) saturate(0%) invert(66%);
}


/* ── Tree view panel — sits above footer, replaces header+subheader+main ── */
.tree-view-panel {
  --sl-pad-left: var(--main-content-pad-left);
  --sl-pad-right: var(--main-content-pad-right);
  position: absolute;
  inset: calc(var(--header-height) + var(--header-top-gap) + 36px) 0 0 0;
  bottom: var(--footer-height);
  background: var(--app-background);
  display: none;
  flex-direction: column;
  z-index: 10;
  overflow: hidden;
}

#app[data-tabs-enabled="true"] .tree-view-panel {
  inset: calc(var(--header-height) + var(--header-top-gap) + 36px + var(--tabbar-height)) 0 0 0;
  bottom: var(--footer-height);
}

/* When active */
.tree-view-panel.is-active {
  display: flex;
}

/* app-stage needs position:relative so the absolute panel works */
.app-stage {
  position: relative;
}

/* ── Header (mirrors sample-list-header) ── */
.tree-view-header {
  flex: 0 0 auto;
  background: var(--header-background);
  border-top: var(--header-top-gap) solid var(--sidebar-background);
}

.tree-view-list-header {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.55);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  height: 30px;
  padding-top: 12px;
}

/* ── Scrollable body ── */
.tree-view-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding-bottom: 12px;
  position: relative;
}

/* ── Empty state ── */
.tree-view-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 200px;
  color: rgba(255, 255, 255, 0.22);
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  text-align: center;
  padding: 0 32px;
}

/* ── Folder row ── */
.tree-folder-row {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 6px var(--sl-pad-right, 20px) 6px var(--sl-pad-left, 24px);
  padding-left: calc(var(--sl-pad-left, 24px) + var(--tree-indent, 0px));
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.75);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  outline: none;
  letter-spacing: 0.01em;
  border-top: 1px solid rgba(255,255,255,0.04);
  margin-top: 2px;
}

.tree-folder-row:hover {
  color: var(--text);
  background: rgba(255,255,255,0.03);
}

.tree-folder-chevron {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 150ms ease;
}

.tree-folder-chevron svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.6;
}

.tree-folder-row.is-open .tree-folder-chevron {
  transform: rotate(90deg);
}

.tree-folder-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(75%);
  opacity: 0.65;
}

.tree-folder-name {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tree-folder-count {
  flex-shrink: 0;
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255, 255, 255, 0.35);
  margin-left: 4px;
}

/* ── Sample rows inside tree — indent per depth ── */
.tree-sample-row {
  /* same as .sample-row but with indent */
  --sl-pad-left: calc(var(--main-content-pad-left) + var(--tree-indent, 0px) + 17px);
  --sl-pad-right: var(--main-content-pad-right);
}

/* ── Children container ── */
.tree-folder-children {
  display: block;
}

.tree-folder-children.is-collapsed {
  display: none;
}

/* ── Loading ── */
.tree-view-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  position: absolute;
  inset: 0;
  padding: 0 48px;
  pointer-events: none;
}

.tree-view-loading-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255, 255, 255, 0.38);
  letter-spacing: 0.01em;
}

/* ══════════════════════════════════════════════════════════════════
   SIMILARITY PANEL — right-side audio match inspector
   ══════════════════════════════════════════════════════════════════ */

.similarity-panel {
  --sim-panel-pad: 14px;
  position: absolute;
  top: calc(var(--header-height) + var(--header-top-gap) + 36px);
  right: 0;
  bottom: var(--footer-height);
  width: var(--similarity-panel-width);
  display: none;
  flex-direction: column;
  min-width: 0;
  background: var(--raised);
  border-left: 1px solid rgba(255,255,255,0.065);
  box-shadow: -14px 0 28px rgba(0,0,0,0.13);
  z-index: 14;
  overflow: hidden;
}

#app[data-tabs-enabled="true"] .similarity-panel {
  top: calc(var(--header-height) + var(--header-top-gap) + 36px + var(--tabbar-height));
}

.similarity-panel.is-open {
  display: flex;
}

.app-stage.has-similarity-panel .tree-view-panel {
  right: var(--similarity-panel-width);
}

.similarity-panel-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px var(--sim-panel-pad) 12px;
  border-bottom: 1px solid rgba(255,255,255,0.055);
}

.similarity-panel-kicker {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(var(--glow-rgb), 0.78);
}

.similarity-panel-title {
  margin-top: 2px;
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.78);
}

.similarity-panel-close {
  position: relative;
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  border: none;
  border-radius: 4px;
  background: rgba(255,255,255,0.045);
  color: rgba(255,255,255,0.48);
  cursor: pointer;
}

.similarity-panel-close:hover {
  background: rgba(255,255,255,0.075);
  color: rgba(255,255,255,0.72);
}

.similarity-panel-close span::before,
.similarity-panel-close span::after {
  content: "";
  position: absolute;
  left: 8px;
  top: 12px;
  width: 10px;
  height: 1.4px;
  border-radius: 2px;
  background: currentColor;
}

.similarity-panel-close span::before { transform: rotate(45deg); }
.similarity-panel-close span::after  { transform: rotate(-45deg); }

.similarity-target {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  gap: 11px;
  padding: 13px var(--sim-panel-pad) 12px;
  border-bottom: 1px solid rgba(255,255,255,0.045);
}

.similarity-target-cover {
  width: 56px;
  height: 56px;
  border-radius: 4px;
}

.similarity-target-meta {
  min-width: 0;
  align-self: center;
}

.similarity-target-label {
  margin-bottom: 3px;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.28);
}

.similarity-target-name {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  line-height: 1.25;
  color: rgba(255,255,255,0.76);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.similarity-target-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 7px;
  max-height: 36px;
  overflow: hidden;
}

.similarity-target-tag {
  flex: 0 0 auto;
  padding: 1px 5px;
  border-radius: 2px;
  background: rgba(255,255,255,0.055);
  color: rgba(255,255,255,0.38);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

.similarity-mode-bar {
  flex: 0 0 auto;
  padding: 10px var(--sim-panel-pad) 9px;
  border-bottom: 1px solid rgba(255,255,255,0.045);
}

.similarity-mode-label {
  margin-bottom: 7px;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.32);
}

.similarity-mode-options {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}

.similarity-mode-btn {
  min-width: 0;
  height: 29px;
  padding: 0 6px;
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: 4px;
  background: rgba(255,255,255,0.045);
  color: rgba(255,255,255,0.48);
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.similarity-mode-btn:hover {
  background: rgba(255,255,255,0.075);
  color: rgba(255,255,255,0.74);
}

.similarity-mode-btn.is-active {
  border-color: rgba(var(--glow-rgb), 0.72);
  background: var(--glow);
  color: var(--ink);
}

.similarity-status {
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 10px var(--sim-panel-pad) 6px;
  color: rgba(255,255,255,0.42);
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

.similarity-status-count {
  min-width: 0;
  max-width: 120px;
  color: rgba(255,255,255,0.26);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: right;
}

.similarity-progress {
  flex: 0 0 auto;
  height: 2px;
  margin: 0 var(--sim-panel-pad) 8px;
  overflow: hidden;
  background: rgba(255,255,255,0.055);
}

.similarity-progress-fill {
  width: 0%;
  height: 100%;
  background: var(--glow);
  transition: width 160ms ease;
}

.similarity-results {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding-bottom: 10px;
}

.similarity-results::-webkit-scrollbar {
  width: 6px;
}

.similarity-results::-webkit-scrollbar-track {
  background: transparent;
}

.similarity-results::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.12);
  border-radius: 2px;
}

.similarity-result-row {
  grid-template-columns: 42px 24px minmax(0, 1fr) 28px;
  gap: 9px;
  height: 58px;
  padding: 0 61px 0 var(--sim-panel-pad);
  border-bottom: 1px solid rgba(255,255,255,0.045);
}

.similarity-result-row .sample-col--pack {
  grid-column: 1;
}

.similarity-result-row .sample-col--type {
  grid-column: 2;
  justify-self: center;
}

.similarity-result-row .sample-col--name {
  grid-column: 3;
  width: 100%;
}

.similarity-result-row .similarity-result-score {
  grid-column: 4;
  justify-self: end;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(var(--glow-rgb), 0.64);
}

.similarity-result-row .pack-avatar {
  width: 34px;
  height: 34px;
  border-radius: 3px;
}

.similarity-result-row .sample-name {
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
}

.similarity-result-row .sample-tags-row {
  height: 17px;
}

.similarity-result-row .sample-tag {
  font-size: calc(11px * var(--ui-font-scale, 1));
  padding: 1px 4px;
}

.similarity-result-row .sample-favorite {
  right: 12px;
}

.similarity-result-row .sample-similar-btn {
  right: 36px;
}

.similarity-empty {
  padding: 34px 22px;
  text-align: center;
}

.similarity-empty-title {
  font-size: calc(13px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.48);
}

.similarity-empty-sub {
  margin-top: 7px;
  font-size: calc(12px * var(--ui-font-scale, 1));
  line-height: 1.45;
  color: rgba(255,255,255,0.26);
}

.tree-view-loading-bar-wrap {
  width: 100%;
  max-width: 260px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.tree-view-loading-track {
  width: 100%;
  height: 4px;
  background: rgba(255,255,255,0.10);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}

.tree-view-loading-fill {
  height: 100%;
  width: 0%;
  background: var(--glow);
  border-radius: 2px;
  transition: width 120ms ease;
}

.tree-view-loading-track.is-indeterminate {
  overflow: hidden;
}

.tree-view-loading-track.is-indeterminate .tree-view-loading-fill {
  width: 25% !important;
  transition: none;
  animation: analysis-sweep 1.4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  transform-origin: left center;
}

.tree-view-loading-pct {
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: rgba(255,255,255,0.28);
  text-align: right;
}
/* ══════════════════════════════════════════════════════════════════
   GLOBAL CUSTOM SCROLLBAR — thin, no track background, sidebar color
   ══════════════════════════════════════════════════════════════════ */

/* LA BARRE GLOBALE EST CELLE DU THEME, ET ELLE EST LA SEULE.
 *
 * Il y en avait une ici aussi, qui peignait la pastille en
 * var(--sidebar-background) — rgb(5,5,7), c'est-a-dire la couleur des surfaces
 * qu'elle recouvre. Elle ne gagnait plus rien : theme.css pose la meme regle
 * plus tard, a specificite egale, et l'emporte. Mais elle a servi de modele :
 * les menus deroulants et le panneau des reglages l'ont recopiee avec une
 * specificite de classe, qui elle GAGNE, et leurs barres etaient invisibles.
 *
 * Une seule ecriture, dans le theme, ou vivent les couleurs. */

/* Elements that must stay hidden (no scrollbar) */
.sample-tags-scroll::-webkit-scrollbar,
.fnp-tags-scroll::-webkit-scrollbar,
.sidebar-folder-label::-webkit-scrollbar,
.sidebar-item-btn[data-collection-id] > span:not(.sidebar-analyzing-spinner)::-webkit-scrollbar,
.sidebar-item-btn[data-favorite-id] > span:not(.sidebar-analyzing-spinner)::-webkit-scrollbar {
  display: none;
  width: 0;
}
/* ══════════════════════════════════════════════════════════════════
   COMPACT MODE — columns hidden progressively as window shrinks.
   Level 1 (data-compact="1"): < 1100×740 → hide waveform
   Level 2 (data-compact="2"): < 900×580  → hide waveform + rating
   ══════════════════════════════════════════════════════════════════ */

/* ── Level 1 : hide waveform ──────────────────────────────────── */
#app[data-compact="1"] .sample-col--waveform,
#app[data-compact="1"] .sample-list-header .sample-col--waveform,
#app[data-compact="2"] .sample-col--waveform,
#app[data-compact="2"] .sample-list-header .sample-col--waveform {
  display: none;
}

#app[data-compact="1"] .sample-waveform,
#app[data-compact="2"] .sample-waveform {
  display: none;
}

#app[data-compact="1"] .sample-list-header,
#app[data-compact="1"] .sample-row {
  grid-template-columns:
    var(--sl-pad-left)
    var(--sl-col-pack)
    4px
    var(--sl-col-type)
    8px
    1fr
    var(--sl-col-gap)
    var(--sl-col-length)
    var(--sl-col-gap)
    var(--sl-col-tempo)
    var(--sl-col-gap)
    var(--sl-col-key)
    var(--sl-col-gap-key-scale)
    var(--sl-col-scale)
    var(--sl-pad-right);
}

/* Re-assign grid columns for cols after waveform (shifted -2) */
#app[data-compact="1"] .sample-col--length    { grid-column: 8;  }
#app[data-compact="1"] .sample-col--tempo     { grid-column: 10; }
#app[data-compact="1"] .sample-col--key       { grid-column: 12; }
#app[data-compact="1"] .sample-col--scale     { grid-column: 14; }
#app[data-compact="1"] .sample-col--rating    { display: none; }

/* ── Level 2 : hide waveform + rating ────────────────────────── */
#app[data-compact="2"] .sample-col--rating,
#app[data-compact="2"] .sample-list-header .sample-col--rating {
  display: none;
}

#app[data-compact="2"] .sample-list-header,
#app[data-compact="2"] .sample-row {
  grid-template-columns:
    var(--sl-pad-left)
    var(--sl-col-pack)
    4px
    var(--sl-col-type)
    8px
    1fr
    var(--sl-col-gap)
    var(--sl-col-length)
    var(--sl-col-gap)
    var(--sl-col-tempo)
    var(--sl-col-gap)
    var(--sl-col-key)
    var(--sl-col-gap-key-scale)
    var(--sl-col-scale)
    var(--sl-pad-right);
}

/* Re-assign grid columns (no waveform, no rating) */
#app[data-compact="2"] .sample-col--length    { grid-column: 8;  }
#app[data-compact="2"] .sample-col--tempo     { grid-column: 10; }
#app[data-compact="2"] .sample-col--key       { grid-column: 12; }
#app[data-compact="2"] .sample-col--scale     { grid-column: 14; }

#app[data-compact="1"] .similarity-result-row,
#app[data-compact="2"] .similarity-result-row {
  grid-template-columns: 42px 24px minmax(0, 1fr) 28px;
}

#app[data-compact="1"] .similarity-result-row .sample-col--pack,
#app[data-compact="2"] .similarity-result-row .sample-col--pack {
  grid-column: 1;
}

#app[data-compact="1"] .similarity-result-row .sample-col--type,
#app[data-compact="2"] .similarity-result-row .sample-col--type {
  grid-column: 2;
  display: flex;
}

#app[data-compact="1"] .similarity-result-row .sample-col--name,
#app[data-compact="2"] .similarity-result-row .sample-col--name {
  grid-column: 3;
}

/* Unified modal chrome: match the sample right-click menu surface. */
.import-modal,
.ai-confirm-modal,
.settings-modal,
.feedback-modal,
.daw-bridge-modal,
.folder-relink-modal,
.auth-confirm-modal,
.app-update-modal {
  box-sizing: border-box;
  border: 1px solid rgba(255,255,255,0.075);
  border-radius: 4px;
}

/* ── Dropping something in from outside ────────────────────────────────────
   The window splits in two while a file is over it, and the half under the
   pointer lights up. Two things can be meant by dropping a sample on a sample
   library and they are opposites - keep this, or tell me what I already have
   that sounds like it - so the gesture is asked to say which.

   Only while something is actually being dragged. A permanent target sitting
   in the interface is empty and in the way every minute nobody is using it.

   The first version was one panel over a full-window scrim, which was both
   heavier than it needed to be and asked nothing. */
.dropzone {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: grid;
  grid-template-columns: 1fr 1fr;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.14s var(--ease);
}

/* By class and not by opacity alone: an invisible overlay across the whole
   window still swallows every click behind it. */
.dropzone.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* A folder can be kept and cannot be a question, so when one is dragged over
   there is only one half and it is the whole window. */
.dropzone.is-folder { grid-template-columns: 1fr; }
.dropzone.is-folder [data-drop-similar] { display: none; }

.dropzone-half {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 12px;
  padding: 40px;
  text-align: center;
  background: rgba(7, 7, 10, 0.82);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  transition: background 0.14s var(--ease);
}

/* The line between them, and the accent creeping in under the half being
   pointed at. No border, no ring, no card: the ground itself answers. */
.dropzone-half + .dropzone-half {
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.07);
}

.dropzone-half.is-over {
  background:
    radial-gradient(120% 90% at 50% 50%, rgba(var(--glow-rgb), 0.16) 0%, rgba(var(--glow-rgb), 0.05) 100%),
    rgba(7, 7, 10, 0.86);
}

.dropzone-mark {
  color: var(--dim);
  transition: color 0.14s var(--ease), transform 0.18s var(--ease);
}

.dropzone-mark svg {
  width: 26px;
  height: 26px;
  display: block;
}

.dropzone-half.is-over .dropzone-mark {
  color: var(--glow);
  transform: translateY(-2px);
}

.dropzone-title {
  margin: 0;
  font-size: calc(15px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-medium, 500);
  letter-spacing: 0.01em;
  color: var(--text);
}

/* The line that answers what is actually being wondered: what is this about to
   do to my library. */
.dropzone-note {
  margin: 0;
  max-width: 30ch;
  font-size: calc(12px * var(--ui-font-scale, 1));
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--dim);
}

/* Nothing usable came in. Said in the panel already on screen rather than in a
   dialogue nobody asked for. */
.dropzone.is-refused { grid-template-columns: 1fr; }
.dropzone.is-refused [data-drop-similar] { display: none; }
.dropzone.is-refused .dropzone-mark { color: var(--dim); }

/* The one checkbox in the remove dialogue, and it is only ever there for the
   folder Sonus fills itself. Borrowed wholesale from the analysis options so it
   is the same checkbox as everywhere else; all that is said here is where it
   sits. See the comment beside it in main.js for why no other place gets it. */
.delete-confirm-also {
  /* No margin of its own: .ai-confirm-body is a flex column with a 12px gap,
     so anything added here is added ON TOP of the spacing every other line in
     every other dialogue already gets. Between that and the centring it had,
     it read as a second section rather than a footnote on the name above it. */
  align-self: start;
}

.delete-confirm-also[hidden] { display: none; }

/* ════════════════════════════════════════════════════════════════
   The panels that stop at the footer stop at the TALLER footer

   Tree View and the similarity panel both end at
   bottom: var(--footer-height), which was the whole footer until the MIDI
   piano opened: the footer then grows upward by --midi-piano-height and
   neither panel moved, so the tree covered the keyboard completely and the
   similarity panel covered its right-hand end.

   Last in the file on purpose. Two earlier rules restate that same bottom -
   the tab-bar variant of .tree-view-panel and .similarity-panel itself - and
   each of them would win from further down. Written here, nothing can.

   Keyed off the footer's own class through :has(), so one condition governs
   both the footer's height and what has to make room for it.
   ════════════════════════════════════════════════════════════════ */
#app:has(.app-footer.is-midi-piano-visible) .tree-view-panel,
#app:has(.app-footer.is-midi-piano-visible) .similarity-panel {
  bottom: calc(var(--footer-height) + var(--midi-piano-height));
}

/* ══════════════════════════════════════════════════════════════════
   LE CHOIX DE L'ICONE D'UNE PLACE OU D'UNE COLLECTION
   ══════════════════════════════════════════════════════════════════

   Meme fond, meme bordure, meme ombre et meme ouverture que le menu
   contextuel d'ou il sort : c'est le meme geste qui continue, pas une
   deuxieme fenetre qui s'ajoute. Ce qui change est la forme — une grille,
   parce qu'un jeu entier se parcourt d'un coup d'oeil et ne se
   lisent pas ligne par ligne. */
.sidebar-icon-picker {
  position: fixed;
  z-index: 9001;
  width: 252px;
  background: rgba(5, 5, 7, 0.98);
  border: 1px solid rgba(255, 255, 255, 0.075);
  border-radius: 4px;
  padding: 7px;
  box-shadow: 0 14px 42px rgba(0, 0, 0, 0.48), 0 2px 8px rgba(0, 0, 0, 0.28);
  display: none;
  flex-direction: column;
  gap: 6px;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 120ms ease, transform 120ms ease;
  pointer-events: none;
  backdrop-filter: blur(18px);
}

.sidebar-icon-picker.is-open {
  display: flex;
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Reprendre son icone d'origine est aussi facile que d'en changer : c'est la
   premiere chose du panneau, pas une sortie cachee en bas. */
.sidebar-icon-default {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 7px;
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: rgba(255, 255, 255, 0.62);
  background: transparent;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  outline: none;
  transition: background 80ms ease, color 80ms ease;
}

.sidebar-icon-default img {
  width: 14px;
  height: 14px;
  filter: brightness(0) saturate(0%) invert(64%);
}

.sidebar-icon-default:hover {
  background: rgba(255, 255, 255, 0.055);
  color: var(--text);
}

.sidebar-icon-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  border-top: 1px solid rgba(255, 255, 255, 0.055);
  padding-top: 6px;
}

.sidebar-icon-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  background: transparent;
  border: none;
  border-radius: 3px;
  cursor: pointer;
  outline: none;
  transition: background 80ms ease;
}

.sidebar-icon-cell img {
  width: 17px;
  height: 17px;
  pointer-events: none;
  filter: brightness(0) saturate(0%) invert(64%);
  transition: filter 80ms ease;
}

.sidebar-icon-cell:hover {
  background: rgba(255, 255, 255, 0.075);
}

.sidebar-icon-cell:hover img {
  filter: brightness(0) saturate(0%) invert(92%);
}

/* Celle qui est portee en ce moment se voit sans avoir a la chercher : le
   fond de l'accent, et le dessin dans l'accent lui-meme. */
.sidebar-icon-cell.is-selected,
.sidebar-icon-default.is-selected {
  background: var(--glow-soft);
}

.sidebar-icon-cell.is-selected img,
.sidebar-icon-default.is-selected img {
  filter: var(--glow-filter);
}

/* ══════════════════════════════════════════════════════════════════
   LA FENETRE DU CHOIX D'ICONE
   ══════════════════════════════════════════════════════════════════

   Batie sur la meme coque que les autres fenetres de l'application —
   .modal-shell, fond partage, titre, boutons en bas — parce qu'un panneau
   flottant se lisait comme un menu de plus pose de travers.

   Un rail de familles a gauche, une famille a la fois a droite. Tout etait
   pose a plat auparavant, cinquante-trois dessins sous six titres empiles :
   une fenetre plus haute que large ou l'oeil ne se posait nulle part. Le rail
   lit comme la barre laterale de l'application, au pixel de clarte pres —
   c'est le meme geste, choisir a gauche ce qu'on regarde a droite. */
.icon-modal {
  /* L'ECART VIENT DU DESSIN, ET LA LARGEUR VIENT DE L'ECART.
     Un dessin de 23 dans une case de 54 laissait 37 de vide entre deux
     voisins, soit une fois et demie le dessin lui-meme : on ne voyait plus une
     grille mais des ilots. La case fait donc le dessin plus sa moitie, l'ecart
     n'est rien d'autre que ce qui reste autour, et la fenetre se deduit du rail
     plus les huit colonnes au lieu d'etre posee a cote. */
  --icon-mark: 23px;
  --icon-cell: calc(var(--icon-mark) * 1.57);
  --icon-columns: 8;
  --icon-rail: 118px;
  --icon-split: 18px;
  width: calc(var(--icon-rail) + var(--icon-columns) * var(--icon-cell)
              + 2 * var(--icon-split));
  max-width: calc(100vw - 48px);
  background: var(--raised);
  border: 1px solid rgba(255, 255, 255, 0.075);
  border-radius: 6px;
  /* Sans marge propre : la colonne du rail va d'un bord a l'autre, comme celle
     de Settings. Ce sont les zones qui portent leur marge, pas la fenetre. */
  padding: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55), 0 2px 10px rgba(0, 0, 0, 0.35);
}

/* NI TITRE, NI LIGNE DE TITRE.
 *
 * « Choose an icon » etait ecrit en haut d'une fenetre qui ne montre que des
 * icones, ouverte par une commande qui s'appelle Choose an icon. Il occupait
 * une ligne entiere pour redire son propre chemin.
 *
 * Au bout de cette ligne se tenait l'icone d'origine, avec le mot « original »
 * pose a sa gauche en petites capitales. Elle etait donc le seul dessin de la
 * fenetre a vivre hors du rail, au-dessus de la separation, sans famille — et
 * un dessin seul au bout d'un titre se lit comme une decoration du titre, pas
 * comme un choix. Elle a maintenant sa page, Default, en tete du rail : le
 * premier endroit ou l'on regarde, pour ce qui est le point de depart. */

/* La page Default : le dessin, et la phrase A COTE.
 *
 * Posee dessous, elle laissait une icone seule dans un coin et une ligne
 * orpheline sous elle, dans une page ou tout le reste est une grille. A cote,
 * c'est la forme que la page Custom emploie deja pour sa zone de depot — un
 * dessin a gauche, ce qu'il est a droite — et les deux pages singulieres de
 * cette fenetre se ressemblent au lieu de differer chacune a sa facon. */
.icon-modal-first {
  display: flex;
  align-items: center;
  gap: 4px;
}

.icon-modal-say {
  margin: 0;
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255, 255, 255, 0.34);
  line-height: 1.35;
}

.icon-modal-body {
  display: flex;
  flex: 1 1 auto;
}

/* Le rail EST la colonne de Settings.
 *
 * Premier essai : j'avais recopie .settings-nav-item depuis cette feuille-ci.
 * Sauf que theme.css la redeclare plus loin et gagne — la regle d'ici ne
 * s'affiche nulle part. J'ai donc copie une regle morte, et le rail ne
 * ressemblait a rien de ce que Sonus montre. Les chiffres ci-dessous sont
 * MESURES sur la fenetre Settings a l'ecran : douze, arrondi de sept, six sur
 * dix de marge, le gris a 0,44, et la selection en ACCENT — pas en blanc.
 * Le fond de colonne et son filet viennent du meme endroit. */
.icon-modal-rail {
  width: var(--icon-rail);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 16px 6px 12px;

  /* Sans fond a elle non plus : voir .settings-sidebar, d'ou vient la raison.
     Le filet est dessine de la meme facon. */
  position: relative;
}

.icon-modal-rail::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 1px;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent 0,
    var(--divider) 15%,
    var(--divider) 85%,
    transparent 100%);
}

.icon-modal-tab {
  font-family: inherit;
  font-size: calc(12px * var(--ui-font-scale, 1));
  font-weight: var(--ui-weight-regular, 400);
  color: var(--dim);
  background: transparent;
  border: none;
  border-radius: 7px;
  padding: 6px 10px;
  text-align: left;
  cursor: pointer;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
  outline: none;
}

.icon-modal-tab:hover {
  background: rgba(255, 255, 255, 0.055);
  color: var(--text);
}

.icon-modal-tab.is-selected,
.icon-modal-tab.is-selected:hover {
  background: rgba(255, 255, 255, 0.055);
  color: var(--glow);
}

/* Custom est a l'ecart : ce ne sont pas des dessins livres, ce sont les
   siennes. La marge dit la difference mieux qu'un trait — et elle pousse vers
   le bas quand le panneau laisse de la place. */
.icon-modal-tab--custom {
  margin-top: 16px;
}

.icon-modal-panel {
  flex: 1 1 auto;
  min-width: 0;
  padding: 16px var(--icon-split) 12px;
  /* Deux rangees : la plus grande famille en tient deux, donc la fenetre ne
     change jamais de hauteur en changeant de page. */
  min-height: calc(2 * var(--icon-cell));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.icon-modal-grid {
  display: grid;
  grid-template-columns: repeat(var(--icon-columns), var(--icon-cell));
  gap: 0;
  align-content: start;
}

/* Ni cadre au survol ni cadre sur le choix : une icone se lit comme une place
   dans la barre laterale, par sa clarte. Un carre gris derriere chacune faisait
   autant de boutons la ou il n'y a que des dessins. */
.icon-modal-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-cell);
  height: var(--icon-cell);
  background: transparent;
  border: none;
  cursor: pointer;
  outline: none;
}

.icon-modal-cell img {
  width: var(--icon-mark);
  height: var(--icon-mark);
  pointer-events: none;
  filter: brightness(0) saturate(0%) invert(75%);
  opacity: 0.7;
  transition: opacity 80ms ease, filter 80ms ease;
}

.icon-modal-cell:hover img {
  opacity: 0.9;
}

.icon-modal-cell.is-selected img {
  filter: var(--glow-filter);
  opacity: 1;
}

/* La case qui tourne, a l'endroit ou le dessin va apparaitre. Le meme anneau
   que partout ailleurs dans l'application : accent, et la meme rotation. */
.icon-modal-waiting {
  cursor: default;
}

.icon-modal-spin {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1.5px solid rgba(var(--glow-rgb), 0.22);
  border-top-color: var(--glow);
  animation: sidebar-spin 700ms linear infinite;
}

/* La zone d'import : un depot ET un bouton, parce qu'on ne sait pas lequel des
   deux la personne essaiera en premier. En haut de la page Custom, au-dessus de
   ce qu'on y a deja mis. */
.icon-modal-import {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 12px;
  border: 1px dashed rgba(255, 255, 255, 0.14);
  border-radius: 5px;
  cursor: pointer;
  transition: border-color 100ms ease, background 100ms ease;
}

.icon-modal-import:hover,
.icon-modal-import.is-over {
  border-color: var(--glow);
  background: var(--glow-soft);
}

.icon-modal-import img {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  filter: brightness(0) saturate(0%) invert(64%);
}

.icon-modal-import-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.icon-modal-import-label {
  font-size: calc(12px * var(--ui-font-scale, 1));
  color: var(--read);
}

.icon-modal-import-desc {
  font-size: calc(11px * var(--ui-font-scale, 1));
  color: rgba(255, 255, 255, 0.34);
  line-height: 1.35;
}

.icon-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 0 var(--icon-split) 14px;
}

/* --- la taille de tous les mots de la fenetre -------------------------------

   Un seul nombre. Les deux cent quarante-trois tailles de ces deux feuilles
   sont ecrites « calc(Npx * var(--ui-font-scale, 1)) », donc ce curseur les
   deplace toutes ensemble, en direct, sans qu'aucune ne soit reecrite.

   Demande par quelqu'un qui l'avait achete et n'arrivait pas a le lire : « the
   UI font is quite small and thin ».

   LES TROIS A SONT LE REGLAGE. Un pourcentage ne dit pas si on pourra lire un
   tag ; trois lettres aux tailles et aux graisses que cette fenetre emploie
   vraiment le disent tout de suite. Elles ne sont pas decoratives : c'est le 11
   en 400 des tags, le 13 en 400 d'un nom de sample et le 15 en 500 d'un titre,
   donc les deux graisses et toute l'echelle sont a l'ecran en meme temps.
   ------------------------------------------------------------------------- */

.ui-scale-control {
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
}

/* LES DEUX SELECTEURS, ET LE SECOND N'EST PAS UNE PRECAUTION.
 *
 * theme.css, qui charge apres celle-ci, contient :
 *
 *     input:focus, textarea:focus { background: none; }
 *
 * « input:focus » est plus fort qu'une classe seule. Des que cette barre prend
 * le focus — c'est-a-dire des le mousedown, donc au premier instant du glisser —
 * son fond etait efface : la piste disparaissait pendant qu'on tirait, restait
 * absente une fois le doigt leve puisque le focus restait, et revenait des qu'on
 * cliquait ailleurs. Trois symptomes qui se decrivent mal et qui sont une seule
 * ligne.
 *
 * Aucun de mes essais ne l'avait vu : ils posaient la valeur par script et
 * emettaient l'evenement a la main, ce qui ne donne le focus a rien. Le
 * mecanisme etait juste, l'interaction ne l'etait pas. */
.ui-scale-slider,
.ui-scale-slider:focus {
  appearance: none;
  -webkit-appearance: none;
  width: 96px;
  height: 4px;
  border-radius: 2px;

  /* DEUX COULEURS, ET LA PASTILLE N'EST PAS LE CURSEUR A ELLE SEULE.
   *
   * Elle l'a ete : une piste en blanc a neuf pour cent, d'un bout a l'autre, la
   * meme des deux cotes de la pastille. A cent pour cent la pastille est tout a
   * gauche et il reste quatre-vingt-seize pixels de piste, qu'on voit ; a cent
   * vingt-cinq elle est presque au bout, il n'en reste qu'une douzaine a droite,
   * et les neuf pour cent a gauche ne se lisent pas. Le controle devenait un
   * point pose dans le vide, et c'est ce qu'il a decrit : « quand je module le
   * slider on voit plus le slider ».
   *
   * Le chemin parcouru prend l'accent et le reste un gris qu'on voit, comme la
   * glissiere de volume dans le pied de fenetre. La position se lit alors sans
   * chercher la pastille, a n'importe quelle valeur.
   *
   * --ui-scale-fill est pose par main.js a chaque changement : une seule
   * proportion, ecrite la ou la valeur est deja connue. */
  background: linear-gradient(to right,
    var(--glow) 0 var(--ui-scale-fill, 0%),
    var(--faint) var(--ui-scale-fill, 0%) 100%);
  cursor: pointer;
  outline: none;
  flex: 0 0 auto;
}

/* La meme pastille que partout ailleurs : ronde, claire, et elle s'eclaire
   sous le doigt comme le reste des commandes de cette fenetre. */
.ui-scale-slider::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--text);
  border: 0;
  cursor: grab;
  transition: transform 0.12s var(--ease), background 0.12s var(--ease);
}

.ui-scale-slider:hover::-webkit-slider-thumb { transform: scale(1.12); }
.ui-scale-slider:active::-webkit-slider-thumb { cursor: grabbing; background: var(--glow); }

.ui-scale-slider:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--glow) 26%, transparent);
}

/* Alignees sur leur base, parce que trois lettres de trois tailles alignees par
   le milieu ont l'air de flotter. Sur la ligne, elles se lisent comme une
   echelle. */
.ui-scale-specimen {
  display: flex;
  align-items: baseline;
  gap: 7px;
  min-width: 46px;
  color: var(--dim);
}

/* Les valeurs de cette fenetre, et pas des nombres choisis pour la vignette.
   Elles portent le meme facteur que tout le reste, donc elles grandissent avec
   ce que le curseur vient de regler : c'est tout l'interet. */
.ui-scale-a { line-height: 1; transition: font-size 0.08s linear; }
.ui-scale-a--tag  { font-size: calc(11px * var(--ui-font-scale, 1)); font-weight: var(--ui-weight-regular, 400); }
.ui-scale-a--name { font-size: calc(13px * var(--ui-font-scale, 1)); font-weight: var(--ui-weight-regular, 400); }
.ui-scale-a--head { font-size: calc(15px * var(--ui-font-scale, 1)); font-weight: var(--ui-weight-medium, 500); color: var(--text); }

.ui-scale-value {
  min-width: 36px;
  text-align: right;
  font-size: calc(11px * var(--ui-font-scale, 1));
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}

/* --- assigner une touche ----------------------------------------------------

   L'etiquette devient un bouton. On presse, elle dit qu'elle ecoute, on appuie
   sur la touche voulue, elle se remplace. Meme forme que les autres valeurs de
   ce panneau — c'est la meme chose qu'un nombre qu'on regle, avec un clavier
   pour cadran.
   ------------------------------------------------------------------------- */

.settings-shortcuts { display: flex; flex-direction: column; }

.shortcut-badge {
  font-family: inherit;
  cursor: pointer;
  border: 1px solid transparent;
  transition: border-color 0.14s var(--ease), color 0.14s var(--ease),
              background 0.14s var(--ease);
}

.shortcut-badge:hover { border-color: var(--hairline); color: var(--text); }

/* Elle ecoute : l'accent, et une pulsation lente pour dire que la fenetre
   attend quelque chose de vous plutot que l'inverse. */
.shortcut-badge.is-listening {
  color: var(--glow);
  border-color: rgba(var(--glow-rgb), 0.45);
  background: rgba(var(--glow-rgb), 0.08);
  animation: shortcut-waiting 1.4s ease-in-out infinite;
}

@keyframes shortcut-waiting {
  0%, 100% { border-color: rgba(var(--glow-rgb), 0.45); }
  50%      { border-color: rgba(var(--glow-rgb), 0.9); }
}

.shortcut-badge:focus-visible {
  outline: 1px solid rgba(var(--glow-rgb), 0.7);
  outline-offset: 2px;
}

/* Le refus, ecrit sous la touche qu'on vient de presser plutot qu'ailleurs :
   c'est la qu'on regarde au moment ou on se demande pourquoi elle n'a pas ete
   prise. */
.settings-row-desc.is-refused { color: var(--warning); }

@media (prefers-reduced-motion: reduce) {
  .shortcut-badge.is-listening { animation: none; border-color: rgba(var(--glow-rgb), 0.9); }
}
