diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml
index dd99c340..5dfe682e 100644
--- a/.forgejo/workflows/build.yaml
+++ b/.forgejo/workflows/build.yaml
@@ -5,9 +5,7 @@ jobs:
       image: nixos/nix:latest
     steps:
       - run: nix-channel --list && nix-channel --update
-      - run: nix-shell -p git --command 'git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY .'
-      - run: nix-shell -p git --command 'cd rust-jack && git remote set-url origin https://codeberg.org/unspeaker/rust-jack'
-      - run: nix-shell -p git --command 'git submodule update --init --recursive'
+      - run: nix-shell -p git --command 'git clone --recursive $GITHUB_SERVER_URL/$GITHUB_REPOSITORY .'
       - run: whoami && pwd && ls -al
       - run: nix-shell --command 'cargo version -vv && cargo test && cargo build --release && cloc crates/tek/src' .forgejo/workflows/build.nix
       - run: "docker run --security-opt seccomp=unconfined -v $PWD:/volume xd009642/tarpaulin cargo tarpaulin --out Html --all-features"
diff --git a/.gitmodules b/.gitmodules
index c32def4c..3c5a123f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,4 @@
 [submodule "rust-jack"]
 	path = rust-jack
-	url = git@codeberg.org:unspeaker/rust-jack.git
+	url = https://codeberg.org/unspeaker/rust-jack
 	branch = timebase
diff --git a/README.md b/README.md
index ef493af3..76bdd870 100644
--- a/README.md
+++ b/README.md
@@ -32,9 +32,7 @@ the project roadmap is at https://codeberg.org/unspeaker/tek/milestones
 * **requirements:** linux; jack or pipewire; 24-bit terminal (i use `kitty`)
 * **recommended:** midi controller; samples in wav format; lv2 plugins.
 
-### installation
-
-#### arch linux
+### arch linux
 
 [tek](https://codeberg.org/unspeaker/tek) is available as a package in the AUR.
 you can install it using an AUR helper (e.g. `paru`):
diff --git a/engine/src/tui.rs b/engine/src/tui.rs
index 56abbe5d..5fae7615 100644
--- a/engine/src/tui.rs
+++ b/engine/src/tui.rs
@@ -19,6 +19,7 @@ pub(crate) use ratatui::{
     prelude::{Color, Style, Buffer},
     style::Modifier,
     backend::{Backend, CrosstermBackend, ClearType},
+    layout::{Size, Rect},
     buffer::Cell
 };
 
@@ -64,11 +65,11 @@ impl Tui {
     /// Construct a new TUI engine and wrap it for shared ownership.
     pub fn new () -> Usually<Arc<RwLock<Self>>> {
         let backend = CrosstermBackend::new(stdout());
-        let area    = backend.size()?;
+        let Size { width, height } = backend.size()?;
         Ok(Arc::new(RwLock::new(Self {
             exited:  Arc::new(AtomicBool::new(false)),
-            buffer:  Buffer::empty(area),
-            area:    [area.x, area.y, area.width, area.height],
+            buffer:  Buffer::empty(Rect { x: 0, y: 0, width, height }),
+            area:    [0, 0, width, height],
             backend,
         })))
     }
@@ -133,25 +134,23 @@ impl<T: Render<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tu
         let exited = self.read().unwrap().exited.clone();
         let engine = self.clone();
         let state  = state.clone();
-        let size   = engine.read().unwrap().backend.size().expect("get size failed");
-        let mut buffer = Buffer::empty(size);
+        let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed");
+        let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height });
         spawn(move || loop {
             if exited.fetch_and(true, Relaxed) {
                 break
             }
-            let size = engine.read().unwrap().backend.size()
+            let Size { width, height } = engine.read().unwrap().backend.size()
                 .expect("get size failed");
             if let Ok(state) = state.try_read() {
+                let size = Rect { x: 0, y: 0, width, height };
                 if buffer.area != size {
                     engine.write().unwrap().backend.clear_region(ClearType::All)
                         .expect("clear failed");
                     buffer.resize(size);
                     buffer.reset();
                 }
-                let mut output = TuiOutput {
-                    buffer,
-                    area: [size.x, size.y, size.width, size.height]
-                };
+                let mut output = TuiOutput { buffer, area: [0, 0, width, height] };
                 state.render(&mut output).expect("render failed");
                 buffer = engine.write().unwrap().flip(output.buffer, size);
             }
diff --git a/layout/src/transform.rs b/layout/src/transform.rs
index 731f2a16..cda8f91a 100644
--- a/layout/src/transform.rs
+++ b/layout/src/transform.rs
@@ -136,21 +136,12 @@ transform_xy_unit!(Fixed
     });
 
 transform_xy_unit!(Shrink
-    |self, to|Ok(
-        self.content().min_size(to)?.map(|to|match *self {
-            Self::X(w, _)     => [
-                if to.w() > w { to.w() - w } else { 0.into() },
-                to.h()
-            ],
-            Self::Y(h, _)     => [
-                to.w(),
-                if to.h() > h { to.h() - h } else { 0.into() }
-            ],
-            Self::XY(w, h, _) => [
-                if to.w() > w { to.w() - w } else { 0.into() },
-                if to.h() > h { to.h() - h } else { 0.into() }
-            ],
-        }.into())),
+    |self, to|Ok(self.content().min_size(to)?
+        .map(|wh|wh.wh())
+        .map(|[w, h]|[
+            w.minus(self.dx()).into(),
+            h.minus(self.dy()).into()
+        ].into())),
     |self, to|Ok(self.min_size(to.area().wh().into())?
         .map(|size|to.render_in(to.area().clip(size).into(), &self.content()))
         .transpose()?.unwrap_or(())));
diff --git a/src/transport.rs b/src/transport.rs
index 5ff9b647..f9314b02 100644
--- a/src/transport.rs
+++ b/src/transport.rs
@@ -10,21 +10,23 @@ pub struct TransportTui {
     pub size:   Measure<Tui>,
     pub cursor: (usize, usize),
     pub focus:  TransportFocus,
+    pub color:  ItemPalette,
 }
 from_jack!(|jack|TransportTui Self {
     jack:   jack.clone(),
     clock:  ClockModel::from(jack),
     size:   Measure::new(),
     cursor: (0, 0),
-    focus:  TransportFocus::PlayPause
+    focus:  TransportFocus::PlayPause,
+    color:  ItemPalette::random(),
 });
 has_clock!(|self: TransportTui|&self.clock);
 audio!(|self: TransportTui, client, scope|ClockAudio(self).process(client, scope));
 handle!(<Tui>|self: TransportTui, from|TransportCommand::execute_with_state(self, from));
-render!(<Tui>|self: TransportTui|row!([
-    Fixed::xy(5, 3, PlayPause(false)),
-    Fixed::y(3, TransportView::new(self, None, true))
-]));
+render!(<Tui>|self: TransportTui|Fixed::y(3, row!([
+    " ", Fixed::x(5, PlayPause(false)),
+    " ", Shrink::x(1, TransportView::new(self, Some(self.color), true)),
+])));
 impl std::fmt::Debug for TransportTui {
     fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
         f.debug_struct("TransportTui")
@@ -88,7 +90,6 @@ impl TransportView {
 render!(<Tui>|self: TransportView|{
     let color = self.color;
     Fixed::y(3, Tui::bg(color.base.rgb, Fill::x(row!([
-        //PlayPause(self.started), " ",
         col!([
             TransportField(" Beat", self.beat.as_str(), &color),
             TransportField(" Time", format!("{:.1}s", self.current_second).as_str(), &color),