    /** The app's own icon, decoded ONCE and kept, as the card's large icon
     *  when there is no cover yet. `art` is cleared the moment `artKey`
     *  changes and only refilled when the frontend has finished decoding a
     *  cover into bytes — and a track with no cover never refills it at all —
     *  so every track change has a real window with a null large icon and the
     *  lock screen shows a grey hole. This is what stands in it.
     *
     *  Through the PackageManager rather than a resource id on purpose: this
     *  is a library module and its own R has no `ic_launcher`, so from in here
     *  the app's icon is only reachable this way.
     *
     *  Drawn at the tile's own bounds, which is the whole trick. On API 26+
     *  what comes back is an AdaptiveIconDrawable, and an adaptive icon's
     *  BOUNDS are the 72 units the launcher shows — not the 108 its layers
     *  are drawn in. The drawable scales its own layers up by 3/2 and masks
     *  them inside whatever box it is handed, which is why its intrinsic
     *  width is two thirds of the layer PNG. So setBounds(0, 0, 512, 512) IS
     *  the launcher's view; a box set outside the tile to "reveal the 18%
     *  bleed" does the opposite and crops into the safe zone. Every other
     *  kind of drawable simply fills the tile.
     *
     *  Decoded on the first ask and never again — `refresh()` runs on every
     *  position tick, and this must not. A failure caches nothing and answers
     *  null, which is the card exactly as it was before this existed. */
    fun appIcon(ctx: Context): Bitmap? {
        val cached = icon
        if (cached != null) return cached
        return try {
            val drawable = ctx.packageManager.getApplicationIcon(ctx.packageName)
            val bitmap = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888)
            drawable.setBounds(0, 0, 512, 512)
            drawable.draw(Canvas(bitmap))
            icon = bitmap
            bitmap
        } catch (e: Exception) {
            android.util.Log.w("SoulStream", "app icon could not be decoded: $e")
            null
        }
    }
