diff --git a/xtrn/maze/game.js b/xtrn/maze/game.js
index ebbc4de39d8aab2fa3dbad3f44674b64665b8535..8c13454555b854597da678c0066da8b1a7dc126d 100644
--- a/xtrn/maze/game.js
+++ b/xtrn/maze/game.js
@@ -26,12 +26,11 @@ load(root + "menu.js");
 var settings=client.read(game_id,"settings",1);
 var status={WAITING:-1,STARTING:0,RACING:1,FINISHED:2};
 var frame=new Frame(1,1,80,24);
-var layout=new Layout(frame);
 var data=new GameData();
 
 /* game splash screens */
 function splashStart() {
-	console.ctrlkey_passthru="+ACGKLOPQRTUVWXYZ";
+	console.ctrlkey_passthru="+ACGKLOPQRTVWXYZ";
 	bbs.sys_status|=SS_MOFF;
 	bbs.sys_status|=SS_PAUSEOFF;
 	console.clear();
@@ -78,22 +77,14 @@ function lobby() {
 	
 	/* display objects */
 	var playerlist=new Frame(59,5,20,18,BG_BLACK,frame);
-	var input=new InputLine(3,console.screen_rows-1,54,150);
+	var input=new InputLine();
 	var chat=new JSONChat(user.number,undefined,serverAddr,serverPort);
+	var layout=new Layout(frame);
 	var chat_window=layout.addView("chat",2,4,56,18)
-	var chat_tab;
-		
-	/* main menu */
-	var menu=new Menu([
-		"~Join race",
-		"~Leave race",
-		"~Ready",
-		"S~ettings",
-		"~Scores",
-		"~Chat",
-		"~Help",
-		"~Quit"
-	],3,23,54,"\1c\1h","\1n");	
+	var menu=new Menu(["~Join race","~Leave race","~Ready",
+		"S~ettings","~Scores","~Chat","~Help","~Quit"],
+		new Frame(3,23,54,1,BG_BLACK + LIGHTGRAY,frame),
+		"\1c\1h","\1n")	;	
 
 	/* metadata */
 	var profile=undefined;
@@ -104,23 +95,36 @@ function lobby() {
 		frame.open();
 		frame.load(root + "lobby.bin",80,24);
 		client.subscribe(game_id,"games");
-		chat.join("#mazerace");
 		chat_window.show_title=false;
 		chat_window.show_border=false;
-		chat_tab=chat_window.addTab("#mazerace");
-		data.who();
+		chat.view = chat_window;
+		chat.join("#mazerace");
+		profile=data.profiles[user.alias];
+		input.init(3,23,54,1,frame);
 		menu.disable("L");
 		menu.disable("R");
-		profile=data.profiles[user.alias];
+		layout.open();
+		menu.frame.top();
+		data.who();
 	}
 	function main()	{
 		while(!js.terminated) {
 			cycle();
-			var k=input.inkey(hotkeys);
-			if(!k) 
+			var k = input.getkey(hotkeys);
+			if(k == undefined) 
 				continue;
 			if(hotkeys) {
 				k = k.toUpperCase();
+				switch(k) {
+				case KEY_LEFT:
+				case KEY_RIGHT:
+				case KEY_UP:
+				case KEY_DOWN:
+				case KEY_HOME:
+				case KEY_END:
+					layout.getcmd(k);
+					break;
+				}
 				if(!menu.items[k] || !menu.items[k].enabled)
 					continue;
 				switch(k.toUpperCase()) {
@@ -141,21 +145,26 @@ function lobby() {
 					leaveMaze();
 					break;
 				case "E":
-					chooseAvatar();
-					chooseColor();
+					todo();
+					//chooseAvatar();
+					//chooseColor();
 					break;
 				case "Q":
 					return;
 				case "C":
-					input.draw();
+					input.frame.top();
 					hotkeys=false;
 					break;
 				}
 			}
 			else {
-				chat.submit(chat_tab.title.toUpperCase(),k);
-				menu.draw();
-				hotkeys=true;
+				if(k.length > 0) {
+					layout.getcmd(k);
+				}
+				else {
+					menu.frame.top();
+					hotkeys=true;
+				}
 			}
 		}
 	}
@@ -165,10 +174,10 @@ function lobby() {
 	}
 	function cycle() {
 		client.cycle();
+		layout.cycle();
+		chat.cycle();
 		while(client.updates.length > 0)
 			processUpdate(client.updates.shift());
-		chat.cycle();
-		updateChatTab();
 		if(frame.cycle())
 			console.gotoxy(1,24);
 		if(full_redraw)
@@ -177,28 +186,10 @@ function lobby() {
 			listPlayers();
 			data.updated = false;
 		}
-		if(hotkeys && menu.updated) {
-			menu.draw();
-			menu.updated = false;
-		}
 		if(readyToRace()) {
 			data.mazes[gnum] = client.read(game_id,"mazes." + gnum,1);
 			race(gnum);
 			leaveMaze();
-			full_redraw = true;
-		}
-	}
-	function updateChatTab() {
-		var channel = chat.channels[chat_tab.title.toUpperCase()];
-		while(channel && channel.messages.length > 0) {
-			var msg = channel.messages.shift();
-			var str = "";
-			if(msg.nick)
-				var str = getColor(chat.settings.NICK_COLOR) + msg.nick.name + "\1n: " + 
-				getColor(chat.settings.TEXT_COLOR) + msg.str;
-			else
-				var str = getColor(chat.settings.NOTICE_COLOR) + msg.str;
-			chat_tab.getcmd(str + "\r\n");
 		}
 	}
 	function getGameNumber() {
@@ -247,7 +238,6 @@ function lobby() {
 		}
 	}
 	function chooseAvatar() {
-		todo();
 		// var aFrame = new Frame(25,10,30,4,BG_BLUE,frame);
 		// aFrame.open();
 		// aFrame.crlf();
@@ -259,7 +249,6 @@ function lobby() {
 		// aFrame.delete();
 	}
 	function chooseColor() {
-		todo();
 		//var cf = new Frame(30,10,10,2,BG_BLACK);
 	}
 	function todo() {
diff --git a/xtrn/maze/menu.js b/xtrn/maze/menu.js
index 246d23754e1f869edfddba0c0e379dd9bce1daf7..87a209c11ab3fa0d0a962cc1e9b05ee7587bdaa8 100644
--- a/xtrn/maze/menu.js
+++ b/xtrn/maze/menu.js
@@ -1,53 +1,40 @@
-function Menu(items,x,y,w,hl,txt)		
-{								
+function Menu(items,frame,hk_color,text_color) {								
 	this.items=[];
-	this.x=x;
-	this.y=y;
-	this.width=w;
-	this.hl=hl;
-	this.txt=txt;
-	this.updated=true;
+	this.frame=frame;
+	this.hk_color=hk_color;
+	this.text_color=text_color;
 	
 	this.disable=function(item)
 	{
 		this.items[item].enabled=false;
-		this.updated=true;
+		this.draw();
 	}
 	this.enable=function(item)
 	{
 		this.items[item].enabled=true;
-		this.updated=true;
+		this.draw();
 	}
 	this.add=function(items)
 	{
 		for(i=0;i<items.length;i++) {
 			hotkey=get_hotkey(items[i]);
-			this.items[hotkey.toUpperCase()]=new Item(items[i],hotkey,hl,txt);
+			this.items[hotkey.toUpperCase()]=new Item(items[i],hotkey,hk_color,text_color);
 		}
 	}
-	this.clear=function()
-	{
-		console.gotoxy(this);
-		console.write(format("%*s",this.width,""));
-	}
 	this.draw=function()
 	{
 		var str="";
-		for each(var i in this.items) {
+		for each(var i in this.items)
 			if(i.enabled==true) str+=i.text + " ";
-		}
-		var offset = str.length - console.strlen(strip_ctrl(str));
-		console.gotoxy(this);
-		console.attributes=ANSI_NORMAL;
-		console.putmsg(format("%-*s",this.width + offset,str));
-		this.updated=false;
+		this.frame.clear();
+		this.frame.putmsg(str);
 	}
 	
-	function Item(item,hotkey,hl,txt)
+	function Item(item,hotkey,hk_color,text_color)
 	{								
 		this.enabled=true;
 		this.hotkey=hotkey;
-		this.text=item.replace(("~" + hotkey) , hl + hotkey + txt);
+		this.text=item.replace(("~" + hotkey) , hk_color + hotkey + text_color);
 	}
 	function get_hotkey(item)
 	{