diff --git a/crates/ui-egui/src/edit_window.rs b/crates/ui-egui/src/edit_window.rs
--- a/crates/ui-egui/src/edit_window.rs
+++ b/crates/ui-egui/src/edit_window.rs
@@ -42,7 +42,7 @@ use layout::COLUMN_TITLES_H;
 #[cfg(test)]
 use layout::lane_rect;
 pub use layout::{COLUMN_W, EditLayout, HEADER_W, LANE_H, RULER_H, edit_columns, header_width, rect, sample_at, track_height, x_of};
-use overlay::overlay;
+use overlay::{overlay, playhead_colour};
 pub use rulers::ruler_height;
 use rulers::{column_titles, draw_rulers, views_button};
 pub use scroll::zoom_toggle;
diff --git a/crates/ui-egui/src/edit_window/overlay.rs b/crates/ui-egui/src/edit_window/overlay.rs
--- a/crates/ui-egui/src/edit_window/overlay.rs
+++ b/crates/ui-egui/src/edit_window/overlay.rs
@@ -4,6 +4,12 @@ use super::layout::x_of;
 use super::scroll::{FollowFrame, follow_update};
 use super::*;
 
+/// The playback cursor is red only while recording; plain playback draws it in its own neutral
+/// grey, apart from the insertion colour of the edit cursor that stands beside it.
+pub(super) fn playhead_colour(t: &Tokens, recording: bool) -> Color32 {
+    if recording { t.playhead } else { t.playback }
+}
+
 /// Selection overlay, insertion point and playhead across rulers and tracks.
 /// While playing, auto-scroll keeps the playhead in view for the follow
 /// modes (`page`/`continuous`/`center`) — but any outside view move (a manual
@@ -61,7 +67,7 @@ pub(super) fn overlay(app: &mut SoundApp, ui: &mut Ui, tl: Rect, area: Rect) {
     }
     if app.is_playing() {
         let x = x_of(s, tl, app.position());
-        painter.line_segment([pos2(x, area.min.y), pos2(x, area.max.y)], Stroke::new(1.5, t.playhead));
+        painter.line_segment([pos2(x, area.min.y), pos2(x, area.max.y)], Stroke::new(1.5, playhead_colour(&t, app.engine.transport.recording)));
         // Session reads first (owned copies), then the layout mutation: the
         // two borrow disjoint fields.
         let pos = app.position();
diff --git a/crates/ui-egui/src/edit_window/tests.rs b/crates/ui-egui/src/edit_window/tests.rs
--- a/crates/ui-egui/src/edit_window/tests.rs
+++ b/crates/ui-egui/src/edit_window/tests.rs
@@ -1482,3 +1482,65 @@ fn alt_click_on_a_breakpoint_deletes_it() {
         assert_eq!(h.state().engine.session().track(track).unwrap().clips(), clips.as_slice(), "{tool}");
     }
 }
