diff --git a/app/navigator_routes.py b/app/navigator_routes.py
index be2c8b0..c070d57 100644
--- a/app/navigator_routes.py
+++ b/app/navigator_routes.py
@@ -67,8 +67,9 @@ async def rack_detail(rack_id: int, ident=Depends(require_token), db=Depends(get
crates = [dict(r) for r in (await db.execute(text("""
SELECT c.id, c.name, c.label_text, c.rack_level_index::int AS level, c.slot_number::int AS slot,
c.direction, c.pos_x::float AS x, c.pos_z::float AS z, c.crate_purpose,
+ ct.name AS crate_type, ct.material_color AS color,
(SELECT count(*) FROM inventory i WHERE i.crate_id = c.id AND i.in_stock) AS items
- FROM virtual_crate c
+ FROM virtual_crate c LEFT JOIN virtual_crate_type ct ON ct.id = c.crate_type_id
WHERE c.rack_id = :id AND c.visible = 'y'
ORDER BY c.rack_level_index NULLS FIRST, c.slot_number NULLS LAST
"""), {"id": rack_id})).mappings()]
diff --git a/site/search.html b/site/search.html
index 5614674..bbae63e 100644
--- a/site/search.html
+++ b/site/search.html
@@ -145,6 +145,10 @@ const money=n=>n==null?'—':'$'+Number(n).toFixed(2);
const esc=s=>(s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
const imgTag=it=> it.release_id ? `
` : (it.thumb?`
`:'');
function chip(label,ondel,num){ return `${esc(label)} ✕`; }
+// crate-type colour (material_color from virtual_crate_type): blue crate #0000FF, wooden #BA906A, white tub #FFFFFF…
+function hexRgb(h){ h=String(h||'').replace('#',''); if(h.length===3)h=h.split('').map(c=>c+c).join(''); const n=parseInt(h,16); return isNaN(n)?[30,144,255]:[(n>>16)&255,(n>>8)&255,n&255]; }
+function rgba(h,a){ const [r,g,b]=hexRgb(h); return `rgba(${r},${g},${b},${a})`; }
+function darken(h,f){ const [r,g,b]=hexRgb(h); return `rgb(${Math.round(r*f)},${Math.round(g*f)},${Math.round(b*f)})`; }
// facing convention (RECORDGOD_NAVIGATOR_PLAN §3)
const CELL=0.34;
@@ -262,7 +266,9 @@ function drawRack(){
const {lv,label}=levelMeta();
$('#lvlCtl').style.display = lv>1?'inline-flex':'none';
$('#lvlLabel').textContent=label; $('#lvlPrev').disabled=ST.level<=1; $('#lvlNext').disabled=ST.level>=lv;
- $('#legend').innerHTML='crate↑↓←→ = facing · green=items'+(ST.tab==='reorganize'?' · click in order to pick':ST.tab==='collections'&&ST.edit?' · click to add to collection':'')+'';
+ const types=[...new Map((ST.crates||[]).map(c=>[c.crate_type||'crate', c.color||'#1e90ff'])).entries()];
+ $('#legend').innerHTML=types.map(([n,c])=>`${esc(n)}`).join('')
+ +'↑↓←→ = facing'+(ST.tab==='reorganize'?'click in order to pick':ST.tab==='collections'&&ST.edit?'click to add to collection':'');
const cv=$('#cv'), g=cv.getContext('2d'), W=cv.width, H=cv.height;
g.clearRect(0,0,W,H); g.fillStyle='#fbfbfd'; g.fillRect(0,0,W,H);
ST.hit=[];
@@ -286,16 +292,16 @@ function drawRack(){
if(row>=rows) return;
const x=oX+col*cellW+3, y=oY+row*cellH+3, w=cellW-6, h=cellH-6;
const sel=ST.selCrate===c.id;
- const picked=ST.tab==='reorganize' && ST.pick.indexOf(c.id);
const inColl=ST.tab==='collections' && ST.edit && ST.edit.crate_ids.includes(c.id);
- g.fillStyle = inColl?'rgba(70,180,90,.28)' : sel?'rgba(255,46,147,.30)' : 'rgba(30,144,255,.16)';
- g.strokeStyle = inColl?'#2e8b57' : sel?'#d10f7a' : '#1e90ff'; g.lineWidth=sel?3:1.5;
+ const tcol=c.color||'#1e90ff'; // crate-type material colour
+ g.fillStyle = inColl?'rgba(70,180,90,.28)' : sel?'rgba(255,46,147,.30)' : rgba(tcol,0.34);
+ g.strokeStyle = inColl?'#2e8b57' : sel?'#d10f7a' : darken(tcol,0.55); g.lineWidth=sel?3:1.5;
g.beginPath(); g.rect(x,y,w,h); g.fill(); g.stroke();
g.fillStyle='#1b1b22'; g.font='bold 10px system-ui'; g.textAlign='center'; g.textBaseline='top';
g.fillText('#'+c.id, x+w/2, y+4);
const gl=(c.label_text||c.crate_purpose||'').toString().split(',')[0].slice(0,12);
g.fillStyle='#555'; g.font='9px system-ui'; g.fillText(gl||'—', x+w/2, y+16);
- g.fillStyle=sel?'#d10f7a':'#1e90ff'; g.font='bold 20px system-ui'; g.textBaseline='middle';
+ g.fillStyle=sel?'#d10f7a':darken(tcol,0.55); g.font='bold 20px system-ui'; g.textBaseline='middle';
g.fillText(ARROW[(c.direction||'').toLowerCase()]||'•', x+w/2, y+h-13);
g.fillStyle='#1a8f54'; g.font='9px system-ui'; g.textAlign='right'; g.textBaseline='top';
g.fillText(slot, x+w-3, y+3);