diff --git a/Assets/Scripts/Components/Radar.cs b/Assets/Scripts/Components/Radar.cs
new file mode 100644
index 0000000..aae5037
--- /dev/null
+++ b/Assets/Scripts/Components/Radar.cs
@@ -0,0 +1,56 @@
+using UnityEngine;
+
+///
+/// Sits on the "Radar" overlay object (a disc rendered by the Radar Camera in front of the main camera).
+/// Spawns "Target" blips - clones of the child "Target Template" - each of which continuously points from
+/// the reference camera toward a world-space transform (visualising the bearing camera -> target) and then
+/// destroys itself after a given lifetime. See for the per-blip behaviour.
+///
+[DisallowMultipleComponent]
+public sealed class Radar : MonoBehaviour
+{
+ [Tooltip("Template each spawned Target is cloned from. Auto-resolves to the child \"Target Template\".")]
+ [SerializeField] private Transform targetTemplate;
+
+ [Tooltip("Camera the direction is measured FROM (camera -> target). Auto-resolves to the Camera this radar sits under.")]
+ [SerializeField] private Camera referenceCamera;
+
+ private void Awake()
+ {
+ if (!targetTemplate)
+ {
+ var t = transform.Find("Target Template");
+ if (t) targetTemplate = t;
+ }
+ if (!referenceCamera) referenceCamera = GetComponentInParent();
+ }
+
+ ///
+ /// Spawns a Target blip that each frame orients toward (from the reference
+ /// camera) and destroys itself after seconds. Returns the spawned blip, or
+ /// null if it could not be created.
+ ///
+ public RadarTarget Spawn(Transform target, float lifetime)
+ {
+ if (!target)
+ {
+ Debug.LogWarning("[Radar] Spawn called without a target transform.", this);
+ return null;
+ }
+ if (!targetTemplate)
+ {
+ Debug.LogWarning("[Radar] No Target Template assigned or found.", this);
+ return null;
+ }
+
+ Transform clone = Instantiate(targetTemplate, targetTemplate.parent);
+ clone.name = "Target";
+ clone.localPosition = targetTemplate.localPosition;
+ clone.localScale = targetTemplate.localScale;
+ clone.gameObject.SetActive(true);
+
+ var blip = clone.GetComponent();
+ blip.Init(target, referenceCamera, lifetime);
+ return blip;
+ }
+}
diff --git a/Assets/Scripts/Components/Radar.cs.meta b/Assets/Scripts/Components/Radar.cs.meta
new file mode 100644
index 0000000..83ec13a
--- /dev/null
+++ b/Assets/Scripts/Components/Radar.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: b8be89860f43b3240bb09cd9931ceaf2
\ No newline at end of file
diff --git a/Assets/Scripts/Components/RadarTarget.cs b/Assets/Scripts/Components/RadarTarget.cs
new file mode 100644
index 0000000..f519796
--- /dev/null
+++ b/Assets/Scripts/Components/RadarTarget.cs
@@ -0,0 +1,100 @@
+using UnityEngine;
+
+///
+/// A single radar blip spawned by . Each frame it orients itself so its forward (+Z, where
+/// the "Point" marker sits) faces the direction from the reference camera to a world-space target, visualising
+/// the bearing camera -> target. Tracks continuously (the camera turns / the target moves) and self-destructs
+/// once its lifetime elapses - or earlier if its target is destroyed. The point's base colour and size encode
+/// the two things the flat radar disc can't show via position alone: tints it by
+/// how far the target sits ahead of / behind the camera, and its scale shrinks with camera -> target distance.
+///
+[DisallowMultipleComponent]
+public sealed class RadarTarget : MonoBehaviour
+{
+ public Renderer pointRenderer;
+
+ [Tooltip("Point tint by bearing: sampled at 0 when the target is dead ahead of the camera, at 1 when directly behind. Flip the keys for the reverse.")]
+ [SerializeField] Gradient forwardGradient;
+ [Tooltip("Point transform scale at the near (x) and far (y) end of pointDistanceRange.")]
+ [SerializeField] Vector2 pointDistanceScale;
+ [Tooltip("camera -> target distance mapped onto pointDistanceScale: x = near distance, y = far distance.")]
+ [SerializeField] Vector2 pointDistanceRange;
+
+ private Transform _target;
+ private Camera _camera;
+ private float _lifetime;
+ private float _age;
+ private bool _initialised;
+
+ private MaterialPropertyBlock _mpb;
+ private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");
+
+ public Transform Target => _target;
+ public float RemainingLifetime => Mathf.Max(0f, _lifetime - _age);
+
+ public void Init(Transform target, Camera referenceCamera, float lifetime)
+ {
+ _target = target;
+ _camera = referenceCamera;
+ _lifetime = lifetime;
+ _age = 0f;
+ _initialised = true;
+ Aim();
+ }
+
+ // LateUpdate so the aim uses the camera's final position/rotation for this frame.
+ private void LateUpdate()
+ {
+ if (!_initialised) return;
+
+ _age += Time.deltaTime;
+ if (_age >= _lifetime)
+ {
+ Destroy(gameObject);
+ return;
+ }
+
+ // The world target may despawn (e.g. a caught hider) before this blip expires - drop it, don't dangle.
+ if (!_target)
+ {
+ Destroy(gameObject);
+ return;
+ }
+
+ Aim();
+ }
+
+ private void Aim()
+ {
+ if (!_target || !_camera) return;
+ Vector3 dir = _target.position - _camera.transform.position;
+ float sqr = dir.sqrMagnitude;
+ if (sqr < 1e-8f) return; // camera sitting on the target -> keep the last orientation/colour/scale
+
+ transform.rotation = Quaternion.LookRotation(dir);
+
+ float distance = Mathf.Sqrt(sqr);
+ ApplyDistanceScale(distance);
+ ApplyForwardColor(dir / distance);
+ }
+
+ // Shrinks the blip with distance: pointDistanceRange (near, far) remapped onto pointDistanceScale (near, far), clamped.
+ private void ApplyDistanceScale(float distance)
+ {
+ if (!pointRenderer) return;
+ float t = Mathf.InverseLerp(pointDistanceRange.x, pointDistanceRange.y, distance);
+ float s = Mathf.Lerp(pointDistanceScale.x, pointDistanceScale.y, t);
+ pointRenderer.transform.localScale = new Vector3(s, s, s);
+ }
+
+ // Tints the point's base colour by how far the target sits ahead of (0) vs directly behind (1) the camera.
+ private void ApplyForwardColor(Vector3 dirNormalized)
+ {
+ if (!pointRenderer || forwardGradient == null) return;
+ float t = Vector3.Angle(_camera.transform.forward, dirNormalized) / 180f;
+ _mpb ??= new MaterialPropertyBlock();
+ pointRenderer.GetPropertyBlock(_mpb);
+ _mpb.SetColor(BaseColorId, forwardGradient.Evaluate(t));
+ pointRenderer.SetPropertyBlock(_mpb);
+ }
+}
diff --git a/Assets/Scripts/Components/RadarTarget.cs.meta b/Assets/Scripts/Components/RadarTarget.cs.meta
new file mode 100644
index 0000000..bfdea7b
--- /dev/null
+++ b/Assets/Scripts/Components/RadarTarget.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: b4a573fc0fda31d42aec388198183b81
\ No newline at end of file