Index: mods/starlit-eco/plants.lua
==================================================================
--- mods/starlit-eco/plants.lua
+++ mods/starlit-eco/plants.lua
@@ -12,14 +12,67 @@
 	return {
 		max_items = a.max_items + b.max_items;
 		items = lib.tbl.append(a.items, b.items);
 	}
 end
+
+local function simpleDrop(rarity, what)
+	return {
+		max_items = 1;
+		items = {
+			{rarity = rarity, items = {what}};
+		};
+	};
+end
+
+local function biomeGrass(biomes, seed)
+	return function(def)
+		world.ecology.plants.link(def.id, {
+			stages = {{
+				tex = def.tex;
+				drop = simpleDrop(4, 'starlit_eco:fiber');
+			}};
+			decoration = {
+				deco_type = 'simple';
+				place_on = 'starlit:greengraze';
+				biomes = biomes;
+				noise_params = {
+					offset = def.ofs;
+					spread = vector.new(10,10,10);
+					seed = seed;
+					octaves = 3;
+				};
+			}
+		})
+	end
+end
+
+do gg = biomeGrass({'starlit:steppe', 'starlit:forest'}, 0x5e8fa0)
+	gg {
+		id = 'starlit_eco:greengraze_low';
+		name = 'Low Greengraze';
+		tex = 'starlit-eco-plant-grass-sprig.png';
+		ofs = -.3;
+	}
+	gg {
+		id = 'starlit_eco:greengraze_high';
+		name = 'High Greengraze';
+		tex = 'starlit-eco-plant-grass-high.png';
+		ofs = -.5;
+	}
+	gg {
+		id = 'starlit_eco:greengraze_tall';
+		name = 'Tall Greengraze';
+		tex = 'starlit-eco-plant-grass-tall.png';
+		ofs = -.7;
+	}
+end
+
 local function stalkPlant(def)
 	local function stage(s, drops, swap)
 		return {
-			tex = lib.image(string.format('starlit-eco-plant-stalk%s.png',s)):shift(def.color);
+			tex = lib.image(string.format('starlit-eco-plant-stalk%s.png',s)):colorize(def.color);
 			drop = drops;
 			swap = swap;
 		}
 	end
 	local function plantMatter(opts)
@@ -66,18 +119,10 @@
 		decoration = def.decoration;
 		meshOpt = 3;
 	})
 end
 
-local function simpleDrop(rarity, what)
-	return {
-		max_items = 1;
-		items = {
-			{rarity = rarity, items = {what}};
-		};
-	};
-end
 
 function stalkPlantAuto(def)
 	local id = def.id
 	local id_berries = def.id .. '_berry'
 	local id_seed = def.id .. '_seed'
