diff --git a/source/main.cpp b/source/main.cpp
index b60178ea1365b1c269df5f2ccbebcb122d2fe6ab..5fd7853d2d483e8e6f23bcd7ac996460548eb0b7 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -1,9 +1,7 @@
 #include <atomic>
 #include <cstdio>
-#include <sstream>
 #include <iomanip>
 #include <cerrno>
-#include <iostream>
 
 #include "simple.hpp"
 #include "plain_button.h"
@@ -13,23 +11,27 @@
 #include "utils.hpp"
 #include "digit_display.h"
 #include "timer.h"
+#include "simple/support/debug.hpp"
 
 const std::chrono::steady_clock::duration max_duration = 99h + 59min + 59s;
 const std::chrono::steady_clock::duration min_duration{};
 
+struct nothing {};
+using movement = motion::movement<std::chrono::steady_clock::duration, nothing, nothing>;
+
 int main(int argc, const char** argv) try
 {
 	using support::ston;
 
-	const auto main_color = argc > 5
-		? rgb_pixel::from(ston<rgb_pixel::int_type>(argv[5]))
+	const auto main_color = argc > 2
+		? rgb_pixel::from(ston<rgb_pixel::int_type>(argv[2]))
 		: 0x009dff_rgb; // or 0x3ba3cd_rgb;
 
-	const auto second_color = argc > 6
-		? rgb_pixel::from(ston<rgb_pixel::int_type>(argv[6]))
+	const auto second_color = argc > 3
+		? rgb_pixel::from(ston<rgb_pixel::int_type>(argv[3]))
 		: 0x000000_rgb;
 
-	const std::chrono::milliseconds frametime (argc > 7 ? ston<unsigned>(argv[7]) : 33);
+	const std::chrono::milliseconds frametime (argc > 4 ? ston<unsigned>(argv[4]) : 33);
 
 	if(main_color == second_color) // would be cool to have a smarter contrast check
 	{
@@ -38,9 +40,6 @@ int main(int argc, const char** argv) try
 		return -1;
 	}
 
-	initializer init;
-	ui_factory ui;
-
 	std::string icon_string =
 		"---------"  // "---------"
 		"+++++++++"  // "-+++++++-"
@@ -52,15 +51,14 @@ int main(int argc, const char** argv) try
 		"--+-+++++"  // "----+----"
 		"---------"  // "---------"
 	;
+
+	initializer init;
+
 	surface icon_small(reinterpret_cast<surface::byte*>(icon_string.data()), {9,9},
 		pixel_format(pixel_format::type::index8));
 	icon_small.format().palette()->set_color('+', main_color);
 	icon_small.format().palette()->set_color('-', 0x0_rgba);
 
-	auto music = argc > 1
-		?  argv[1][0] != '\0' ? std::optional<musical::wav>(argv[1]) : std::nullopt
-		: std::optional<musical::wav>("./truwo.wav");
-
 	graphical::software_window win("truwo", {400,400}, graphical::window::flags::resizable);
 	auto fg_color = win.surface().format().color(main_color);
 	auto bg_color = win.surface().format().color(second_color);
@@ -69,22 +67,12 @@ int main(int argc, const char** argv) try
 	blit(convert( icon_small, icon.format()), icon, rect{icon.size()});
 	win.icon(icon);
 
-	rect button_rect{win.size()/8 + int2{5,5}};
-	auto make_control_button = [&]() -> auto&
-	{
-		auto& button = ui.make<plain_button>(fg_color, button_rect);
-		return button;
-	};
-
-	int2 digit_size{40,100};
-	auto digit_spacing = int2::i(5);
-	auto make_time_display = [&]() -> auto&
-	{
-		auto& display = ui.make<digit_display<>>(digit_size, digit_spacing, fg_color);
-		return display;
-	};
+	auto music = argc > 1
+		?  argv[1][0] != '\0' ? std::optional<musical::wav>(argv[1]) : std::nullopt
+		: std::optional<musical::wav>("./truwo.wav");
 
 	std::atomic<bool> music_playing = false;
+	// std::atomic<bool> music_done = false;
 	auto player = [&music_playing, &music, i = music->buffer().begin()](auto& device, auto buffer) mutable
 	{
 		if(!music_playing)
@@ -106,62 +94,28 @@ int main(int argc, const char** argv) try
 	using music_device = musical::device_with_callback<decltype(player)>;
 	std::unique_ptr<music_device> device = nullptr;
 
-	auto current_timer = timer{0ms};
-	current_timer = timer(clamp(current_timer.duration(), min_duration, max_duration));
-
-	auto& hours_display = make_time_display();
-	auto& minutes_display = make_time_display();
-	auto& seconds_display = make_time_display();
-	rect separator_rect{{13,100}};
-	bounds_layout timer_layout(
-		{
-			&hours_display,
-			&ui.make<digit_bitmap>(digit[10], fg_color, separator_rect),
-			&minutes_display,
-			&ui.make<digit_bitmap>(digit[10], fg_color, separator_rect),
-			&seconds_display
-		},
-		int2::i(5)
-	);
-	timer_layout.update();
-
-	hours_display.on_input.push_back([&](auto&&, int old_value, int new_value)
-	{
-		using namespace std::chrono;
-		auto offset = hours(new_value) - hours(old_value);
-		current_timer = timer(current_timer.remaining_duration() + offset);
-	});
-	minutes_display.on_input.push_back([&](auto&&, int old_value, int new_value)
-	{
-		using namespace std::chrono;
-		auto new_minutes = minutes(new_value);
-		if(new_minutes >= hours(1))
-			new_minutes = hours(1) - minutes(1);
-		auto offset = new_minutes - minutes(old_value);
-		current_timer = timer(current_timer.remaining_duration() + offset);
-	});
-	seconds_display.on_input.push_back([&](auto&&, int old_value, int new_value)
-	{
-		using namespace std::chrono;
-		auto new_seconds = seconds(new_value);
-		if(new_seconds >= minutes(1))
-			new_seconds = minutes(1) - seconds(1);
-		auto offset = new_seconds - seconds(old_value);
-		current_timer = timer(current_timer.remaining_duration() + offset);
-	});
+	std::vector<movement> timers;
+	timers.reserve(100); // TODO: hack to avoid invalidation
+	auto current_timer = motion::symphony{support::make_range(timers)};
+	bool paused = true;
 
+	ui_shop ui{ ui_factory<i_ui_object, i_graphic, i_interactive>{} };
 
+	rect button_rect{win.size()/8 + int2{5,5}};
+	auto make_control_button = [&]() -> auto&
+	{
+		auto& button = ui.make<plain_button>(fg_color, button_rect);
+		return button;
+	};
 	auto& stop_button = make_control_button();
 	auto& down_button = make_control_button();
+
 	focus_vector button_focus_group{
 		{&stop_button, &down_button} };
 
 	focus_vector main_focus_group
 	{{
-		&hours_display,
-		&minutes_display,
-		&seconds_display,
-		&button_focus_group
+		&button_focus_group,
 	}};
 
 	auto focus_handler = [&main_focus_group](auto& element)
@@ -171,34 +125,87 @@ int main(int argc, const char** argv) try
 
 	stop_button.on_press.push_back(focus_handler);
 	down_button.on_press.push_back(focus_handler);
-	hours_display.on_press.push_back(focus_handler);
-	minutes_display.on_press.push_back(focus_handler);
-	seconds_display.on_press.push_back(focus_handler);
+	bounds_layout button_layout ({&stop_button, &down_button}, int2::i(5));
+	button_layout.update();
 
+	bounds_layout main_layout({&button_layout}, int2::j(15));
+	main_layout.update();
+
+	main_layout += int2(10,10);
+
+	auto make_timer_ui = [&ui, &fg_color, &focus_handler]()
+	{
+		auto [hours_display, minutes_display, seconds_display] =
+			ui.make_order([&](auto& factory)
+		{
+			int2 digit_size{40,100};
+			auto digit_spacing = int2::i(5);
+			auto make_time_display = [&]() -> auto&
+			{
+				// oof, template -_-
+				auto& display = factory.template make<digit_display<>>(digit_size, digit_spacing, fg_color);
+				return display;
+			};
+			return std::tie
+			(
+				make_time_display(),
+				make_time_display(),
+				make_time_display()
+			);
+		}).goods;
+
+		hours_display.on_press.push_back(focus_handler);
+		minutes_display.on_press.push_back(focus_handler);
+		seconds_display.on_press.push_back(focus_handler);
+
+		rect separator_rect{{13,100}};
+		bounds_layout timer_layout(
+			{
+				&hours_display,
+				&ui.make<digit_bitmap>(digit[10], fg_color, separator_rect),
+				&minutes_display,
+				&ui.make<digit_bitmap>(digit[10], fg_color, separator_rect),
+				&seconds_display
+			},
+			int2::i(5)
+		);
+		timer_layout.update();
+
+		return std::tuple_cat(
+			std::tie(hours_display, minutes_display, seconds_display),
+			std::tuple{
+				focus_vector{ {&hours_display, &minutes_display, &seconds_display} },
+				std::move(timer_layout)
+			}
+		);
+	};
+	std::vector<decltype(make_timer_ui())> timer_displays;
+	timer_displays.reserve(timers.capacity()); // TODO: hack to avoid invalidation
 
 	auto reset_current_timer = [&]()
 	{
 		music_playing = false;
 		init.graphics.screensaver.release_one();
 		device = nullptr;
-		current_timer = timer(current_timer.duration());
+		current_timer.reset();
+		paused = true;
 	};
 
 	stop_button.on_click.push_back([&](auto&)
 	{
-		if(music_playing)
+		if(music_playing && current_timer.done())
 		{
 			reset_current_timer();
 		}
 		else
 		{
-			current_timer.pause();
+			paused = true;
 		}
 	});
 
 	down_button.on_click.push_back([&](auto&)
 	{
-		current_timer.resume();
+		paused = false;
 	});
 
 	timer stop_button_hold(1s);
@@ -212,28 +219,19 @@ int main(int argc, const char** argv) try
 		stop_button_hold.pause();
 	});
 
