A new row in the session list menu opens one focused screen that answers a single question:
how much of each account’s quota is gone, and when does it come back. Every number on this page is invented.
Nothing here is a live reading, and no real account or credential is quoted.
The recommendation
One sheet, one card per account, one refresh. It borrows the retry-later sheet’s shape and the
S2 list’s colours, so it adds no new visual system.
Grouped by account, not by host
A provider heading, then one card per account underneath it.
Hosts are a line inside the card. Two hosts never become two quotas when they share one sign-in.
They are only merged on proof. An unidentified host gets its own card and says so.
Windows named by their duration
“Current window · 5 h” and “Weekly · 7 days”, taken from the duration the provider reported.
Never from the API’s own key name. primary is not a promise of five hours.
A window that was not reported says so, and draws no bar.
A reading, with its provenance
Every card footer says when the reading was taken and which host took it.
One Refresh in the header. Opening the sheet refreshes.
A refresh never blanks the numbers already on screen.
Where it lives
A plain row in the session list ••• menu, between Terminal and Settings — with the other read-only
destinations, above the Group by and Sort by sections. The desktop host-strip menu gets the same row.
Phone · the ••• menu. The row is a normal menu item, not a badge or a status readout —
the list header stays about sessions.
Why here, and not in Settings or the header
Not the header. The header says which hosts are up. A percentage there competes with
the thing Eugene actually scans the list for.
Not Settings. Nothing here is a setting. It is a reading he checks before starting
expensive work, which is a destination, not a preference.
Not a tab. Two tabs already carry badges. A third destination for a number he looks at
a few times a day is too much furniture.
Pick a scenario to inspect each state. The same six scenarios drive the canvas boards.
Phone · 390 pt. The sheet hugs its content: about 700 pt in the normal state,
with .large as a second detent.
Anatomy, top to bottom
Header, 48 pt. Close on the left, “Usage limits” centred at 17 semibold, Refresh on the
right. No other controls.
Provider heading, 13 pt semibold uppercase, 0.04em tracking, at tertiaryLabel.
Account card, radius 20, tertiarySystemGroupedBackground. Plan at 17
semibold, identity and hosts at 13 grey.
Quota row, ~83 pt. Label and percentage on one baseline, a 6 pt capacity bar, then the
reset line at 13 pt.
Footer, 34 pt. When the reading was taken, and from where.
Honest states
The states that matter most are the ones where there is no good number. Each gets its own
sentence rather than a plausible-looking zero.
A missing window is never 0%
The live Codex login returns a weekly window and nothing else. That shape must survive the design.
Such a row reads Not reported in grey and draws no bar at all. An
empty track reads as “0% used”, which would be a lie in the most expensive direction.
Each window is classified by its own reported duration, so a provider that adds a third bucket
appears without a code change.
A failed read is not a signed-out account
Copy names the host in plain words: “LFG couldn’t reach Air for an update. This does not mean the
account is signed out.”
No status codes in the UI. The last good reading stays visible underneath, with its age.
Sign-in copy appears only when the provider itself says the credential was rejected.
Stale data stays readable
The footer turns amber and says how old the reading is. The numbers are never dimmed — a stale
reading is still the best information there is.
An offline host keeps its card at 58% opacity rather than disappearing, so the account does not
silently lose a window.
Pay-per-token has nothing to draw
An API-key account shows a short explanation and no bars. There is no subscription
window, so none is invented.
Claude with no observation yet says “No reading yet”, not 0% and not an error.
Thresholds
Used
Bar and value
Copy
Why
0–74%
Accent blue
“Resets 4:10 PM · in 1 h 12 m”
Information, not a warning.
75–89%
Amber
Unchanged
Attention without alarm — the same amber the list uses for “needs input”.
90–99%
Amber
“Little left · resets …”
A word, so colour is never the only carrier.
100%
Red, full
“Used up · back at 4:10 PM”
The reset time becomes the headline, because it is the only thing left to act on.
Not reported
No bar
“Not reported”
A zero-length bar would read as 0% used.
iPad
A fitted form sheet about 460 pt wide, centred over the split view, with the same two-column
content. It does not stretch to fill a regular-width sheet — half an empty sheet was the retry-later mistake.
iPad · landscape. .presentationSizing(.fitted). The sidebar and detail stay visible
behind the scrim, so the sheet reads as a scoped detour rather than a new screen.
Mac
A 320 pt popover anchored to the ••• button in the host strip — not a sheet. This is a glance,
and a popover dismisses on an outside click with nothing to confirm.
Mac · the narrow 400 pt window the desktop app is actually used in. The popover fits with
10 pt to spare on each side.
Mac specifics
Width 320 pt, content height about 430 pt, capped at 520 pt with a scroll.
Metrics drop to the desktop scale: 13 / 12 / 11 / 10 pt.
Refresh is a 24 pt circular button in the popover header; ⌘R also refreshes.
Esc closes. Tab reaches Refresh, then any Try again button.
A sheet would have been consistent with Retry later, but Retry later is a task with a commit step.
This has no commit step, so a modal is a cost with no payoff.
Rules, tokens and accessibility
Role
Phone dark / light
Mac dark / light
Code
Sheet ground
#1C1C1E / #F2F2F7
#232327 / #FFFFFF
grouped background · TaskPalette
Account card
#2C2C2E / #FFFFFF
#26262B / #EFEFEF
tertiarySystemGroupedBackground
Bar fill, normal
#0A6EE0 / #0088FF
#3B8EFF / #007AFF
TaskColors.accentFill
Bar fill, 75%+
#FF9F0A / #B35100
#FFB340 / #B35100
S2 attention
Bar fill, 100%
#FF453A / #D70015
#FF6B61 / #D70015
TaskColors.danger
Bar track
label ink 16%
label ink 16%
derived, no semantic twin
Reset line
#A1A1A6 / #6C6C70
#8A8A93 / #737377
S2 secondaryInk · S2Desk.secondary
Footer / provider head
#8E8E93 both
#8E8E93 both
S2 metaInk
Accessibility
Each quota row is one combined element: “Claude Code, Max 20×, current window,
5 hours, 62 percent used, resets in 1 hour 12 minutes.”
The bar is hidden from VoiceOver. A gauge that repeats the row’s own value just double-speaks it;
Apple’s gauge guidance is satisfied by the row text, which names the value and the range.
Every warning state carries a word as well as a colour, and blue / amber / red differ
in lightness, not only hue.
Dynamic Type scales the text; the bar stays 6 pt and the row wraps to two lines at accessibility sizes.
Targets are at least 44 pt on the phone, including Try again.
Keyboard
Esc closes. ⌘R refreshes on iPad and Mac.
Tab order: Close → Refresh → each Try again in card order.
The sheet traps focus while open and restores it to the menu button on close.
Refresh behaviour
Opening the sheet refreshes every host. A short cache stops a reopen from hammering them.
While refreshing: the header reads “Refreshing”, card footers read “Refreshing…”, and
the numbers stay.
A per-card Try again appears only in the error state. There is no per-card refresh otherwise —
with at most four cards, two taps for one job is noise.
What this is not
Not context-window fullness, and not token spend for one conversation. These quotas belong to the
account and span every conversation on it.
Not an analytics dashboard. No history, no burn-rate chart, no projections — those need data nobody
is collecting yet.