    /* the banner ahead of the page. A hero picked on this phone is answered
       from the override with no network at all, exactly as the Rust does —
       and the field really is snake_case there (no rename_all on it) */
    if (cmd === "catalog_artist_hero_seed") {
        const id = Number(args?.artistId);
        if (heroes[id]) return Promise.resolve({ banner: heroes[id], banner_custom: true });
        const a = id === CAT_ARTIST.id ? CAT_ARTIST : id === CAT_SLOW.id ? CAT_SLOW : RELATED.find((r) => r.id === id);
        if (!a) return Promise.resolve(null);
        return new Promise((r) => setTimeout(() => r({ banner: svgArt(210, a.name[0]), banner_custom: false }), 200));
    }
    /* the one-round content stage: the raw first page of releases, before the
       exhaustive pass classifies them. It goes through checkedArtistPayload,
       so `artist.id` must equal the argument or the wrapper throws */
    if (cmd === "catalog_artist_overview") {
        const id = Number(args?.artistId);
        const a = id === CAT_ARTIST.id ? CAT_ARTIST : id === CAT_SLOW.id ? CAT_SLOW : RELATED.find((r) => r.id === id);
        if (!a) return Promise.reject(new Error("catalog_artist_overview: unknown artist"));
        const albums = id === CAT_SLOW.id ? SLOW_ALBUMS.slice(0, 4) : id === CAT_ARTIST.id ? CAT_DISCO.slice(0, 4) : [];
        return new Promise((r) => setTimeout(() => r({ artist: a, popular_tracks: [], albums }), 300));
    }