-	bounds_layout button_layout ({&stop_button, &down_button}, int2::j(5));
-	button_layout.update();
-	button_layout += int2::j() * center(button_layout, timer_layout);
-
-	bounds_layout main_layout({&timer_layout, &button_layout}, int2::i(15));
-	main_layout.update();
-
-	main_layout += center(main_layout, range2D{int2::zero(), win.size()});
-
 	bool done = false;
+	std::chrono::steady_clock::time_point current_time;
 	while(!done)
 	{
-		const auto current_time = std::chrono::steady_clock::now();
+		const auto new_time = std::chrono::steady_clock::now();
+		const auto time_delta = new_time - current_time;
+		current_time = new_time;
+
 		using namespace interactive;
 		while(auto event = next_event())
 		{
 			std::visit(support::overloaded{
 				[&done](quit_request) { done = true; },
-				[&win](window_resized)
-				{
-					win.update_surface();
-				},
 				[&win](window_size_changed)
 				{
 					win.update_surface();
@@ -242,7 +240,7 @@ int main(int argc, const char** argv) try
 				{
 					main_focus_group.drop_focus();
 				},
-				[&main_focus_group](const key_pressed& e)
+				[&main_focus_group, &make_timer_ui, &timer_displays, &timers, &main_layout, &current_timer](const key_pressed& e)
 				{
 					if(e.data.keycode == keycode::tab)
 					{
@@ -260,57 +258,148 @@ int main(int argc, const char** argv) try
 						}
 
 					}
+					else if(e.data.keycode == keycode::n)
+					{
+						auto& [h,m,s, focus, layout] = timer_displays.emplace_back(make_timer_ui());
+
+						auto last_total_duration = empty(timers) ? 0ms
+							: timers.back().total;
+						auto& timer = timers.emplace_back(movement{last_total_duration});
+						current_timer = motion::symphony{support::make_range(timers)};
+
+						main_focus_group.drop_focus();
+						auto elements = std::move(main_focus_group).extract();
+						elements.push_back(&focus);
+						main_focus_group = focus_vector(std::move(elements));
+
+						main_layout.elements.push_back(&layout);
+						main_layout.update();
+
+						h.on_input.push_back([&timer, &h](auto&&, int old_value, int new_value)
+						{
+							using namespace std::chrono;
+							auto offset = hours(new_value) - hours(old_value);
+							timer.reset();
+							timer.total = timer.total - timer.elapsed + offset;
+						});
+						m.on_input.push_back([&timer, &m](auto&&, int old_value, int new_value)
+						{
+							using namespace std::chrono;
+							auto new_minutes = minutes(new_value);
+							if(new_minutes >= hours(1))
+								new_minutes = hours(1) - minutes(1);
+							auto offset = new_minutes - minutes(old_value);
+							timer.reset();
+							timer.total = timer.total - timer.elapsed + offset;
+						});
+						s.on_input.push_back([&timer, &s](auto&&, int old_value, int new_value)
+						{
+							using namespace std::chrono;
+							auto new_seconds = seconds(new_value);
+							if(new_seconds >= minutes(1))
+								new_seconds = minutes(1) - seconds(1);
+							auto offset = new_seconds - seconds(old_value);
+							timer.reset();
+							timer.total = timer.total - timer.elapsed + offset;
+						});
+
+					}
 				},
 				[](auto) { }
 			}, *event);
 
-			for(auto&& interactive : ui.interactives())
+			for(auto&& interactive : ui.get<i_interactive>())
 				interactive->update(*event);
 		}
 
+		for
+		(
+			auto [timer, display] = std::tuple
+			{
+				timers.begin(),
+				timer_displays.begin()
+			};
+			timer < timers.end();
+			++timer, ++display
+		)
+		{
+			using std::get;
+			auto duration = timer->total - timer->elapsed;
+			get<0>(*display).set(extract_duration<std::chrono::hours>(duration).count());
+			get<1>(*display).set(extract_duration<std::chrono::minutes>(duration).count());
+			get<2>(*display).set(extract_duration<std::chrono::seconds>(duration).count());
+		}
+
 		fill(win.surface(), bg_color);
 
-		for(auto&& graphic : ui.graphics())
+		for(auto&& graphic : ui.get<i_graphic>())
 			graphic->draw(win.surface());
 		win.update();
 
-		down_button.enable(!music_playing && current_timer.paused());
-		hours_display.enable(!music_playing);
-		minutes_display.enable(!music_playing);
-		seconds_display.enable(!music_playing);
+		down_button.enable(!music_playing && paused);
+		for(auto&& timer_display : timer_displays)
+		{
+			using std::get;
+			get<0>(timer_display).enable(!music_playing && paused);
+			get<1>(timer_display).enable(!music_playing && paused);
+			get<2>(timer_display).enable(!music_playing && paused);
+		}
 
 		if(stop_button_hold.check())
 			reset_current_timer();
 
-		if(current_timer.check())
+		if(!music_playing)
 		{
-			if(!music_playing)
+			if(timers.size() != 0 && current_timer.done())
 			{
-				music_playing = true;
-				init.graphics.screensaver.keep_alive();
-				win.restore();
-				win.raise();
-				main_focus_group.focus_on(&stop_button);
-				if(music)
+				reset_current_timer();
+			}
+		}
+
+		if(!paused)
+		{
+			auto result = current_timer.advance(time_delta);
+			auto updated_displays = support::map_range(result.updated, timer_displays, timers.begin());
+
+			for
+			(
+				auto [timer, display] = std::tuple
+				{
+					result.updated.begin(),
+					updated_displays.begin()
+				};
+				timer < result.updated.end();
+				++timer, ++display
+			)
+			{
+				if(timer->done())
 				{
-					device = std::make_unique<music_device>(
-						musical::basic_device_parameters{music->obtained()},
-						player
-					);
-					device->play();
+					if(!music_playing)
+					{
+						music_playing = true;
+						main_focus_group.focus_on(&stop_button);
+						if(music)
+						{
+							device = std::make_unique<music_device>(
+								musical::basic_device_parameters{music->obtained()},
+								player
+							);
+							device->play();
+						}
+					}
 				}
 			}
-			current_timer.pause();
+			if(result.done)
+			{
+				paused = true;
+				// TODO:
+			}
 		}
 
-		auto duration = current_timer.remaining_duration();
-		hours_display.set(extract_duration<std::chrono::hours>(duration).count());
-		minutes_display.set(extract_duration<std::chrono::minutes>(duration).count());
-		seconds_display.set(extract_duration<std::chrono::seconds>(duration).count());
-
-		const auto next_frame_time = current_timer.paused()
-			? current_time + frametime
-			: min(current_timer.target_time_point(), current_time + frametime);
+		const auto next_frame_time = current_time + frametime;
+		// const auto next_frame_time = paused
+		// 	? current_time + frametime
+		// 	: min(current_timer.target_time_point(), current_time + frametime);
 		std::this_thread::sleep_until(next_frame_time);
 
 	}