@@ -123,11 +168,11 @@
 		color = lib.color(.6, .8, .8);
 		drop = simpleDrop(2, 'starlit_eco:moondrop_petal');
 	};
 	berries = {
 		desc = "The fruits of the moondrop are not very nutritious, but their peculiar sweet-sour flavor profile makes them one of Farthest Shadow's great delicacies";
-		color = lib.color(1,0,.4);
+		color = lib.color(.5,0,.1);
 		mass = 1;
 		impact = starlit.type.impact {
 			nutrition = 10;
 			hydration = 0.05;
 			taste = 1*60;
@@ -145,11 +190,11 @@
 stalkPlantAuto {
 	id = 'starlit_eco:dustrose';
 	name = "Dust Rose";
 	fiber = simpleDrop(2, 'starlit_eco:fiber');
 	seed = {};
-	color = lib.color(.3, .1, .2);
+	color = lib.color(.9, .8, .3);
 	leaf = {
 		color = lib.color(.7, .4, .8);
 		drop = simpleDrop(2, 'starlit_eco:dustrose_petal');
 	};
 	decoration = {

Index: mods/starlit-eco/trees.lua
==================================================================
--- mods/starlit-eco/trees.lua
+++ mods/starlit-eco/trees.lua
@@ -59,21 +59,21 @@
 
 
 starlit.item.food.link('starlit_eco:lambent_pine_berry', {
 	name = 'Lambent Pine Berry';
 	desc = 'Though packed with human-compatible nutrients, these berries are almost painfully sour when eaten raw.';
-	tex = lib.image('starlit-eco-plant-berry-bunch.png'):shift{hue=180,sat=-30,lum=30}:render();
+	tex = lib.image('starlit-eco-plant-berry-bunch.png'):shift{hue=180,sat=-.3,lum=3}:render();
 	impact = starlit.type.impact {
 		nutrition = 150;
 		taste = -2 * 60;
 	};
 	mass = 2;
 })
 
 starlit.item.seed.link('starlit_eco:lambent_pine_seed', {
 	name = 'Lambent Pine Seed';
-	tex = lib.image('starlit-eco-plant-seeds.png'):shift{hue=150, sat=-50, lum=80}:render();
+	tex = lib.image('starlit-eco-plant-seeds.png'):shift{hue=150, sat=-.5, lum=.8}:render();
 	grow = {kind = 'tree', id = 'starlit_eco:lambent_pine'};
 })
 
 minetest.register_node('starlit_eco:lambent_pine_bulb', {
 	description = 'Lambent Pine Bulb';

Index: mods/starlit/user.lua
==================================================================
--- mods/starlit/user.lua
+++ mods/starlit/user.lua
@@ -245,20 +245,31 @@
 			m.destroy = function()
 				luser:hud_remove(m.meter)
 				luser:hud_remove(m.readout)
 			end
 			m.update = function()
-				local v,txt,color,txtcolor = def.measure(luser,def)
+				local v,txt,color,txtcolor,hl,hlcolor = def.measure(luser,def)
 				v = math.max(0, math.min(1, v))
 				local n = math.floor(v*16) + 1
-				local img = hudAdjustBacklight(lib.image('starlit-ui-meter.png'))
-					:colorize(color or def.color)
+				local function adjust(img)
+					return hudAdjustBacklight(lib.image(img)):shift(color or def.color)
+				end
+				local img = adjust 'starlit-ui-meter.png'
 				if def.flipX then
 					img = img:transform 'FX'
 				end
 				img = img:render()
 				img = img .. '^[verticalframe:17:' .. tostring(17 - n)
+				if hl then
+					hl = math.floor(hl*16) + 1
+					local hi = hudAdjustBacklight(lib.image 'starlit-ui-meter-hl.png')
+						:shift(hlcolor or def.color)
+						:render()
+					hi = hi .. '^[verticalframe:17:' .. tostring(17 - hl)
+					img = string.format('%s^(%s)', img, hi)
+				end
+				img = string.format('%s^(%s)', img, adjust 'starlit-ui-meter-readout.png':render())
 				luser:hud_change(m.meter, 'text', img)
 				if txt then
 					luser:hud_change(m.readout, 'text', txt)
 				end
 				if txtcolor then
@@ -387,19 +398,30 @@
 				align = {x=1, y=-1};
 				pos = {x=0, y=1};
 				ofs = {x=20, y=-20};
 				measure = function(user)
 					local warm = self:effectiveStat 'warmth'
-					local n, color if warm < 0 then
-						n = math.min(100, -warm)
-						color = lib.color(0.1,0.3,1):lerp(lib.color(0.7, 1, 1), math.min(1, n/50))
-					else
-						n = math.min(100,  warm)
-						color = lib.color(0.1,0.3,1):lerp(lib.color(1, 0, 0), math.min(1, n/50))
+					local exposure = starlit.world.climate.temp(self.entity:get_pos())
+
+					local function tempVals(warm, br)
+						local n if warm < 0 then
+							n = math.min(100, -warm)
+-- 							color = lib.color(0.1,0.3,1):lerp(lib.color(0.7, 1, 1), math.min(1, n/50))
+						else
+							n = math.min(100,  warm)
+-- 							color = lib.color(0.1,0.3,1):lerp(lib.color(1, 0, 0), math.min(1, n/50))
+						end
+						local hue = lib.math.gradient({
+							205, 264, 281, 360 + 17
+						}, (warm + 50) / 100) % 360
+						return {hue=hue, sat = 1, lum = br}, n
 					end
+
+					local color, n = tempVals(warm,0)
+					local hlcolor, hl = tempVals(exposure,.5)
 					local txt = string.format("%s°", math.floor(warm))
-					return (n/50), txt, color
+					return (n/50), txt, color, nil, (hl/50), hlcolor
 				end;
 			}
 			self.hud.elt.geiger = self:attachMeter {
 				name = 'geiger';
 				align = {x=-1, y=-1};
@@ -514,11 +536,10 @@
 			local sd = s:def()
 			local w = self:effectiveStat 'warmth'
 			local kappa = starlit.constant.heat.thermalConductivity
 			local insul = sd.temp.insulation
 			local dt = (kappa * (1-insul)) * (t - w)
-			print('dt', dt, dump(sd.temp))
 			if (dt > 0 and          dt  > sd.temp.maxCool)
 			or (dt < 0 and math.abs(dt) > sd.temp.maxHeat) then return false end
 			return true
 		end;
 		-- will exposure to temperature t cause the player eventual harm

Index: mods/starlit/world.lua
==================================================================
--- mods/starlit/world.lua
+++ mods/starlit/world.lua
@@ -9,30 +9,23 @@
 		year = year, day = day;
 		season = day / world.planet.orbit + 0.5; -- begin summer
 	}
 end
 local lerp = lib.math.lerp
-
-local function gradient(grad, pos)
-	local n = #grad
-	if n == 1 then return grad[1] end
-	local op = pos*(n-1)
-	local idx = math.floor(op)
-	local t = op-idx
-	return lerp(t, grad[1 + idx], grad[2 + idx])
-end
+local gradient = lib.math.gradient
 
 local altitudeCooling = 10 / 100
-local heatRange = {min = -70, max = 70} -- translate mt temps into real temps
+local heatRange = {min = -50, max = 50} -- translate mt temps into real temps
 
 -- this function provides the basis for temperature calculation,
 -- which is performed by adding this value to the ambient temperature,
 -- determined by querying nearby group:heatSource items in accordance
 -- with the inverse-square law
 function world.climate.eval(pos, tod, season)
 	local data = minetest.get_biome_data(pos)
 	local biome = world.ecology.biomes.db[minetest.get_biome_name(data.biome)]
+-- 	print('climate:', dump(data))
 	local heat, humid = data.heat, data.humidity
 	heat = lerp(heat/100, heatRange.min, heatRange.max)
 	tod = tod or minetest.get_timeofday()
 	heat = lerp(math.abs(tod - 0.5)*2, heat, heat + biome.nightTempDelta)
 -- 	print('base heat', heat)

Index: mods/vtlib/image.lua
==================================================================
--- mods/vtlib/image.lua
+++ mods/vtlib/image.lua
@@ -84,11 +84,11 @@
 				color = color:to_hsl()
 			end
 			return image.change(self, {
 				fx = lib.tbl.append(self.fx, {
 					string.format('hsl:%s:%s:%s',
-						color.hue, color.sat, color.lum)
+						color.hue, color.sat*100, color.lum*100)
 				})
 			})
 		end;
 
 		rehue = function(self, hue)

Index: mods/vtlib/math.lua
==================================================================
--- mods/vtlib/math.lua
+++ mods/vtlib/math.lua
@@ -71,10 +71,18 @@
 
 	return string.format("%s%s", val, unit)
 end
 
 function fn.lerp(t, a, b) return (1-t)*a + t*b end
+function fn.gradient(grad, pos)
+	local n = #grad
+	if n == 1 then return grad[1] end
+	local op = pos*(n-1)
+	local idx = math.floor(op)
+	local t = op-idx
+	return fn.lerp(t, grad[1 + idx], grad[2 + idx])
+end
 
 function fn.trim(fl, prec)
 	local fac = 10^prec
 	return math.floor(fl * fac) / fac
 end