Index: render/html.lua
==================================================================
--- render/html.lua
+++ render/html.lua
@@ -622,11 +622,15 @@
 				catenate = function(...) return ... end;
 			};
 			toHTML = {
 				elt = elt;
 				tag = function(t,attrs,body)
-					return f('<%s>%s</%s>', html_open(t,attrs), body, t)
+					if t then
+						return f('<%s>%s</%s>', html_open(t,attrs), body, t)
+					else
+						return tostring(body)
+					end
 				end;
 				catenate = table.concat;
 			};
 		}
 	end
@@ -979,13 +983,13 @@
 				return t
 			elseif type(t) == 'table' then
 				if t[1] then
 					return catenate(ss.map(flatten, t))
 				elseif t.tag then
-					return tag(t.tag, t.attrs or {}, flatten(t.nodes))
+					return tag(t.tag, t.attrs or {}, flatten(t.nodes), t.src)
 				elseif t.elt then
-					return tag(t.elt, t.attrs or {})
+					return elt(t.elt, t.attrs or {}, t.src)
 				end
 			end
 		end
 
 		function block_renderers.embed(b,s)
@@ -1163,11 +1167,11 @@
 		end
 
 		function block_renderers.macro(b,s)
 			local all = renderSubdoc(b.doc)
 			local cat = catenate(ss.map(flatten,all))
-			return tag('div', {}, cat)
+			return tag(nil, {}, cat)
 		end
 
 		function block_renderers.quote(b,s)
 			local ir = renderSubdoc(b.doc)
 			return tag('blockquote', b.id and {id=getSafeID(b)} or {}, catenate(ss.map(flatten,ir)))
@@ -1341,10 +1345,24 @@
 		table.insert(ir, tagproc.toIR.tag('div',{id='cover'},''))
 	end
 
 	-- restructure passes
 	runhook('ir_restructure_pre', ir)
+
+	-- flay empty containers
+	for _, sec in pairs(ir) do
+		if sec.tag == 'section' then
+			local i = 1 while i <= #sec.nodes do local v = sec.nodes[i]
+				if type(v) ~= 'string' and v.nodes and v.tag == nil then
+					table.remove(sec.nodes,i)
+					for j=1,#v.nodes do
+						table.insert(sec.nodes, i+j - 1, v.nodes[j])
+					end
+				end
+			i=i+1 end
+		end
+	end
 
 	---- list insertion pass
 	local lists = {}
 	for _, sec in pairs(ir) do
 		if sec.tag == 'section' then