diff --git a/source/ui_factory.hpp b/source/ui_factory.hpp
index 7cd6613761ccd1c9ccf921f79ed281bb104d0c8f..0b3a9dc28ef0cf0b07fe61d2692f3e1eb62a53ad 100644
--- a/source/ui_factory.hpp
+++ b/source/ui_factory.hpp
@@ -4,35 +4,197 @@
 #include <vector>
 #include <memory>
 
-#include "simple.hpp"
-#include "implementation.h"
+#include "simple/support/tuple_utils.hpp"
 
+//TODO; nothing UI here anymore... also not really a factory and not really a shop... rename all the things!
+
+template <typename... Interfaces>
+class ui_shop;
+
+template <typename Type, typename... Interfaces>
 class ui_factory
 {
-	std::vector<std::unique_ptr<i_ui_object>> elements;
-	std::vector<i_graphic*> _graphics;
-	std::vector<i_interactive*> _interactives;
-
 	public:
 
+	using type = Type;
+
 	template <typename Element, typename ...Args>
 	Element& make(Args&&... args)
 	{
+		using namespace simple::support;
+		auto& elements = tuple_car(interfaces);
 		auto element = std::make_unique<Element>(std::forward<Args>(args)...);
 		Element& raw = *element.get();
-		if constexpr (std::is_base_of_v<i_graphic, Element>)
-			_graphics.push_back(&raw);
-		if constexpr (std::is_base_of_v<i_interactive, Element>)
-			_interactives.push_back(&raw);
+
+		add_interface(raw, tuple_tie_cdr(interfaces));
 
 		elements.push_back(std::move(element)); // assuming raw pointer will not change... that's safe right?
 		return raw;
 	}
 
-	const std::vector<i_graphic*>& graphics() const noexcept
-	{ return _graphics; }
-	const std::vector<i_interactive*>& interactives() const noexcept
-	{ return _interactives; }
+	template <typename Interface>
+	const auto& get() const noexcept
+	{
+		using ptr_t = std::conditional_t<std::is_same_v<Interface, type>,
+			  std::unique_ptr<type>,
+			  Interface*>;
+		return std::get<std::vector<ptr_t>>(interfaces);
+	}
+
+	private:
+
+	template <typename... T>
+	using container = std::tuple< std::vector<T> ... >;
+	template <typename... T>
+	using container_ref = std::tuple< std::vector<T>& ... >;
+
+	container<
+		std::unique_ptr<type>,
+		Interfaces* ...
+	> interfaces;
+
+
+	template <typename El>
+	void add_interface(El&, std::tuple<>){}
+
+	template <typename El, typename In, typename... Rest>
+	void add_interface(El& element, container_ref<In*, Rest...> interfaces)
+	{
+		using namespace simple::support;
+		if constexpr (std::is_base_of_v<In, El>)
+			tuple_car(interfaces).push_back(&element);
+		add_interface(element,tuple_tie_cdr(interfaces));
+	}
+
+	friend class ui_shop<Type, Interfaces...>;
+};
+
+template <typename... Interfaces>
+class ui_shop
+{
+	public:
+
+	using receipt_id_t = unsigned;
+
+	ui_shop(ui_factory<Interfaces...> supplier) :
+		supplier(std::move(supplier))
+	{
+	}
+
+	template <typename Goods>
+	struct order
+	{
+		public:
+		Goods goods;
+		const receipt_id_t receipt_id;
+
+		private:
+		order(Goods&& goods, const receipt_id_t& receipt_id) :
+			goods(std::forward<Goods>(goods)),
+			receipt_id(receipt_id)
+		{}
+		friend class ui_shop;
+	};
+
+	template <typename Function>
+	order<std::invoke_result_t<
+		Function, ui_factory<Interfaces...>&>>
+	make_order(Function&& function)
+	{
+		using simple::support::transform;
+		auto size = transform([](const auto& x) {return x.size(); },
+			supplier.interfaces);
+
+		auto&& goods = std::invoke(
+			std::forward<Function>(function),
+			supplier);
+
+		receipt_ids.push_back(get_id());
+
+		transform([](auto& receipt_size, auto size, const auto& interface)
+		{
+			receipt_size.push_back(interface.size() - size);
+		}, receipt_sizes, size, supplier.interfaces);
+
+		return
+		{
+			std::forward<decltype(goods)>(goods),
+			receipt_ids.back()
+		};
+	};
+
+	template <typename Element, typename ...Args>
+	Element& make(Args&&... args)
+	{
+		return make_order([&](auto& factory) -> auto&
+		{
+			return
+				factory.template make<Element>
+					(std::forward<Args>(args)...);
+		}).goods;
+	}
+
+	bool recycle(receipt_id_t id)
+	{
+		auto found = lower_bound
+		(
+			begin(receipt_ids),
+			end(receipt_ids),
+			id
+		);
+
+		if(found != end(receipt_ids))
+		{
+			using simple::support::transform;
+			transform([&](auto& interfaces, auto& sizes)
+			{
+				auto size_begin = begin(receipt_sizes);
+				auto found_size = size_begin +
+					(found - begin(receipt_ids));
+
+				auto goods_begin = begin(interfaces) +
+					accumulate(size_begin, found_size, 0);
+				interfaces.erase
+				(
+					goods_begin,
+					goods_begin + *found_size
+				);
+
+
+				sizes.erase(found_size);
+			}, supplier.interfaces, receipt_sizes);
+
+			receipt_ids.erase(found);
+			return true;
+		}
+
+		return false;
+	}
+
+	template <typename Interface>
+	decltype(auto) get() const noexcept
+	{ return supplier.template get<Interface>(); }
+
+	private:
+
+	using receipt_size_t = unsigned short;
+
+	receipt_id_t get_id()
+	{
+		if(empty(receipt_ids))
+			return 0;
+		return receipt_ids.back() + 1; //TODO: what if we run out?
+		// but it'll take like two hundred bajillion years D':
+		// yes, what will this program do after two hundred bajillion years of runtime?
+		// reuse ids? do I smell std::rotate? =)
+	}
+
+	ui_factory<Interfaces...> supplier;
+	std::vector<receipt_id_t> receipt_ids;
+	std::array<
+		std::vector<receipt_size_t>,
+		sizeof...(Interfaces) + 1
+	> receipt_sizes;
 };