Skip to content
750 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The plan says what g1t is for without measuring it against other products1/**
2 * The billing page's sections: the spend-spike banner (also in the app
3 * shell), alerts, the g1t plan, the trial, the spend limit and its range,
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.4 * Raise my limit, credits from g1t, Prepay, and "Spent more than you
The plan says what g1t is for without measuring it against other products5 * meant to?". Each posts to the billing route's action with an `intent`.
6 */
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging7import { ArrowUpRight, CreditCard, Gauge, Gift, Landmark, ShieldCheck, Sparkles } from "lucide-react";
The plan says what g1t is for without measuring it against other products8import type { ReactNode } from "react";
9import { Form } from "react-router";
10
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging11import type { Credits, Entitlements, FeatureState, Limit, LimitRequest, MeterUsage, UsageAlert } from "@g1t/contracts";
The plan says what g1t is for without measuring it against other products12
13import {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging14 CREDIT_KIND,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person15 PLUS_TAX,
The plan says what g1t is for without measuring it against other products16 PREPAY,
17 type PlanStatus,
18 alertText,
19 alertTone,
Merge Stripe Tax, the card fee on card payments, and one free workspace per person20 cardFeeCents,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging21 creditLine,
The plan says what g1t is for without measuring it against other products22 gigabytes,
23 requestStatus,
24 share,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging25 shortDay,
The plan says what g1t is for without measuring it against other products26 shownMeters,
27 spendRange,
28} from "../lib/billing";
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.29import { money, wholeDollars } from "../lib/money";
Merge branch 'worktree-agent-a985c01f43538af01'30import { ErrorText, SubmitButton } from "./ui";
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.31import { Badge } from "./ui/badge";
The plan says what g1t is for without measuring it against other products32import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
33
34/** An error the action returned, for the section it belongs to. */
35export type SectionError = { section: string; error: string } | null | undefined;
36
37const CONTROL =
38 "rounded-md border border-line bg-bg px-2.5 py-1.5 text-sm tabular-nums outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim";
39
40/** A dollar amount field: a `$` and a number. */
41function DollarInput({ name, defaultValue, placeholder, label }: { name: string; defaultValue?: string; placeholder?: string; label: string }) {
42 return (
43 <span className="flex items-center rounded-md border border-line bg-bg px-2 focus-within:border-accent-dim">
44 <span className="text-sm text-muted">$</span>
45 <input
46 name={name}
47 inputMode="decimal"
48 aria-label={label}
49 defaultValue={defaultValue}
50 placeholder={placeholder}
51 autoComplete="off"
52 data-1p-ignore
53 data-lpignore="true"
54 className="w-24 bg-transparent px-1 py-1.5 text-sm tabular-nums outline-none placeholder:text-faint"
55 />
56 </span>
57 );
58}
59
60/** A bordered card with a heading, a line about it, and a tone. */
61export function Card({
62 id,
63 title,
64 icon,
65 aside,
66 about,
67 tone = "plain",
68 children,
69}: {
70 id?: string;
71 title: ReactNode;
72 icon?: ReactNode;
73 aside?: ReactNode;
74 about?: ReactNode;
75 tone?: "plain" | "accent" | "warn" | "danger";
76 children?: ReactNode;
77}) {
78 const tones = {
79 plain: "border-line bg-surface",
80 accent: "border-accent/40 bg-accent/5",
81 warn: "border-warn/40 bg-warn/5",
82 danger: "border-danger/40 bg-danger/5",
83 };
84 return (
85 <section id={id} className={`mb-6 scroll-mt-20 rounded-xl border p-5 ${tones[tone]}`}>
86 <div className="flex flex-wrap items-start justify-between gap-3 sm:flex-nowrap">
87 <div className="min-w-0 basis-full sm:basis-0 sm:flex-1">
88 <h2 className="flex flex-wrap items-center gap-2 font-medium">
89 {icon && <span className="text-accent">{icon}</span>}
90 {title}
91 </h2>
92 {about && <div className="mt-1 max-w-2xl text-sm text-muted">{about}</div>}
93 </div>
94 {aside}
95 </div>
96 {children}
97 </section>
98 );
99}
100
101/** One meter: how much of something is used. */
102export function Meter({
103 used,
104 of,
105 label,
106 detail,
107 calm = false,
108}: {
109 used: number;
110 of: number | null;
111 label: string;
112 detail?: ReactNode;
113 /** Reaching the end is ordinary, not a warning: the plan's included usage, past which usage simply goes on demand. */
114 calm?: boolean;
115}) {
116 const part = share(used, of);
Merge branch 'worktree-agent-ad7c6d88d93adc817'117 const bar = calm ? "bg-success" : of != null && used >= of ? "bg-danger" : part >= 0.75 ? "bg-warn" : "bg-success";
The plan says what g1t is for without measuring it against other products118 return (
119 <div className="mt-4">
120 <div className="flex flex-wrap justify-between gap-x-4 text-sm">
121 <span className="text-muted">{label}</span>
122 <span className="tabular-nums">
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.123 {money(used)}
124 {of != null && <span className="text-faint"> of {money(of)}</span>}
The plan says what g1t is for without measuring it against other products125 </span>
126 </div>
127 <div className="mt-1.5 h-1.5 overflow-hidden rounded-full bg-line" role="presentation">
128 <div className={`h-full ${bar}`} style={{ width: `${Math.max(part * 100, part > 0 ? 2 : 0)}%` }} />
129 </div>
130 {detail && <p className="mt-1.5 text-xs text-faint">{detail}</p>}
131 </div>
132 );
133}
134
135// --- Spike ------------------------------------------------------------------------
136
137// In its own module, so the app shell carries it without the billing page.
138export { SpikeBanner } from "./spike-banner";
139
140// --- Alerts -----------------------------------------------------------------------
141
142export function Alerts({ alerts }: { alerts: UsageAlert[] }) {
143 const shown = alerts.filter((alert) => alert.level >= 50).sort((a, b) => b.level - a.level);
144 if (shown.length === 0) return null;
145 return (
146 <ul className="mb-6 space-y-2">
147 {shown.map((alert) => {
148 const tone = alertTone(alert.level);
149 return (
150 <li
151 key={`${alert.meter}-${alert.level}`}
152 className={`flex gap-2.5 rounded-lg border px-4 py-2.5 text-sm ${
153 tone === "stopped" ? "border-danger/40 bg-danger/5" : tone === "warning" ? "border-warn/40 bg-warn/5" : "border-line bg-surface"
154 }`}
155 >
156 <Gauge size={15} className={`mt-0.5 shrink-0 ${tone === "stopped" ? "text-danger" : tone === "warning" ? "text-warn" : "text-muted"}`} />
157 <span>{alert.message || alertText(alert)}</span>
158 </li>
159 );
160 })}
161 </ul>
162 );
163}
164
165// --- The plan -----------------------------------------------------------------------
166
167const STATUS_TONE: Record<PlanStatus["kind"], "plain" | "accent" | "warn"> = {
168 free: "plain",
169 trial: "plain",
170 paid: "accent",
171 canceling: "warn",
172 past_due: "warn",
173 comped: "accent",
174 enterprise: "accent",
175};
176
177export function PlanCard({
178 state,
179 status,
180 entitlements,
181 owner,
182 enabled,
183 live,
184 meters,
185 error,
186}: {
187 state: FeatureState | null;
188 status: PlanStatus;
189 entitlements: Entitlements | null;
190 owner: boolean;
191 enabled: boolean;
192 live: boolean;
193 /** This month's usage by meter, from billing's `usage_meters`. */
194 meters: MeterUsage[] | null;
195 error?: string;
196}) {
197 const plan = state?.plan;
198 const subscription = state?.subscription ?? null;
199 const on = ["paid", "canceling", "comped", "enterprise"].includes(status.kind);
200 return (
201 <Card
202 id="plan"
203 tone={STATUS_TONE[status.kind]}
204 icon={<Sparkles size={16} />}
205 title={
206 <>
207 {plan?.title ?? "g1t"}
208 <span
209 className={`rounded-full px-2 py-0.5 text-xs ${
210 status.kind === "past_due" || status.kind === "canceling"
211 ? "bg-warn/15 text-warn"
212 : on
Merge branch 'worktree-agent-ad7c6d88d93adc817'213 ? "bg-success/15 text-success"
The plan says what g1t is for without measuring it against other products214 : "border border-line text-muted"
215 }`}
216 >
217 {status.label}
218 {status.kind === "canceling" && subscription?.periodEnd ? `, ${new Date(subscription.periodEnd).toLocaleDateString()}` : ""}
219 </span>
220 </>
221 }
222 about={
223 on
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.224 ? "Everything on g1t for the workspace: checks, workflows, the merge queue, deployments and semantic search."
The plan says what g1t is for without measuring it against other products225 : "The forge is free: repositories, issues, pull requests, reviews, search and the audit log. Anything that runs compute needs the plan, or the trial."
226 }
227 aside={
228 <p className="shrink-0 sm:text-right">
229 <span className="text-2xl font-semibold tabular-nums tracking-tight">${((plan?.monthlyCents ?? 2000) / 100).toFixed(0)}</span>
230 <span className="text-sm text-muted"> / month</span>
231 <span className="block text-xs text-faint">per workspace, never per seat</span>
Merge Stripe Tax, the card fee on card payments, and one free workspace per person232 <span className="block text-xs text-faint">
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.233 {plan?.cardFeeCents ? `+ ${money(plan.cardFeeCents * 10_000)} card processing fee, ${PLUS_TAX}` : "Plus tax where it applies"}
Merge Stripe Tax, the card fee on card payments, and one free workspace per person234 </span>
The plan says what g1t is for without measuring it against other products235 </p>
236 }
237 >
238 {plan && (
239 <ul className="mt-4 grid gap-1.5 text-sm text-muted sm:grid-cols-2">
240 {plan.includes.map((line) => (
241 <li key={line} className="flex gap-2">
Merge branch 'worktree-agent-ad7c6d88d93adc817'242 <span className="text-success">✓</span>
The plan says what g1t is for without measuring it against other products243 {line}
244 </li>
245 ))}
246 </ul>
247 )}
248 {plan && <p className="mt-3 text-xs text-faint">{plan.overage}</p>}
249
250 {on && entitlements?.includedMicros ? (
251 <Meter
252 label="Included usage this month"
253 used={entitlements.includedUsedMicros ?? 0}
254 of={entitlements.includedMicros}
255 calm
256 detail="Used first, at cost plus 20%. Past it, usage goes on at the same prices. It starts again on the 1st; what is unused does not carry over."
257 />
258 ) : null}
259 {meters && <MonthUsage meters={meters} on={on} comped={status.kind === "comped"} entitlements={entitlements} />}
260
261 {status.kind === "comped" || status.kind === "enterprise" ? (
262 <p className="mt-4 text-sm text-muted">
263 {status.kind === "comped"
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging264 ? "This workspace has a 100% discount from g1t: the plan and its usage are shown at their price, and nothing is charged."
The plan says what g1t is for without measuring it against other products265 : "An enterprise pays for this workspace, on its own invoice."}
266 </p>
267 ) : !enabled ? (
268 <p className="mt-4 text-sm text-muted">Payments are not set up on this g1t, so the plan cannot be started here.</p>
269 ) : !owner ? (
270 <p className="mt-4 text-sm text-muted">An owner manages the plan.</p>
271 ) : (
272 <Form method="post" className="mt-5 flex flex-wrap items-center gap-3">
273 <input type="hidden" name="feature" value={plan?.feature ?? "plan"} />
274 {status.kind === "free" || status.kind === "trial" ? (
Merge branch 'worktree-agent-a985c01f43538af01'275 <SubmitButton variant="accent" name="intent" value="subscribe" pending="Opening Stripe…">
The plan says what g1t is for without measuring it against other products276 <CreditCard size={14} />
277 Start the g1t plan
Merge branch 'worktree-agent-a985c01f43538af01'278 </SubmitButton>
The plan says what g1t is for without measuring it against other products279 ) : status.kind === "canceling" ? (
Merge branch 'worktree-agent-a985c01f43538af01'280 <SubmitButton variant="accent" name="intent" value="resume" pending="Saving…">
The plan says what g1t is for without measuring it against other products281 Keep the plan
Merge branch 'worktree-agent-a985c01f43538af01'282 </SubmitButton>
The plan says what g1t is for without measuring it against other products283 ) : null}
284 {(status.kind === "paid" || status.kind === "canceling" || status.kind === "past_due") && (
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.285 <SubmitButton variant={status.kind === "past_due" ? "accent" : "outline"} name="intent" value="portal" pending="Opening Stripe…">
The plan says what g1t is for without measuring it against other products286 {status.kind === "past_due" ? "Update payment on Stripe" : "Manage on Stripe"}
287 <ArrowUpRight size={14} />
Merge branch 'worktree-agent-a985c01f43538af01'288 </SubmitButton>
The plan says what g1t is for without measuring it against other products289 )}
290 {status.kind === "paid" && subscription && (
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.291 <SubmitButton variant="outline" name="intent" value="cancel" pending="Saving…">
The plan says what g1t is for without measuring it against other products292 End at the end of the period
Merge branch 'worktree-agent-a985c01f43538af01'293 </SubmitButton>
The plan says what g1t is for without measuring it against other products294 )}
295 {!live && (status.kind === "free" || status.kind === "trial") && (
296 <span className="text-xs text-faint">Test mode: card 4242 4242 4242 4242, any future date and code.</span>
297 )}
298 </Form>
299 )}
300 <ErrorText>{error}</ErrorText>
301 {(status.kind === "free" || status.kind === "trial") && (
302 <p className="mt-4 text-xs text-faint">
303 Your first month starts at a {wholeDollars(100_000_000)} limit on usage past what is included, so a stolen card cannot run
304 up a bill. It rises as payments clear, and you can prepay or ask to raise it at once.
305 </p>
306 )}
307 </Card>
308 );
309}
310
311/**
312 * This month's usage, one line per meter, in dollars at cost plus 20% and
313 * in what was used. No quotas: every line is metered from the first unit.
314 */
315function MonthUsage({
316 meters,
317 on,
318 comped,
319 entitlements,
320}: {
321 meters: MeterUsage[];
322 on: boolean;
323 comped: boolean;
324 entitlements: Entitlements | null;
325}) {
326 const freeStorage = gigabytes(entitlements?.freePrivateStorageBytes ?? 1_000_000_000);
327 const freeGit = (entitlements?.gitOperationsIncluded ?? 50_000).toLocaleString("en-US");
328 const rows = shownMeters(meters, on);
329 const total = rows.reduce((sum, row) => sum + row.micros, 0);
330 return (
331 <div className="mt-4 rounded-lg border border-line bg-bg/40 p-4">
332 <div className="flex items-baseline justify-between gap-4">
333 <p className="text-xs font-medium text-muted">This month's usage</p>
334 <p className="text-xs text-faint">At cost plus 20%</p>
335 </div>
336 <ul className="mt-2 divide-y divide-line/60">
337 {rows.map((row) => (
338 <li key={row.key} className="flex items-baseline justify-between gap-4 py-2 text-sm">
339 <span className="min-w-0">
340 <span>{row.label}</span>
341 {(row.quantity || row.micros === 0) && (
342 <span className="block truncate text-xs text-faint">{row.quantity ?? "None yet"}</span>
343 )}
344 </span>
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.345 <span className={`shrink-0 tabular-nums ${row.micros > 0 ? "" : "text-faint"}`}>{money(row.micros)}</span>
The plan says what g1t is for without measuring it against other products346 </li>
347 ))}
348 </ul>
349 <div className="mt-1 flex items-baseline justify-between gap-4 border-t border-line pt-2.5 text-sm">
Billing's usage total is labeled at price, and Usage says what part of it was paid for350 {/* At price, before the plan's included usage, the trial and the pools paid
351 their part: Usage shows what was charged. */}
352 <span className="font-medium">Total at price</span>
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.353 <span className="font-medium tabular-nums">{money(total)}</span>
The plan says what g1t is for without measuring it against other products354 </div>
355 <p className="mt-2 text-xs text-faint">
356 {comped
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging357 ? "What this workspace's usage comes to at price. Its 100% discount takes all of it off."
The plan says what g1t is for without measuring it against other products358 : on
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.359 ? `Drawn from the included usage first, then charged up to your spend limit. Projects, previews and repositories are never charged, and the first ${freeStorage} of private storage and ${freeGit} git operations a month are free. App traffic, custom domains, storage and git operations are counted through the month and charged when it closes, after the included usage and any discount, like the rest.`
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.360 : `The forge is free: ${freeStorage} of private storage and ${freeGit} git operations a month. Checks, workflows and the merge queue run from the trial, and on public repositories from the open-source pool; together they pay part of this total: Usage shows what was charged.`}
The plan says what g1t is for without measuring it against other products361 </p>
362 </div>
363 );
364}
365
366// --- The trial -----------------------------------------------------------------------
367
368export function TrialCard({
369 entitlements,
370 trialMicros,
371 owner,
372 enabled,
373 error,
374}: {
375 entitlements: Entitlements | null;
376 trialMicros: number;
377 owner: boolean;
378 enabled: boolean;
379 error?: string;
380}) {
381 const verified = entitlements?.trialVerified ?? false;
382 const left = entitlements?.trialMicrosLeft ?? 0;
383 return (
384 <Card
385 id="trial"
386 icon={<ShieldCheck size={16} />}
387 title={`Try it with ${wholeDollars(trialMicros)} of usage`}
388 about={
389 verified
390 ? left > 0
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.391 ? "Your trial pays for checks, workflows and deployments until it is used. It is given once per workspace."
The plan says what g1t is for without measuring it against other products392 : "This workspace has no trial left. Start the plan to keep going."
393 : "Once per workspace, after a card check: Stripe saves and verifies the card with 3-D Secure and charges nothing. The trial needs a credit or debit card; prepaid cards can still pay for the plan."
394 }
395 >
396 {verified && left > 0 && <Meter label="Trial used" used={Math.max(0, trialMicros - left)} of={trialMicros} />}
397 {!verified && enabled && owner && (
398 <Form method="post" className="mt-4 flex flex-wrap items-center gap-3">
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.399 <SubmitButton variant="outline" name="intent" value="card-check" pending="Opening Stripe…">
The plan says what g1t is for without measuring it against other products400 <ShieldCheck size={14} />
401 Check a card
Merge branch 'worktree-agent-a985c01f43538af01'402 </SubmitButton>
The plan says what g1t is for without measuring it against other products403 <span className="text-xs text-faint">Trials come from a monthly pool; when it is given out, new ones start on the 1st.</span>
404 </Form>
405 )}
406 {!verified && !owner && <p className="mt-3 text-sm text-muted">An owner can do the card check.</p>}
407 <ErrorText>{error}</ErrorText>
408 </Card>
409 );
410}
411
412// --- Spend limit -----------------------------------------------------------------------
413
Merge branch 'worktree-agent-a985c01f43538af01'414export function SpendLimitCard({ limit, owner, error }: { limit: Limit; owner: boolean; error?: string }) {
The plan says what g1t is for without measuring it against other products415 const range = spendRange(limit);
416 const spent = limit.spentMicros ?? 0;
417 const spendLimit = limit.spendLimitMicros;
418 const mode = limit.defaultSpendLimit ? "automatic" : spendLimit == null ? "full" : "fixed";
419 const tone = limit.state === "stopped" ? "danger" : limit.state === "warning" ? "warn" : "plain";
420 return (
421 <Card
422 id="limit"
423 tone={tone}
424 icon={<Gauge size={16} />}
425 title="Spend limit"
426 about="What on-demand usage may reach this month, past what the plan includes. At the limit new work waits; runs already going finish."
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.427 aside={limit.firstMonth ? <Badge size="md">First month</Badge> : null}
The plan says what g1t is for without measuring it against other products428 >
429 {limit.account.startsWith("ent_") && (
430 <p className="mt-2 text-sm text-muted">
431 Paid for by the <span className="font-medium text-fg">{limit.accountName}</span> enterprise: these figures are for all of its
432 workspaces together.
433 </p>
434 )}
435 <div className="grid gap-x-8 sm:grid-cols-2">
436 <Meter
437 label="Charged this month"
438 used={spent}
439 of={spendLimit}
440 detail={
441 mode === "automatic"
442 ? "Automatic: $200, or twice last month's spend, within what is available."
443 : mode === "fixed"
444 ? "A limit you set."
445 : "As high as is available."
446 }
447 />
448 <Meter
449 label="Not yet paid"
450 used={limit.exposureMicros}
451 of={limit.ceilingMicros}
452 detail={limit.growth ?? "Charged to the card as it nears the limit, so work keeps going."}
453 />
454 </div>
455 {limit.message && <p className="mt-4 text-sm">{limit.message}</p>}
456
457 <div className="mt-5 rounded-lg border border-line bg-bg/40 p-4 text-sm">
458 <p>
459 {range.selfServeMicros == null ? (
460 "No ceiling: set any limit."
461 ) : (
462 <>
463 You can set up to <span className="font-medium tabular-nums">{wholeDollars(range.selfServeMicros)}</span> yourself: the
464 highest limit this workspace has had{(limit.prepaidMicros ?? 0) > 0 ? ", plus what is prepaid" : ""}.
465 </>
466 )}
467 </p>
468 <p className="mt-1 text-muted">
469 {range.raiseOnceMicros != null ? (
470 <>
471 Once, you can raise it yourself to <span className="tabular-nums text-fg">{wholeDollars(range.raiseOnceMicros)}</span>.
472 Past that, prepay or use Raise my limit.
473 </>
474 ) : range.raisedAt ? (
475 <>The one-time raise was used on {new Date(range.raisedAt).toLocaleDateString()}. Prepay or use Raise my limit for more.</>
476 ) : range.selfServeMicros != null ? (
477 "For more, prepay or use Raise my limit."
478 ) : null}
479 </p>
480 </div>
481
482 {owner && (
483 <Form method="post" className="mt-4">
484 <input type="hidden" name="intent" value="spend-limit" />
485 <fieldset>
486 <legend className="text-xs text-muted">Your monthly spend limit</legend>
487 <RadioGroup name="mode" defaultValue={mode} className="mt-2 gap-2.5 text-sm">
488 <label className="flex cursor-pointer items-center gap-2">
489 <RadioGroupItem value="automatic" />
490 Automatic
491 </label>
492 <label className="flex cursor-pointer flex-wrap items-center gap-2">
493 <RadioGroupItem value="fixed" />
494 Fixed at
495 <DollarInput
496 name="limit"
497 label="Spend limit in dollars"
498 defaultValue={mode === "fixed" && spendLimit != null ? String(spendLimit / 1_000_000) : ""}
499 placeholder={range.selfServeMicros != null ? String(range.selfServeMicros / 1_000_000) : "500"}
500 />
501 </label>
502 <label className="flex cursor-pointer items-center gap-2">
503 <RadioGroupItem value="full" />
504 Everything available{range.selfServeMicros != null ? ` (${wholeDollars(range.selfServeMicros)})` : ""}
505 </label>
506 {range.raiseOnceMicros != null && (
507 <label className="flex cursor-pointer items-center gap-2">
508 <RadioGroupItem value="raise" />
509 Use my one-time raise, to the amount above or up to {wholeDollars(range.raiseOnceMicros)}
510 </label>
511 )}
512 </RadioGroup>
513 </fieldset>
514 <div className="mt-3 flex flex-wrap items-center gap-3">
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.515 <SubmitButton variant="outline" match={{ intent: "spend-limit" }} pending="Saving…">
The plan says what g1t is for without measuring it against other products516 Save limit
Merge branch 'worktree-agent-a985c01f43538af01'517 </SubmitButton>
The plan says what g1t is for without measuring it against other products518 <a href="#raise" className="text-sm text-muted hover:text-fg">
519 Need more? Raise my limit
520 </a>
521 </div>
522 <ErrorText>{error}</ErrorText>
523 </Form>
524 )}
525 </Card>
526 );
527}
528
529// --- Raise my limit -----------------------------------------------------------------------
530
531function RequestList({ requests }: { requests: LimitRequest[] }) {
532 if (requests.length === 0) return null;
533 return (
534 <ul className="mt-4 divide-y divide-line rounded-lg border border-line text-sm">
535 {requests.map((request) => (
536 <li key={request.id} className="px-4 py-2.5">
537 <p className="flex flex-wrap items-center justify-between gap-2">
538 <span>
539 {request.kind === "limit" ? `Asked for ${wholeDollars(request.amountMicros)}` : "Told g1t about this month"}
540 <span className="text-faint"> · {new Date(request.createdAt).toLocaleDateString()}</span>
541 </span>
542 <span
543 className={`rounded-full px-2 py-0.5 text-xs ${
544 request.status === "approved"
Merge branch 'worktree-agent-ad7c6d88d93adc817'545 ? "bg-success/15 text-success"
The plan says what g1t is for without measuring it against other products546 : request.status === "declined"
547 ? "bg-danger/15 text-danger"
548 : "border border-line text-muted"
549 }`}
550 >
551 {requestStatus(request)}
552 </span>
553 </p>
554 {request.answer && <p className="mt-1 text-muted">{request.answer}</p>}
555 </li>
556 ))}
557 </ul>
558 );
559}
560
Merge branch 'worktree-agent-a985c01f43538af01'561export function RaiseCard({ requests, owner, error }: { requests: LimitRequest[]; owner: boolean; error?: string }) {
The plan says what g1t is for without measuring it against other products562 const mine = requests.filter((request) => request.kind === "limit");
563 const waiting = mine.some((request) => request.status === "open");
564 return (
565 <Card
566 id="raise"
567 title="Raise my limit"
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.568 about="Ask for a higher limit than you can set yourself. A person at g1t reads it and answers within one business day, in the app and by email. Nothing about price is negotiated."
The plan says what g1t is for without measuring it against other products569 >
570 {owner && !waiting && (
571 <Form method="post" className="mt-4 space-y-3">
572 <input type="hidden" name="intent" value="request" />
573 <input type="hidden" name="kind" value="limit" />
574 <div className="flex flex-wrap gap-4">
575 <label className="text-sm">
576 <span className="mb-1 block text-xs text-muted">Limit you need</span>
577 <DollarInput name="amount" label="Limit you need in dollars" placeholder="2000" />
578 </label>
579 <label className="text-sm">
580 <span className="mb-1 block text-xs text-muted">What you expect to spend a month</span>
581 <DollarInput name="expected" label="Expected monthly spend in dollars" placeholder="1500" />
582 </label>
583 </div>
584 <label className="block text-sm">
585 <span className="mb-1 block text-xs text-muted">What it is for</span>
586 <textarea
587 name="reason"
588 rows={2}
589 maxLength={2000}
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.590 placeholder="A launch next week: builds and previews across three projects."
The plan says what g1t is for without measuring it against other products591 className={`${CONTROL} w-full`}
592 />
593 </label>
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.594 <SubmitButton variant="outline" match={{ intent: "request", kind: "limit" }} pending="Sending…">
The plan says what g1t is for without measuring it against other products595 Send the request
Merge branch 'worktree-agent-a985c01f43538af01'596 </SubmitButton>
The plan says what g1t is for without measuring it against other products597 <ErrorText>{error}</ErrorText>
598 </Form>
599 )}
600 {owner && waiting && <p className="mt-3 text-sm text-muted">Your request is with g1t. You can send another once it is answered.</p>}
601 {!owner && <p className="mt-3 text-sm text-muted">An owner can ask.</p>}
602 <RequestList requests={mine} />
603 </Card>
604 );
605}
606
607// --- Prepay -----------------------------------------------------------------------
608
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging609/**
610 * Credit g1t gave the workspace: what is left, each grant in a line with
611 * its expiry, and what each was for. Spent before anything prepaid.
612 */
613export function CreditsCard({ credits }: { credits: Credits }) {
614 const open = credits.grants.filter((grant) => grant.state === "open");
615 const past = credits.grants.filter((grant) => grant.state !== "open");
616 return (
617 <Card
618 id="credits"
619 icon={<Gift size={16} />}
620 title="Credits from g1t"
621 about="Credit g1t gave this workspace. It pays for usage before anything prepaid, the soonest-expiring first. Unused credit stops counting when it expires."
622 aside={
623 <p className="shrink-0 sm:text-right">
624 <span className="block text-xs text-muted">Credit left</span>
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.625 <span className="text-xl font-semibold tabular-nums">{money(credits.leftMicros)}</span>
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging626 </p>
627 }
628 >
629 <ul className="mt-4 divide-y divide-line rounded-lg border border-line">
630 {[...open, ...past].map((grant) => (
631 <li key={grant.id} className={`px-3.5 py-2.5 text-sm ${grant.state === "open" ? "" : "text-muted"}`}>
632 <p className="flex flex-wrap items-center gap-x-2 gap-y-1">
633 <span className={`tabular-nums ${grant.state === "open" ? "font-medium" : ""}`}>{creditLine(grant)}</span>
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.634 <Badge size="md" className="py-px">{CREDIT_KIND[grant.kind]}</Badge>
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging635 </p>
636 <p className="mt-0.5 text-xs text-faint">
637 {grant.kind === "refund" && grant.refundFor ? `For ${grant.refundFor}. ` : ""}
638 {grant.note} · given {shortDay(grant.createdAt)}
639 </p>
640 </li>
641 ))}
642 </ul>
643 </Card>
644 );
645}
646
Merge Stripe Tax, the card fee on card payments, and one free workspace per person647export function PrepayCard({
648 prepaidMicros,
649 owner,
650 live,
651 cardFee,
652 error,
653}: {
654 prepaidMicros: number;
655 owner: boolean;
656 live: boolean;
657 /** The card fee as billing charges it, to show it before paying. */
658 cardFee?: { on: boolean; percentMicros: number; fixedCents: number } | null;
659 error?: string;
660}) {
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.661 const fees = PREPAY.presets.map((amount) => `${money(cardFeeCents(amount * 100, cardFee) * 10_000)} on ${wholeDollars(amount * 1_000_000)}`);
The plan says what g1t is for without measuring it against other products662 return (
663 <Card
664 id="prepay"
665 icon={<Landmark size={16} />}
666 title="Prepay"
667 about="Pay for usage in advance. It raises what you can use before work stops by the same amount, at once, and usage draws it down."
668 aside={
669 <p className="shrink-0 sm:text-right">
670 <span className="block text-xs text-muted">Prepaid balance</span>
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.671 <span className="text-xl font-semibold tabular-nums">{money(prepaidMicros)}</span>
The plan says what g1t is for without measuring it against other products672 </p>
673 }
674 >
675 {owner ? (
676 <Form method="post" className="mt-4 space-y-3">
677 <input type="hidden" name="intent" value="prepay" />
678 <div className="flex flex-wrap items-center gap-2">
679 {PREPAY.presets.map((amount) => (
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.680 <SubmitButton key={amount} variant="outline" name="amount" value={amount} match={{ intent: "prepay" }} pending="Opening Stripe…">
The plan says what g1t is for without measuring it against other products681 {wholeDollars(amount * 1_000_000)}
Merge branch 'worktree-agent-a985c01f43538af01'682 </SubmitButton>
The plan says what g1t is for without measuring it against other products683 ))}
684 <span className="text-sm text-muted">or</span>
685 <DollarInput name="custom" label="Amount to prepay in dollars" placeholder="250" />
Merge branch 'worktree-agent-a985c01f43538af01'686 {/* An empty amount, so a preset's press is not this button's: the typed amount is used. */}
687 <SubmitButton variant="accent" name="amount" value="" match={{ intent: "prepay" }} pending="Opening Stripe…">
The plan says what g1t is for without measuring it against other products688 <CreditCard size={14} />
689 Prepay
Merge branch 'worktree-agent-a985c01f43538af01'690 </SubmitButton>
The plan says what g1t is for without measuring it against other products691 </div>
692 <RadioGroup name="method" defaultValue="card" className="flex flex-wrap gap-x-5 gap-y-2 text-sm">
693 <label className="flex cursor-pointer items-center gap-2">
694 <RadioGroupItem value="card" />
695 By card, with 3-D Secure ({wholeDollars(PREPAY.min * 1_000_000)} to {wholeDollars(PREPAY.maxCard * 1_000_000)})
696 </label>
697 <label className="flex cursor-pointer items-center gap-2">
698 <RadioGroupItem value="bank_transfer" />
699 By bank transfer (from {wholeDollars(PREPAY.bankFrom * 1_000_000)}; counted when it arrives)
700 </label>
701 </RadioGroup>
Merge Stripe Tax, the card fee on card payments, and one free workspace per person702 <p className="text-xs text-faint">
703 {cardFee?.on ? `By card, a card processing fee is its own line (${fees.join(", ")}); a bank transfer has none. ` : ""}
704 Tax is added where it applies, from your billing address; what you prepay is credited in full.
705 </p>
The plan says what g1t is for without measuring it against other products706 {!live && <p className="text-xs text-faint">Test mode: card 4242 4242 4242 4242, any future date and code.</p>}
707 <ErrorText>{error}</ErrorText>
708 </Form>
709 ) : (
710 <p className="mt-3 text-sm text-muted">An owner can prepay.</p>
711 )}
712 </Card>
713 );
714}
715
716// --- Overage -----------------------------------------------------------------------
717
Merge branch 'worktree-agent-a985c01f43538af01'718export function OverageCard({ requests, owner, error }: { requests: LimitRequest[]; owner: boolean; error?: string }) {
The plan says what g1t is for without measuring it against other products719 const mine = requests.filter((request) => request.kind === "overage");
720 const waiting = mine.some((request) => request.status === "open");
721 return (
722 <Card
723 id="overage"
724 title="Spent more than you meant to? Tell us."
725 about="A loop that ran all night, a workflow on every push: say what happened. g1t can credit usage you did not mean, once in 12 months; larger credits, or a second one within 12 months, are reviewed by a person."
726 >
727 {owner && !waiting && (
728 <Form method="post" className="mt-4 space-y-3">
729 <input type="hidden" name="intent" value="request" />
730 <input type="hidden" name="kind" value="overage" />
731 <textarea
732 name="reason"
733 rows={2}
734 maxLength={2000}
735 aria-label="What happened"
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.736 placeholder="A workflow kept retrying a failing check on Tuesday night."
The plan says what g1t is for without measuring it against other products737 className={`${CONTROL} w-full`}
738 />
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.739 <SubmitButton variant="outline" match={{ intent: "request", kind: "overage" }} pending="Sending…">
The plan says what g1t is for without measuring it against other products740 Tell g1t
Merge branch 'worktree-agent-a985c01f43538af01'741 </SubmitButton>
The plan says what g1t is for without measuring it against other products742 <ErrorText>{error}</ErrorText>
743 </Form>
744 )}
745 {owner && waiting && <p className="mt-3 text-sm text-muted">g1t has your note and answers within one business day.</p>}
746 {!owner && <p className="mt-3 text-sm text-muted">An owner can tell g1t.</p>}
747 <RequestList requests={mine} />
748 </Card>
749 );
750}

This file's history is long; its oldest lines are credited to the oldest commit read.