diff --git a/.trivyignore b/.trivyignore
index 03fb2355..17b6d25b 100644
--- a/.trivyignore
+++ b/.trivyignore
@@ -16,3 +16,22 @@ CVE-2025-32434
# executes. Trivy's check does not account for the gosu privilege-drop
# pattern.
DS-0002
+
+# CVE-2026-63374: anyio's TLSStream encodes host names with IDNA 2003, which
+# can be made to disagree with IDNA 2008 and spoof a TLS certificate. Fixed in
+# anyio 4.14.2; the lock pins 4.13.0.
+#
+# StemDeck never opens a TLSStream. anyio is here transitively: starlette uses
+# it for task groups and thread offloading, not for sockets, and httpx (which
+# does use it for TLS) is a dev-only dependency, present for FastAPI's
+# TestClient. The server itself speaks plain HTTP on localhost, and the two
+# things that do fetch over TLS, yt-dlp and audio-separator, bring their own
+# HTTP stacks.
+#
+# Not fixed in place because the fix is not a one-line bump: regenerating the
+# lock for it also rewrites platform markers across a dozen packages, including
+# the Intel macOS paths that pyproject gates by hand, and any uv.lock change
+# sends every existing desktop install to a full download instead of an in-app
+# update. Take the bump the next time the lock moves for another reason, and
+# drop this entry then.
+CVE-2026-63374
diff --git a/static/css/daw.css b/static/css/daw.css
index 938979bb..efb50bd5 100644
--- a/static/css/daw.css
+++ b/static/css/daw.css
@@ -255,13 +255,6 @@ input, textarea { font-family: inherit; }
background: color-mix(in srgb, var(--color) 14%, transparent);
color: var(--color);
}
-.stem-dot {
- width: 5px; height: 5px;
- border-radius: 50%;
- background: currentColor;
- opacity: 0.85;
-}
-.stem-choice[aria-pressed="false"] .stem-dot { opacity: 0.4; }
/* Automatic song-structure detection. It sits at the end of the composer next
to Split stems and matches its footprint, so the pair reads as two segments
of one bar rather than a chip loose beside a button. Colour still separates
@@ -392,12 +385,17 @@ input, textarea { font-family: inherit; }
.daw-iconbtn:active { transform: scale(0.96); }
/* Notification panel */
+/* The wrapper is still the badge's anchor; the panel is not anchored to it.
+ .sidebar is overflow:hidden and narrows to the rail's 66px when the library
+ is collapsed, so an absolutely positioned panel would be clipped to nothing
+ in that state. Fixed escapes the clip, and placeNotifPanel in ui-chrome.js
+ sets left/top from the bell's rect, the same way the search dropdown and the
+ metronome popover are placed. No coordinates here on purpose: the only code
+ that shows this panel is the code that positions it. */
.daw-notif-wrap { position: relative; }
.daw-notif-panel {
display: none;
- position: absolute;
- top: calc(100% + 8px);
- right: 0;
+ position: fixed;
width: 280px;
background: var(--panel-2);
border: 1px solid var(--border-strong);
@@ -772,9 +770,14 @@ input, textarea { font-family: inherit; }
.cat-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.cat-title { color: var(--fg); font-size: 12px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cat-sub { color: var(--muted); font-size: 10.5px; display: flex; gap: 5px; white-space: nowrap; overflow: hidden; }
-.cat-status { width: 6px; height: 6px; border-radius: 50%; background: #5fbc56; flex-shrink: 0; }
-.cat-status.processing { background: var(--accent); animation: cat-pulse 1.4s infinite; }
-.cat-status.unavailable { background: #666; }
+/* Shown only when it has something to say (#636). A finished import is the
+ resting state of every row in the library, so a green dot on all of them
+ marked nothing out: it was one more thing to read past on the way to the
+ title. The states that still carry information keep the dot, which is also
+ what makes them visible now that they are the only rows wearing one. */
+.cat-status { display: none; width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
+.cat-status.processing { display: block; background: var(--accent); animation: cat-pulse 1.4s infinite; }
+.cat-status.unavailable { display: block; background: #666; }
.cat-item.unavailable { cursor: pointer; }
.cat-item.unavailable .cat-title { opacity: 0.55; }
.cat-unavailable-warning { color: var(--accent); font-weight: 600; opacity: 1; }
@@ -791,6 +794,9 @@ input, textarea { font-family: inherit; }
.cat-item.queue-waiting .cat-thumb { opacity: 0.55; }
.cat-item.queue-waiting .cat-title { color: var(--muted); }
.cat-item.queue-waiting .cat-status {
+ /* Explicit, not inherited from .processing: a waiting row is only styled
+ through its parent, so it must turn the dot on for itself. */
+ display: block;
background: transparent;
box-shadow: inset 0 0 0 1.5px var(--muted);
animation: none;
diff --git a/static/index.html b/static/index.html
index f432726d..2ea9c7a5 100644
--- a/static/index.html
+++ b/static/index.html
@@ -81,7 +81,7 @@