Looks like Categorical overrides ticks without being able to change them after, feels like a bug to me but I don’t like how Categorical works, anyway. It’s easier in your case to use a cgrad(...; categorical = true) which makes the colorbar render individual facets. Then you can set the color limits to -0.5/+0.5 at the ends and your ticks work.
The background you can do with a nested GridLayout and a Box, plus Outside alignmodes.
N = (200, 100)
x = LinRange(-2, 2, N[1])
y = LinRange(-1, 1, N[2])
field = zeros(N...)
field[ 1: 50,:] .= 1.0
field[ 50:100,:] .= 2.0
field[100:150,:] .= 3.0
field[150:200,:] .= 4.0
field[:,70:100] .= NaN
f = Figure()
ax = Axis(f[1, 1])
hm = heatmap!(ax, x, y, field, colormap = cgrad(:vik, 4; categorical = true, alpha = 0.8), colorrange=(0.5, 4.5))
grid = GridLayout(f[1, 1], tellwidth = false, tellheight = false, alignmode = Outside(10),
halign = :right, valign = :bottom)
Box(grid[1, 1], color = :gray80)
cbar = Colorbar(grid[1, 1], hm, label = L"$$Mechanisms", width = 320, height = 25,
labelsize = 20, ticklabelsize = 20, bbox=BBox(220, 100, 20, 150),
alignmode = Outside(20), halign = :left, ticklabelcolor = :black, labelcolor = :black, vertical=false,
tickcolor = :black, ticks = ([1,2,3,4], ["a","b","c","d"]))
f