+
+#[test]
+fn playhead_is_red_only_while_recording() {
+    for t in [Tokens::DARK, Tokens::LIGHT] {
+        assert_eq!(playhead_colour(&t, true), t.playhead);
+        assert_eq!(playhead_colour(&t, false), t.playback);
+        assert_ne!(t.playback, t.playhead, "plain playback must not read as the record line");
+    }
+}
+
+/// WCAG contrast ratio between two colours (1 = identical, 21 = black on white).
+fn contrast(a: Color32, b: Color32) -> f32 {
+    let lum = |c: Color32| {
+        let lin = |v: u8| {
+            let v = f32::from(v) / 255.0;
+            if v <= 0.04045 { v / 12.92 } else { ((v + 0.055) / 1.055).powf(2.4) }
+        };
+        0.2126 * lin(c.r()) + 0.7152 * lin(c.g()) + 0.0722 * lin(c.b())
+    };
+    let (hi, lo) = (lum(a).max(lum(b)), lum(a).min(lum(b)));
+    (hi + 0.05) / (lo + 0.05)
+}
+
+/// The edit cursor stands still beside the moving playback line while playing: in both themes
+/// they must be two clearly different greys, and the line must still read on the lanes.
+#[test]
+fn the_playback_line_is_set_apart_from_the_insertion_point_in_both_themes() {
+    for (name, t) in [("dark", Tokens::DARK), ("light", Tokens::LIGHT)] {
+        let apart = contrast(t.playback, t.insertion);
+        assert!(apart >= 2.5, "{name}: playback {:?} and insertion {:?} are only {apart:.2}:1 apart", t.playback, t.insertion);
+        let reads = contrast(t.playback, t.playlist_bg);
+        assert!(reads >= 3.0, "{name}: the playback line is only {reads:.2}:1 on the lanes");
+    }
+}
+
+/// The colour `overlay` gives the full-height playback line (width 1.5) in this transport state.
+fn playback_line_colour(recording: bool) -> Option<Color32> {
+    use egui::{Rect, pos2};
+    let _palette = crate::theme::lock_test_palette();
+    let mut app = SoundApp::new(soundcraft_engine::Engine::default(), None, crate::Services::default());
+    app.engine.session_mut().edit.zoom.samples_per_px = 1_000.0;
+    app.engine.transport.playing = true;
+    app.engine.transport.recording = recording;
+    app.engine.transport.position = 80_000;
+    let tl = Rect::from_min_max(pos2(100.0, 100.0), pos2(900.0, 500.0));
+    let area = Rect::from_min_max(pos2(100.0, 50.0), tl.max);
+    let ctx = egui::Context::default();
+    let mut output = ctx.run_ui(egui::RawInput::default(), |ui| overlay(&mut app, ui, tl, area));
+    output.textures_delta.clear();
+    output.shapes.iter().find_map(|s| match &s.shape {
+        Shape::LineSegment { points, stroke } if points[0].x == 180.0 && stroke.width == 1.5 => Some(stroke.color),
+        _ => None,
+    })
+}
+
+#[test]
+fn overlay_draws_the_playback_line_neutral_and_the_record_line_red() {
+    let _palette = crate::theme::lock_test_palette();
+    let t = Tokens::current();
+    assert_eq!(playback_line_colour(false), Some(t.playback));
+    assert_eq!(playback_line_colour(true), Some(t.playhead));
+}
diff --git a/crates/ui-egui/src/edit_window/universe.rs b/crates/ui-egui/src/edit_window/universe.rs
--- a/crates/ui-egui/src/edit_window/universe.rs
+++ b/crates/ui-egui/src/edit_window/universe.rs
@@ -82,7 +82,10 @@ pub(super) fn universe(app: &mut SoundApp, ui: &mut Ui) {
     ui.painter().rect_stroke(vr, 2.0, Stroke::new(1.5, Tokens::current().universe_view), StrokeKind::Outside);
     if app.is_playing() {
         let x = r.min.x + app.position() as f32 / total * r.width();
-        ui.painter().line_segment([pos2(x, r.min.y), pos2(x, r.max.y)], Stroke::new(1.0, Tokens::current().playhead));
+        ui.painter().line_segment(
+            [pos2(x, r.min.y), pos2(x, r.max.y)],
+            Stroke::new(1.0, playhead_colour(&Tokens::current(), app.engine.transport.recording)),
+        );
     }
     let resp = ui.interact(r, ui.scope_id().with("universe"), Sense::click_and_drag());
     if let Some(p) = resp.interact_pointer_pos()
diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs
--- a/crates/ui-egui/src/theme.rs
+++ b/crates/ui-egui/src/theme.rs
@@ -143,6 +143,9 @@ pub struct Tokens {
     pub selection: Color32,
     pub playhead: Color32,
     pub insertion: Color32,
+    /// The playback line while the transport runs without recording: a mid grey, set well apart
+    /// from `insertion` (the edit cursor stands beside it while playing) in both themes.
+    pub playback: Color32,
     pub solo: Color32,
     pub mute: Color32,
     pub rec: Color32,
@@ -253,6 +256,7 @@ impl Tokens {
         selection: Color32::from_rgba_premultiplied(40, 70, 110, 110),
         playhead: Color32::from_rgb(232, 60, 50),
         insertion: Color32::from_rgb(230, 230, 230),
+        playback: Color32::from_rgb(140, 140, 142),
         solo: Color32::from_rgb(222, 196, 52),
         mute: Color32::from_rgb(232, 148, 40),
         rec: Color32::from_rgb(214, 52, 46),
@@ -338,6 +342,7 @@ impl Tokens {
         selection: Color32::from_rgba_premultiplied(100, 145, 205, 90),
         playhead: Color32::from_rgb(210, 48, 42),
         insertion: Color32::from_rgb(35, 36, 40),
+        playback: Color32::from_rgb(110, 113, 119),
         solo: Color32::from_rgb(178, 145, 20),
         mute: Color32::from_rgb(194, 106, 22),
         rec: Color32::from_rgb(190, 42, 38),
