@@ -27,9 +27,9 @@ struct MenuView: View {
2727
2828 var body: some View {
2929 VStack(alignment: .leading, spacing: 18) {
30− hero
30+ hyper
3131 if showsClaude {
32− hairline
32+ serviceBreak
3333 claude
3434 }
3535 hairline
@@ -59,7 +59,35 @@ struct MenuView: View {
5959 Divider().opacity(0.3)
6060 }
6161
62− // MARK: - Hero
62+ /// The rule between the two services. Wider than the others on purpose: Hyper and
63+ /// Claude are two unrelated accounts, and the gap should say so before the headers do.
64+ private var serviceBreak: some View {
65+ hairline.padding(.vertical, 5)
66+ }
67+
68+ /// A section header: an icon and a title, sized to match the "API Key" header below
69+ /// so the popover reads as three sections rather than a hero with two appendices.
70+ /// The icon is what tells the two services apart at a glance.
71+ private func sectionHeader(_ title: String, systemImage: String) -> some View {
72+ HStack(spacing: 5) {
73+ Image(systemName: systemImage)
74+ .font(.system(size: 11, weight: .semibold))
75+
76+ Text(title)
77+ .font(Self.sectionFont)
78+ }
79+ }
80+
81+ // MARK: - Hyper
82+
83+ /// The Hyper section: its header, then the balance as the popover's hero.
84+ private var hyper: some View {
85+ VStack(alignment: .leading, spacing: 12) {
86+ sectionHeader("Hyper Credits", systemImage: "bolt.fill")
87+
88+ hero
89+ }
90+ }
6391
6492 private var hero: some View {
6593 VStack(spacing: 8) {
@@ -68,10 +96,6 @@ struct MenuView: View {
6896 .transition(.opacity)
6997 } else if viewModel.balance == nil {
7098 VStack(spacing: 6) {
71− Text("Hyper Credits")
72− .font(Self.sectionFont)
73− .foregroundColor(.secondary)
74−
7599 Text("⚡?")
76100 .font(Self.heroFont)
77101 .foregroundColor(.secondary)
@@ -83,9 +107,6 @@ struct MenuView: View {
83107 .transition(.opacity)
84108 } else {
85109 VStack(spacing: 6) {
86− Text("Hyper Credits")
87− .font(Self.sectionFont)
88− .foregroundColor(.secondary)
89110 balanceNumber
90111 caption
91112 sparkline
@@ -204,8 +225,7 @@ struct MenuView: View {
204225 private var claude: some View {
205226 VStack(alignment: .leading, spacing: 12) {
206227 HStack(spacing: 8) {
207− Text("Claude Code")
208− .font(Self.sectionFont)
228+ sectionHeader("Claude Code", systemImage: "sparkles")
209229
210230 Spacer(minLength: 0)
211231
@@ -228,7 +248,7 @@ struct MenuView: View {
228248
229249 private func usageDetail(_ usage: ClaudeUsage) -> some View {
230250 VStack(alignment: .leading, spacing: 12) {
231− if let headline = Self.headline(for: usage) {
251+ if let headline = ViewModel.claudeHeadline(for: usage) {
232252 HStack(alignment: .firstTextBaseline, spacing: 8) {
233253 Text("\(headline.percent)%")
234254 .font(Self.subheroFont)
@@ -283,18 +303,6 @@ struct MenuView: View {
283303 }
284304 }
285305
286− /// The number the section leads with: whichever limit the server flags as binding,
287− /// falling back to the 5-hour window when it flags none.
288− private static func headline(for usage: ClaudeUsage) -> (percent: Int, resetsIn: String?)? {
289− if let active = usage.activeLimit {
290− return (active.percent, active.resetsInFormatted)
291− }
292− if let fiveHour = usage.fiveHour {
293− return (Int(fiveHour.utilization.rounded()), fiveHour.resetsInFormatted)
294− }
295− return nil
296− }
297−
298306 /// The balance colors read the other way round here, because for a limit more is
299307 /// worse: green with room to spare, yellow closing in, red nearly spent.
300308 private static func usageColor(_ percent: Int) -> Color {