diff --git a/SVSim.EmulatedEntrypoint/Controllers/ArenaColosseumController.cs b/SVSim.EmulatedEntrypoint/Controllers/ArenaColosseumController.cs
new file mode 100644
index 0000000..1c7029d
--- /dev/null
+++ b/SVSim.EmulatedEntrypoint/Controllers/ArenaColosseumController.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Mvc;
+using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaColosseum;
+using SVSim.EmulatedEntrypoint.Models.Dtos.Responses.ArenaColosseum;
+
+namespace SVSim.EmulatedEntrypoint.Controllers;
+
+///
+/// Stub controller for the Colosseum arena family. Currently only emits a "no Colosseum
+/// period" /get_fee_info response so the home/arena screen doesn't 404. The full Colosseum
+/// flow (top, entry, register_deck, event_info, retire, finish, class_choose, card_choose,
+/// matchmaking) is deferred — see Wizard/ColosseumEntryInfoTask.cs for the parser surface.
+///
+[Route("arena_colosseum")]
+public class ArenaColosseumController : SVSimController
+{
+ [HttpPost("get_fee_info")]
+ public IActionResult GetFeeInfo([FromBody] GetFeeInfoRequest req)
+ {
+ if (!TryGetViewerId(out _)) return Unauthorized();
+ return Ok(new GetFeeInfoResponseDto());
+ }
+}
diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaColosseum/GetFeeInfoRequest.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaColosseum/GetFeeInfoRequest.cs
new file mode 100644
index 0000000..4e7bb3c
--- /dev/null
+++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Requests/ArenaColosseum/GetFeeInfoRequest.cs
@@ -0,0 +1,6 @@
+using MessagePack;
+
+namespace SVSim.EmulatedEntrypoint.Models.Dtos.Requests.ArenaColosseum;
+
+[MessagePackObject]
+public class GetFeeInfoRequest { }
diff --git a/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaColosseum/GetFeeInfoResponseDto.cs b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaColosseum/GetFeeInfoResponseDto.cs
new file mode 100644
index 0000000..f697163
--- /dev/null
+++ b/SVSim.EmulatedEntrypoint/Models/Dtos/Responses/ArenaColosseum/GetFeeInfoResponseDto.cs
@@ -0,0 +1,39 @@
+using System.Text.Json.Serialization;
+using MessagePack;
+
+namespace SVSim.EmulatedEntrypoint.Models.Dtos.Responses.ArenaColosseum;
+
+///
+/// Minimum-viable stub for /arena_colosseum/get_fee_info — emits is_colosseum_period:false
+/// so the client (Wizard/ColosseumEntryInfoTask.cs:99) skips the rest of the parse and the
+/// home/arena screen renders without 404ing. TODO: implement the full Colosseum entry flow
+/// when the Colosseum format is brought online.
+///
+[MessagePackObject]
+public class GetFeeInfoResponseDto
+{
+ ///
+ /// Per-viewer Colosseum entry status (rest_entry_num, now_round_id, is_last_day, etc.).
+ /// Empty object — client (ColosseumEntryInfoTask.cs:146) guards with `if (status.Count != 0)`,
+ /// so an empty dict short-circuits cleanly.
+ ///
+ [JsonPropertyName("colosseum_status")] [Key("colosseum_status")]
+ public ColosseumStatusDto ColosseumStatus { get; set; } = new();
+
+ [JsonPropertyName("colosseum_info")] [Key("colosseum_info")]
+ public ColosseumInfoDto ColosseumInfo { get; set; } = new();
+}
+
+[MessagePackObject]
+public class ColosseumStatusDto { }
+
+[MessagePackObject]
+public class ColosseumInfoDto
+{
+ ///
+ /// false = no Colosseum event running. Client (ColosseumEntryInfoTask.cs:100) gates every
+ /// other field on this — emitting false is what lets us ship an otherwise-empty info block.
+ ///
+ [JsonPropertyName("is_colosseum_period")] [Key("is_colosseum_period")]
+ public bool IsColosseumPeriod { get; set; } = false;
+}
diff --git a/SVSim.UnitTests/RoutingSmokeTests.cs b/SVSim.UnitTests/RoutingSmokeTests.cs
index adcfc80..36792ed 100644
--- a/SVSim.UnitTests/RoutingSmokeTests.cs
+++ b/SVSim.UnitTests/RoutingSmokeTests.cs
@@ -108,6 +108,7 @@ public class RoutingSmokeTests
[TestCase("/arena_two_pick/finish")]
[TestCase("/arena_two_pick_battle/do_matching")]
[TestCase("/arena_two_pick_battle/finish")]
+ [TestCase("/arena_colosseum/get_fee_info")]
public async Task Authenticated_route_resolves(string path)
{
using var factory = new TestFactory();