# Ledger: Mac app

> A five-page site for a Mac app: an interactive desktop hero, a menu-bar tour, a native-features bento, a device lineup, license-or-subscription pricing, a download page, release notes and support.

A shadcn/ui block from the [@ballmac](https://ui.ballmac.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/ballmac/template-ledger).

- Page, with a live preview: https://shadcn.exchange/r/ballmac/template-ledger
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/ballmac/template-ledger-light-2f151b19c8c4b791.png
- Registry item: https://ui.ballmac.com/r/template-ledger.json
- Categories: templates
- Listed: 2026-10-14, source last changed 2026-10-14

## Install

```bash
npx shadcn@latest add https://ui.ballmac.com/r/template-ledger.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`
- Registry items: `utils`, `https://ui.ballmac.com/r/blur-fade.json`, `https://ui.ballmac.com/r/changelog-1.json`, `https://ui.ballmac.com/r/devices-1.json`, `https://ui.ballmac.com/r/download-1.json`, `https://ui.ballmac.com/r/faq-2.json`, `https://ui.ballmac.com/r/features-7.json`, `https://ui.ballmac.com/r/kbd.json`, `https://ui.ballmac.com/r/mac-icons.json`, `https://ui.ballmac.com/r/pricing-4.json`, `https://ui.ballmac.com/r/showcase-1.json`

## Notes from the registry

Pages are at /ledger, /ledger/download, /ledger/pricing, /ledger/changelog and /ledger/support. The light and dark palettes are the ledgerCss string in ledger-theme.tsx.

## Files

### `registry/ballmac/components/templates/ledger/ledger-fonts.ts`

```ts
// Ballmac UI: Ledger template fonts. https://ui.ballmac.com/templates/template-ledger
import { Inter, Newsreader } from "next/font/google"

/** The italic serif used for one emphasised word per heading. Body text uses the system font, like a Mac app. */
export const ledgerSerif = Newsreader({ variable: "--ledger-serif", subsets: ["latin"], style: ["normal", "italic"], display: "swap", preload: false })

/** Used where San Francisco is not installed (Windows, Linux, Android), so the page looks the same everywhere. */
export const ledgerSans = Inter({ variable: "--ledger-sans", subsets: ["latin"], display: "swap", preload: false })
```

### `registry/ballmac/components/templates/ledger/ledger-theme.tsx`

```tsx
// Ballmac UI: Ledger template shell. https://ui.ballmac.com/templates/template-ledger
"use client"

import * as React from "react"
import { Menu, X } from "lucide-react"

import { AppIcon } from "@/components/ballmac/mac-icons"
import { ledgerSans, ledgerSerif } from "@/components/ballmac/templates/ledger/ledger-fonts"
import { cn } from "@/lib/utils"

type LedgerPage = "home" | "download" | "pricing" | "changelog" | "support"
type LedgerHrefs = Record<LedgerPage, string>

const defaultHrefs: LedgerHrefs = { home: "/ledger", download: "/ledger/download", pricing: "/ledger/pricing", changelog: "/ledger/changelog", support: "/ledger/support" }

/** Ledger's palette: Apple-calm. Cool white, soft grey surfaces, one blue. Dark mode follows the system. */
const ledgerCss = `
.ledger-theme{--background:oklch(0.99 0.002 250);--foreground:oklch(0.2 0.01 255);--card:oklch(1 0 0);--card-foreground:oklch(0.2 0.01 255);--popover:oklch(1 0 0);--popover-foreground:oklch(0.2 0.01 255);--primary:oklch(0.2 0.01 255);--primary-foreground:oklch(0.99 0.002 250);--secondary:oklch(0.965 0.004 250);--secondary-foreground:oklch(0.2 0.01 255);--muted:oklch(0.965 0.004 250);--muted-foreground:oklch(0.5 0.012 255);--accent:oklch(0.955 0.006 250);--accent-foreground:oklch(0.2 0.01 255);--border:oklch(0.2 0.01 255 / 10%);--input:oklch(0.2 0.01 255 / 14%);--ring:oklch(0.6 0.19 255);--surface:oklch(0.975 0.003 250);--chart-1:oklch(0.52 0.2 255);--ledger-on-accent:oklch(1 0 0);--chart-2:oklch(0.48 0.14 155);--chart-3:oklch(0.78 0.16 75);--chart-4:oklch(0.62 0.2 305);--chart-5:oklch(0.68 0.19 25);--radius:1rem}
.dark .ledger-theme{--background:oklch(0.16 0.006 255);--foreground:oklch(0.96 0.004 250);--card:oklch(0.2 0.008 255);--card-foreground:oklch(0.96 0.004 250);--popover:oklch(0.22 0.008 255);--popover-foreground:oklch(0.96 0.004 250);--primary:oklch(0.96 0.004 250);--primary-foreground:oklch(0.2 0.01 255);--secondary:oklch(0.25 0.009 255);--secondary-foreground:oklch(0.96 0.004 250);--muted:oklch(0.24 0.009 255);--muted-foreground:oklch(0.72 0.012 250);--accent:oklch(0.27 0.01 255);--accent-foreground:oklch(0.96 0.004 250);--border:oklch(1 0 0 / 11%);--input:oklch(1 0 0 / 15%);--ring:oklch(0.7 0.17 255);--surface:oklch(0.18 0.007 255);--chart-1:oklch(0.72 0.15 255);--ledger-on-accent:oklch(0.16 0.01 255);--chart-2:oklch(0.76 0.16 160);--chart-3:oklch(0.83 0.15 80);--chart-4:oklch(0.72 0.17 305);--chart-5:oklch(0.76 0.16 25)}
`

const links: { key: LedgerPage; label: string }[] = [
  { key: "home", label: "Overview" },
  { key: "pricing", label: "Pricing" },
  { key: "changelog", label: "What’s new" },
  { key: "support", label: "Support" },
]

type LedgerShellProps = React.ComponentProps<"div"> & {
  /** The page being shown, so its nav link is marked current. */
  page: LedgerPage
  /** Override where pages live (used by previews). */
  hrefs?: Partial<LedgerHrefs>
}

/** Ledger's frame: a frosted, centred header like a Mac app’s site, and a quiet footer. Wrap every Ledger page in it. */
function LedgerShell({ page, hrefs: overrides, className, style, children, ...props }: LedgerShellProps) {
  const hrefs = { ...defaultHrefs, ...overrides }
  const [open, setOpen] = React.useState(false)
  return (
    <div
      data-slot="ledger"
      className={cn("ledger-theme bg-background text-foreground relative min-h-dvh overflow-x-clip antialiased", ledgerSerif.variable, ledgerSans.variable, className)}
      style={{ fontFamily: "-apple-system, BlinkMacSystemFont, 'SF Pro Text', var(--ledger-sans), ui-sans-serif, system-ui, sans-serif", ...style }}
      {...props}
    >
      <style>{ledgerCss}</style>
      <header className="bg-background/72 sticky top-0 z-40 border-b backdrop-blur-xl backdrop-saturate-150">
        <div className="mx-auto flex h-12 max-w-6xl items-center justify-between px-4 sm:px-6">
          <a href={hrefs.home} className="focus-visible:ring-ring/50 flex items-center gap-2 rounded-md text-sm font-semibold outline-none focus-visible:ring-[3px]">
            <AppIcon size={22} tone="blue"><span className="text-[11px] font-bold">L</span></AppIcon> Ledger
          </a>
          <nav aria-label="Main" className="hidden items-center gap-7 md:flex">
            {links.map((l) => (
              <a key={l.key} href={hrefs[l.key]} aria-current={page === l.key ? "page" : undefined} className="text-muted-foreground hover:text-foreground aria-[current=page]:text-foreground focus-visible:ring-ring/50 rounded text-[13px] outline-none transition-colors focus-visible:ring-[3px]">
                {l.label}
              </a>
            ))}
          </nav>
          <div className="flex items-center gap-2">
            <a href={hrefs.download} className="bg-chart-1 focus-visible:ring-ring/50 inline-flex h-7 items-center rounded-full px-3.5 text-[13px] font-medium text-[var(--ledger-on-accent)] outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">Download</a>
            <button type="button" aria-label={open ? "Close menu" : "Open menu"} aria-expanded={open} aria-controls="ledger-mobile-menu" onClick={() => setOpen((v) => !v)} className="text-muted-foreground focus-visible:ring-ring/50 inline-flex size-8 items-center justify-center rounded-md outline-none focus-visible:ring-[3px] md:hidden">
              {open ? <X className="size-5" aria-hidden="true" /> : <Menu className="size-5" aria-hidden="true" />}
            </button>
          </div>
        </div>
        {open && (
          <nav id="ledger-mobile-menu" aria-label="Mobile" className="border-t px-4 py-2 md:hidden">
            {links.map((l) => <a key={l.key} href={hrefs[l.key]} className="hover:bg-accent block rounded-lg px-3 py-2.5 text-sm">{l.label}</a>)}
          </nav>
        )}
      </header>
      {children}
      <footer className="bg-surface border-t">
        <div className="mx-auto max-w-6xl px-4 py-12 sm:px-6">
          <div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
            {[
              { title: "Ledger", items: [["Overview", "home"], ["What’s new", "changelog"], ["Pricing", "pricing"], ["Download", "download"]] },
              { title: "Support", items: [["Help center", "support"], ["Keyboard shortcuts", "support"], ["Contact us", "support"], ["System status", "support"]] },
              { title: "Company", items: [["About", "home"], ["Press kit", "home"], ["Privacy", "home"], ["Terms", "home"]] },
              { title: "Elsewhere", items: [["Mastodon", "home"], ["GitHub", "home"], ["RSS", "changelog"], ["Newsletter", "home"]] },
            ].map((c) => (
              <div key={c.title}>
                <h2 className="text-xs font-semibold">{c.title}</h2>
                <ul className="mt-3 space-y-2">
                  {c.items.map(([label, key]) => <li key={label}><a href={hrefs[key as LedgerPage]} className="text-muted-foreground hover:text-foreground text-xs transition-colors">{label}</a></li>)}
                </ul>
              </div>
            ))}
          </div>
          <p className="text-muted-foreground mt-10 border-t pt-5 text-xs text-pretty">Ledger requires macOS 13 Ventura or later. Apple, Mac, macOS and iCloud are trademarks of Apple Inc. © 2026 Ledger Software, made in Portland.</p>
        </div>
      </footer>
    </div>
  )
}

/** A heading with one italic serif word, the template's signature. Wrap the word to emphasise in `<em>`. */
function LedgerHeading({ className, children, as: Tag = "h2", ...props }: React.ComponentProps<"h2"> & { as?: "h1" | "h2" | "h3" }) {
  return (
    <Tag className={cn("font-semibold tracking-[-0.035em] text-balance [&_em]:font-normal [&_em]:tracking-[-0.02em] [&_em]:[font-family:var(--ledger-serif),ui-serif,Georgia,serif]", className)} {...props}>
      {children}
    </Tag>
  )
}

export { LedgerHeading, LedgerShell, defaultHrefs as ledgerDefaultHrefs, type LedgerHrefs, type LedgerPage, type LedgerShellProps }
```

### `registry/ballmac/components/templates/ledger/ledger-home.tsx`

```tsx
// Ballmac UI: Ledger home page. https://ui.ballmac.com/templates/template-ledger
"use client"

import * as React from "react"
import { ArrowDownToLine, Cloud, Laptop, Lock, ShieldCheck, Star, Zap } from "lucide-react"

import { Devices1 } from "@/components/ballmac/blocks/devices-1/devices-1"
import { Features7 } from "@/components/ballmac/blocks/features-7/features-7"
import { Pricing4 } from "@/components/ballmac/blocks/pricing-4/pricing-4"
import { Showcase1 } from "@/components/ballmac/blocks/showcase-1/showcase-1"
import { BlurFade } from "@/components/ballmac/blur-fade"
import { Kbd } from "@/components/ballmac/kbd"
import { AppIcon } from "@/components/ballmac/mac-icons"
import { LedgerHeading, LedgerShell, type LedgerHrefs } from "@/components/ballmac/templates/ledger/ledger-theme"
import { cn } from "@/lib/utils"

const press = [
  { outlet: "Mac App Store", quote: "Editor’s Choice" },
  { outlet: "MacStories", quote: "The calmest way to run a business." },
  { outlet: "Six Colors", quote: "A Mac app that feels like one." },
  { outlet: "Daring Fireball", quote: "Fast, plain and correct." },
]

const reviews = [
  { quote: "I closed the month in an afternoon. The keyboard shortcuts alone paid for the license.", name: "Hannah Okafor", role: "Freelance designer", tone: "bg-chart-1/20" },
  { quote: "It’s the first accounting app that didn’t make me feel like I needed an accountant to use it.", name: "Marcus Lindqvist", role: "Founder, Studio Fjord", tone: "bg-chart-2/20" },
  { quote: "Works offline on the train, syncs when I’m home. Exactly how software should behave.", name: "Priya Raman", role: "Independent consultant", tone: "bg-chart-4/20" },
]

const card = "bg-card rounded-3xl border p-7 shadow-[0_1px_2px_0_rgb(0_0_0/0.04)]"

type LedgerHomeProps = React.ComponentProps<"div"> & { hrefs?: Partial<LedgerHrefs> }

/** The Ledger home page: an interactive Mac desktop, a menu-bar tour, a feature bento, devices, reviews and pricing. */
function LedgerHome({ hrefs, ...props }: LedgerHomeProps) {
  return (
    <LedgerShell page="home" hrefs={hrefs} {...props}>
      <main>
        <section className="px-4 pt-16 text-center sm:px-6 sm:pt-24">
          <BlurFade>
            <p className="text-chart-1 text-sm font-semibold">Ledger 2.4 · Now with approvals</p>
            <LedgerHeading as="h1" className="mx-auto mt-4 max-w-3xl text-5xl leading-[1.02] sm:text-7xl">
              Your books, kept <em>beautifully</em>.
            </LedgerHeading>
            <p className="text-muted-foreground mx-auto mt-6 max-w-xl text-lg text-pretty sm:text-xl">
              Invoices, expenses and reports in a native Mac app that’s fast, private and a pleasure to open on a Monday.
            </p>
            <div className="mt-9 flex flex-col items-center justify-center gap-3 sm:flex-row sm:gap-5">
              <a href={hrefs?.download ?? "/ledger/download"} className="bg-chart-1 focus-visible:ring-ring/50 inline-flex h-12 items-center gap-2 rounded-full px-6 font-medium text-[var(--ledger-on-accent)] outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">
                <ArrowDownToLine className="size-4" aria-hidden="true" /> Download for Mac
              </a>
              <a href={hrefs?.pricing ?? "/ledger/pricing"} className="text-chart-1 focus-visible:ring-ring/50 rounded-md font-medium underline-offset-4 outline-none hover:underline focus-visible:ring-[3px]">
                See pricing ›
              </a>
            </div>
            <p className="text-muted-foreground mt-5 text-xs">Free for 14 days · macOS 13 or later · Apple silicon and Intel</p>
          </BlurFade>
          <BlurFade className="mx-auto mt-14 max-w-6xl text-start" delay={0.12}>
            <Showcase1 app="Ledger" height="38rem" className="shadow-[0_40px_100px_-30px_rgb(0_0_0/0.45)]" />
          </BlurFade>
        </section>

        <section aria-label="Press" className="mx-auto max-w-6xl px-4 py-16 sm:px-6">
          <ul className="grid gap-px overflow-hidden rounded-2xl border bg-border sm:grid-cols-2 lg:grid-cols-4">
            {press.map((p) => (
              <li key={p.outlet} className="bg-card p-6 text-center">
                <p className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">{p.outlet}</p>
                <p className="mt-2 text-sm font-medium text-pretty [font-family:var(--ledger-serif),ui-serif,Georgia,serif] text-lg italic">“{p.quote}”</p>
              </li>
            ))}
          </ul>
        </section>

        <Features7 title="Always one keystroke away." />

        <section className="mx-auto max-w-6xl px-4 py-20 sm:px-6">
          <div className="mx-auto max-w-2xl text-center">
            <LedgerHeading className="text-4xl sm:text-5xl">A real Mac app, <em>all the way down</em>.</LedgerHeading>
            <p className="text-muted-foreground mt-4 text-lg text-pretty">Native, not wrapped. It uses the parts of macOS you already know.</p>
          </div>
          <div className="mt-12 grid gap-4 md:grid-cols-6">
            <div className={cn(card, "md:col-span-3")}>
              <Zap className="text-chart-3 size-6" aria-hidden="true" />
              <h3 className="mt-5 text-xl font-semibold tracking-tight">Keyboard first</h3>
              <p className="text-muted-foreground mt-2 text-pretty">Everything has a shortcut. Learn four and you rarely touch the mouse.</p>
              <ul className="mt-6 space-y-2.5 text-sm">
                {[["New invoice", ["⌘", "N"]], ["Jump to customer", ["⌘", "K"]], ["Mark as paid", ["⌃", "⌘", "P"]], ["Export report", ["⇧", "⌘", "E"]]].map(([label, keys]) => (
                  <li key={label as string} className="flex items-center justify-between">
                    {label as string}
                    <span className="flex gap-1">{(keys as string[]).map((k) => <Kbd key={k}>{k}</Kbd>)}</span>
                  </li>
                ))}
              </ul>
            </div>
            <div className={cn(card, "md:col-span-3")}>
              <Cloud className="text-chart-1 size-6" aria-hidden="true" />
              <h3 className="mt-5 text-xl font-semibold tracking-tight">Local first, iCloud second</h3>
              <p className="text-muted-foreground mt-2 text-pretty">Your data lives on your Mac and works with no connection. Sync across devices with your own iCloud.</p>
              <div className="bg-secondary/60 mt-6 flex items-center justify-between gap-3 rounded-2xl px-5 py-4 text-sm">
                <span className="flex items-center gap-2"><Laptop className="size-4" aria-hidden="true" /> MacBook Pro</span>
                <span className="text-muted-foreground flex-1 border-t border-dashed" aria-hidden="true" />
                <span className="text-chart-2 flex items-center gap-1.5 text-xs font-medium"><span className="bg-chart-2 size-1.5 rounded-full" aria-hidden="true" /> Synced just now</span>
                <span className="text-muted-foreground flex-1 border-t border-dashed" aria-hidden="true" />
                <span className="flex items-center gap-2"><Cloud className="size-4" aria-hidden="true" /> iCloud</span>
              </div>
            </div>
            <div className={cn(card, "md:col-span-2")}>
              <Lock className="text-chart-4 size-6" aria-hidden="true" />
              <h3 className="mt-5 text-xl font-semibold tracking-tight">Private by design</h3>
              <p className="text-muted-foreground mt-2 text-pretty">No account, no analytics, no server that holds your books.</p>
              <p className="mt-6 text-5xl font-semibold tracking-[-0.04em]">0</p>
              <p className="text-muted-foreground text-sm">trackers in the app</p>
            </div>
            <div className={cn(card, "md:col-span-4")}>
              <ShieldCheck className="text-chart-2 size-6" aria-hidden="true" />
              <h3 className="mt-5 text-xl font-semibold tracking-tight">Shortcuts and AppleScript</h3>
              <p className="text-muted-foreground mt-2 max-w-md text-pretty">Create an invoice from a calendar event, or mark paid from a Stripe webhook. Ledger speaks the Mac’s automation language.</p>
              <pre className="bg-secondary/60 mt-6 overflow-x-auto rounded-2xl p-4 font-mono text-[13px] leading-6" tabIndex={0}>{`tell application "Ledger"
  set inv to make new invoice
  set customer of inv to "Northwind"
  mark inv as paid
end tell`}</pre>
            </div>
          </div>
        </section>

        <Devices1 title="Mac first. Everywhere else, too." />

        <section aria-labelledby="ledger-reviews" className="mx-auto max-w-6xl px-4 py-20 sm:px-6">
          <h2 id="ledger-reviews" className="sr-only">Reviews</h2>
          <ul className="grid gap-4 md:grid-cols-3">
            {reviews.map((r) => (
              <li key={r.name} className={cn(card, "flex flex-col")}>
                <div className="text-chart-3 flex gap-0.5" role="img" aria-label="5 out of 5 stars">{Array.from({ length: 5 }, (_, i) => <Star key={i} className="size-4 fill-current" aria-hidden="true" />)}</div>
                <blockquote className="mt-4 flex-1 text-[17px] leading-relaxed text-pretty">“{r.quote}”</blockquote>
                <footer className="mt-6 flex items-center gap-3 text-sm">
                  <span className={cn("flex size-9 items-center justify-center rounded-full text-xs font-semibold", r.tone)} aria-hidden="true">{r.name.split(" ").map((w) => w[0]).join("")}</span>
                  <span><span className="block font-medium">{r.name}</span><span className="text-muted-foreground">{r.role}</span></span>
                </footer>
              </li>
            ))}
          </ul>
        </section>

        <Pricing4 title="Buy it once, or stay current." />

        <section className="px-4 pb-24 text-center sm:px-6">
          <AppIcon size={88} tone="blue" className="mx-auto"><span className="text-4xl font-bold">L</span></AppIcon>
          <LedgerHeading className="mx-auto mt-8 max-w-xl text-4xl sm:text-5xl">Get your Monday <em>back</em>.</LedgerHeading>
          <p className="text-muted-foreground mx-auto mt-4 max-w-md text-lg text-pretty">Download Ledger and try everything free for 14 days.</p>
          <a href={hrefs?.download ?? "/ledger/download"} className="bg-chart-1 focus-visible:ring-ring/50 mt-8 inline-flex h-12 items-center gap-2 rounded-full px-6 font-medium text-[var(--ledger-on-accent)] outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]">
            <ArrowDownToLine className="size-4" aria-hidden="true" /> Download for Mac
          </a>
        </section>
      </main>
    </LedgerShell>
  )
}

export { LedgerHome, type LedgerHomeProps }
```

### `registry/ballmac/components/templates/ledger/ledger-download.tsx`

```tsx
// Ballmac UI: Ledger download page. https://ui.ballmac.com/templates/template-ledger
import * as React from "react"
import { Apple, Cpu, HardDrive, ShieldCheck } from "lucide-react"

import { Download1 } from "@/components/ballmac/blocks/download-1/download-1"
import { LedgerHeading, LedgerShell, type LedgerHrefs } from "@/components/ballmac/templates/ledger/ledger-theme"

const specs = [
  { icon: Apple, title: "macOS 13 Ventura or later", text: "Runs on every Mac that supports it, including the 2018 MacBook Air." },
  { icon: Cpu, title: "Apple silicon and Intel", text: "A universal app, tuned for M-series chips and still quick on Intel." },
  { icon: HardDrive, title: "58 MB to download", text: "Under 150 MB installed. Your books are a single file you can back up." },
  { icon: ShieldCheck, title: "Signed and notarized", text: "Checked by Apple and by us. SHA-256 checksums are published with every release." },
]

type LedgerDownloadProps = React.ComponentProps<"div"> & { hrefs?: Partial<LedgerHrefs> }

/** Ledger download: the download block, the system requirements and a note on updates. */
function LedgerDownload({ hrefs, ...props }: LedgerDownloadProps) {
  return (
    <LedgerShell page="home" hrefs={hrefs} {...props}>
      <main>
        <div className="mx-auto max-w-6xl px-4 pt-16 text-center sm:px-6 sm:pt-20">
          <LedgerHeading as="h1" className="text-4xl sm:text-6xl">Download <em>Ledger</em>.</LedgerHeading>
          <p className="text-muted-foreground mx-auto mt-4 max-w-lg text-lg text-pretty">Free for 14 days. You only need a license if you keep using it.</p>
        </div>
        <Download1 app="Ledger" />
        <section aria-labelledby="ledger-specs" className="mx-auto max-w-6xl px-4 pb-24 sm:px-6">
          <h2 id="ledger-specs" className="text-2xl font-semibold tracking-[-0.03em]">Before you download</h2>
          <ul className="mt-6 grid gap-4 sm:grid-cols-2">
            {specs.map((s) => (
              <li key={s.title} className="bg-card flex gap-4 rounded-2xl border p-5">
                <span className="bg-chart-1/12 text-chart-1 flex size-10 shrink-0 items-center justify-center rounded-xl"><s.icon className="size-5" aria-hidden="true" /></span>
                <div><h3 className="font-medium">{s.title}</h3><p className="text-muted-foreground mt-1 text-sm text-pretty">{s.text}</p></div>
              </li>
            ))}
          </ul>
          <p className="text-muted-foreground mt-8 text-sm text-pretty">Ledger updates itself in the background and asks before restarting. Need an older version? <a href={hrefs?.changelog ?? "/ledger/changelog"} className="text-chart-1 underline underline-offset-4">Browse every release</a>.</p>
        </section>
      </main>
    </LedgerShell>
  )
}

export { LedgerDownload, type LedgerDownloadProps }
```

### `registry/ballmac/components/templates/ledger/ledger-pricing.tsx`

```tsx
// Ballmac UI: Ledger pricing page. https://ui.ballmac.com/templates/template-ledger
import * as React from "react"
import { Check, Minus } from "lucide-react"

import { Faq2 } from "@/components/ballmac/blocks/faq-2/faq-2"
import { Pricing4 } from "@/components/ballmac/blocks/pricing-4/pricing-4"
import { LedgerHeading, LedgerShell, type LedgerHrefs } from "@/components/ballmac/templates/ledger/ledger-theme"

const rows: [string, boolean | string, boolean | string][] = [
  ["Invoices and expenses", "Up to 10 a month", "Unlimited"],
  ["Reports and exports", "Basic", "Full, with CSV and PDF"],
  ["iCloud sync across Macs", false, true],
  ["Approvals and menu bar capture", false, true],
  ["Shortcuts and AppleScript", false, true],
  ["Custom invoice templates", false, true],
  ["Updates", "Bug fixes", "All of this major version"],
]

function Cell({ v }: { v: boolean | string }) {
  if (typeof v === "string") return <span className="text-sm">{v}</span>
  return v ? <Check className="text-chart-2 mx-auto size-4.5" role="img" aria-label="Included" /> : <Minus className="text-muted-foreground mx-auto size-4" role="img" aria-label="Not included" />
}

type LedgerPricingProps = React.ComponentProps<"div"> & { hrefs?: Partial<LedgerHrefs> }

/** Ledger pricing: buy once or subscribe with the cost chart, a free-versus-licensed table and questions. */
function LedgerPricing({ hrefs, ...props }: LedgerPricingProps) {
  return (
    <LedgerShell page="pricing" hrefs={hrefs} {...props}>
      <main>
        <div className="px-4 pt-16 text-center sm:px-6 sm:pt-20">
          <LedgerHeading as="h1" className="mx-auto max-w-2xl text-4xl sm:text-6xl">Honest pricing for <em>honest</em> software.</LedgerHeading>
        </div>
        <Pricing4 title="Pick how you like to pay." description="Buy a license and keep it forever, or subscribe if you’d rather always have the latest. The chart shows exactly when each one pays off." />
        <section aria-labelledby="ledger-compare" className="mx-auto max-w-3xl px-4 pb-20 sm:px-6">
          <h2 id="ledger-compare" className="text-2xl font-semibold tracking-[-0.03em]">Free trial or licensed</h2>
          <div tabIndex={0} role="region" aria-label="Trial compared with a license" className="bg-card focus-visible:ring-ring/50 mt-6 overflow-x-auto rounded-2xl border outline-none focus-visible:ring-[3px]">
            <table className="w-full min-w-[30rem] text-start">
              <caption className="sr-only">Trial and licensed features</caption>
              <thead><tr className="border-b text-sm"><th scope="col" className="p-4 font-medium"><span className="sr-only">Feature</span></th><th scope="col" className="p-4 text-center font-medium">14-day trial</th><th scope="col" className="p-4 text-center font-medium">Licensed</th></tr></thead>
              <tbody>
                {rows.map(([f, a, b]) => (
                  <tr key={f} className="border-b last:border-b-0"><th scope="row" className="p-4 text-sm font-normal">{f}</th><td className="p-4 text-center"><Cell v={a} /></td><td className="bg-chart-1/5 p-4 text-center"><Cell v={b} /></td></tr>
                ))}
              </tbody>
            </table>
          </div>
        </section>
        <Faq2
          title="Pricing questions"
          description="If yours isn’t here, we answer email within a day."
          categories={[
            { name: "Licenses", questions: [
              { question: "Is a license really forever?", answer: "Yes. A license works on the version you bought and every update in that major version, including all of 2.x. A new major version is a paid upgrade at a discount." },
              { question: "Can I move my license to another Mac?", answer: "Deactivate it from Settings on the old Mac and activate it on the new one. There is no limit on moves, only on how many Macs use it at once." },
            ] },
            { name: "Subscriptions", questions: [
              { question: "What happens if I cancel?", answer: "Ledger keeps opening your books in read-only mode, and you can export everything. You can resubscribe any time." },
              { question: "Is there a student or nonprofit discount?", answer: "Yes, 50 percent off for students, teachers and registered nonprofits. Write to us from your school or organisation address." },
            ] },
          ]}
          support={{ label: "Email support", href: "#", text: "Still unsure which is right for you?" }}
        />
      </main>
    </LedgerShell>
  )
}

export { LedgerPricing, type LedgerPricingProps }
```

### `registry/ballmac/components/templates/ledger/ledger-changelog.tsx`

```tsx
// Ballmac UI: Ledger changelog page. https://ui.ballmac.com/templates/template-ledger
import * as React from "react"

import { Changelog1 } from "@/components/ballmac/blocks/changelog-1/changelog-1"
import { LedgerHeading, LedgerShell, type LedgerHrefs } from "@/components/ballmac/templates/ledger/ledger-theme"

type LedgerChangelogProps = React.ComponentProps<"div"> & { hrefs?: Partial<LedgerHrefs> }

/** Ledger changelog: release notes with a generated cover on headline releases. */
function LedgerChangelog({ hrefs, ...props }: LedgerChangelogProps) {
  return (
    <LedgerShell page="changelog" hrefs={hrefs} {...props}>
      <main>
        <div className="px-4 pt-16 text-center sm:px-6 sm:pt-20">
          <LedgerHeading as="h1" className="text-4xl sm:text-6xl">What’s <em>new</em>.</LedgerHeading>
        </div>
        <Changelog1 title="Every release, in plain language." description="We ship every few weeks. Here is what changed and why." />
      </main>
    </LedgerShell>
  )
}

export { LedgerChangelog, type LedgerChangelogProps }
```

### `registry/ballmac/components/templates/ledger/ledger-support.tsx`

```tsx
// Ballmac UI: Ledger support page. https://ui.ballmac.com/templates/template-ledger
"use client"

import * as React from "react"
import { BookOpen, CreditCard, Keyboard, LifeBuoy, Mail, Search, Wrench } from "lucide-react"

import { Kbd } from "@/components/ballmac/kbd"
import { LedgerHeading, LedgerShell, type LedgerHrefs } from "@/components/ballmac/templates/ledger/ledger-theme"
import { cn } from "@/lib/utils"

const topics = [
  { icon: BookOpen, title: "Getting started", text: "Import your data, add a customer and send your first invoice.", articles: ["Import from a spreadsheet", "Your first invoice", "Set up iCloud sync"] },
  { icon: CreditCard, title: "Licenses and billing", text: "Activate, move or recover a license, and manage a subscription.", articles: ["Activate a license", "Move to a new Mac", "Update a payment method"] },
  { icon: Wrench, title: "Troubleshooting", text: "Fix sync conflicts, a stuck export or an app that won’t open.", articles: ["Sync says “paused”", "Rebuild the index", "Reset preferences"] },
  { icon: Keyboard, title: "Shortcuts and automation", text: "Every keyboard shortcut and a few AppleScript recipes.", articles: ["All keyboard shortcuts", "Shortcuts app actions", "AppleScript dictionary"] },
]

const shortcuts: [string, string[]][] = [
  ["New invoice", ["⌘", "N"]],
  ["Quick open", ["⌘", "K"]],
  ["Mark as paid", ["⌃", "⌘", "P"]],
  ["Export report", ["⇧", "⌘", "E"]],
  ["Toggle sidebar", ["⌥", "⌘", "S"]],
  ["Capture expense", ["⌃", "⌥", "N"]],
]

type LedgerSupportProps = React.ComponentProps<"div"> & { hrefs?: Partial<LedgerHrefs> }

/** Ledger support: a search field that filters help topics, shortcut cheat sheet and contact options. */
function LedgerSupport({ hrefs, ...props }: LedgerSupportProps) {
  const [query, setQuery] = React.useState("")
  const q = query.trim().toLowerCase()
  const shown = q ? topics.filter((t) => [t.title, t.text, ...t.articles].join(" ").toLowerCase().includes(q)) : topics
  return (
    <LedgerShell page="support" hrefs={hrefs} {...props}>
      <main>
        <section className="px-4 pt-16 text-center sm:px-6 sm:pt-20">
          <LedgerHeading as="h1" className="text-4xl sm:text-6xl">How can we <em>help</em>?</LedgerHeading>
          <form role="search" onSubmit={(e) => e.preventDefault()} className="relative mx-auto mt-8 max-w-xl">
            <label htmlFor="ledger-search" className="sr-only">Search help articles</label>
            <Search className="text-muted-foreground pointer-events-none absolute top-1/2 start-4 size-5 -translate-y-1/2" aria-hidden="true" />
            <input id="ledger-search" type="search" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search “license”, “sync”, “export”…" className="bg-card focus-visible:ring-ring/50 placeholder:text-muted-foreground h-13 w-full rounded-full border pe-5 ps-12 text-base shadow-sm outline-none focus-visible:ring-[3px]" />
          </form>
          <p className="text-muted-foreground mt-3 text-sm" role="status">{q ? `${shown.length} ${shown.length === 1 ? "topic" : "topics"} match “${query}”` : "Most questions are answered in under a minute."}</p>
        </section>

        <section aria-label="Help topics" className="mx-auto max-w-6xl px-4 py-12 sm:px-6">
          {shown.length === 0 ? (
            <div className="bg-card rounded-3xl border p-10 text-center"><p className="font-medium">Nothing found</p><p className="text-muted-foreground mt-1 text-sm">Try a shorter word, or write to us below.</p></div>
          ) : (
            <ul className="grid gap-4 md:grid-cols-2">
              {shown.map((t) => (
                <li key={t.title} className="bg-card rounded-3xl border p-7">
                  <t.icon className="text-chart-1 size-6" aria-hidden="true" />
                  <h2 className="mt-4 text-xl font-semibold tracking-tight">{t.title}</h2>
                  <p className="text-muted-foreground mt-1.5 text-sm text-pretty">{t.text}</p>
                  <ul className="mt-5 divide-y border-t text-sm">
                    {t.articles.map((a) => <li key={a}><a href="#" className="hover:text-chart-1 flex items-center justify-between py-2.5 transition-colors">{a}<span aria-hidden="true">›</span></a></li>)}
                  </ul>
                </li>
              ))}
            </ul>
          )}
        </section>

        <section aria-labelledby="ledger-keys" className="mx-auto max-w-6xl px-4 pb-16 sm:px-6">
          <h2 id="ledger-keys" className="text-2xl font-semibold tracking-[-0.03em]">Shortcuts worth learning</h2>
          <ul className="mt-6 grid gap-px overflow-hidden rounded-2xl border bg-border sm:grid-cols-2 lg:grid-cols-3">
            {shortcuts.map(([label, keys]) => (
              <li key={label} className="bg-card flex items-center justify-between px-5 py-4 text-sm">{label}<span className="flex gap-1">{keys.map((k) => <Kbd key={k}>{k}</Kbd>)}</span></li>
            ))}
          </ul>
        </section>

        <section aria-labelledby="ledger-contact" className="mx-auto max-w-6xl px-4 pb-24 sm:px-6">
          <h2 id="ledger-contact" className="sr-only">Contact</h2>
          <div className="grid gap-4 md:grid-cols-2">
            {[
              { icon: Mail, title: "Email us", text: "A person writes back, usually within a day.", cta: "support@ledger.app" },
              { icon: LifeBuoy, title: "System status", text: "iCloud sync and license servers are running normally.", cta: "All systems normal" },
            ].map((c) => (
              <div key={c.title} className={cn("bg-card flex items-start gap-4 rounded-3xl border p-6")}>
                <span className="bg-chart-1/12 text-chart-1 flex size-11 shrink-0 items-center justify-center rounded-2xl"><c.icon className="size-5" aria-hidden="true" /></span>
                <div><h3 className="font-semibold">{c.title}</h3><p className="text-muted-foreground mt-1 text-sm text-pretty">{c.text}</p><a href="#" className="text-chart-1 mt-3 inline-block text-sm font-medium underline underline-offset-4">{c.cta}</a></div>
              </div>
            ))}
          </div>
        </section>
      </main>
    </LedgerShell>
  )
}

export { LedgerSupport, type LedgerSupportProps }
```

### `registry/ballmac/app/ledger/page.tsx`

```tsx
// Ballmac UI: Ledger template route. https://ui.ballmac.com/templates/template-ledger
import type { Metadata } from "next"

import { LedgerHome } from "@/components/ballmac/templates/ledger/ledger-home"

export const metadata: Metadata = {
  title: "Ledger: Your books, kept beautifully",
  description: "A native Mac app for invoices, expenses and reports.",
}

export default function Page() {
  return <LedgerHome />
}
```

### `registry/ballmac/app/ledger/download/page.tsx`

```tsx
// Ballmac UI: Ledger template route. https://ui.ballmac.com/templates/template-ledger
import type { Metadata } from "next"

import { LedgerDownload } from "@/components/ballmac/templates/ledger/ledger-download"

export const metadata: Metadata = {
  title: "Download Ledger",
  description: "Download Ledger for Mac. Free for 14 days.",
}

export default function Page() {
  return <LedgerDownload />
}
```

### `registry/ballmac/app/ledger/pricing/page.tsx`

```tsx
// Ballmac UI: Ledger template route. https://ui.ballmac.com/templates/template-ledger
import type { Metadata } from "next"

import { LedgerPricing } from "@/components/ballmac/templates/ledger/ledger-pricing"

export const metadata: Metadata = {
  title: "Ledger pricing",
  description: "Buy a license once, or subscribe and always have the latest.",
}

export default function Page() {
  return <LedgerPricing />
}
```

### `registry/ballmac/app/ledger/changelog/page.tsx`

```tsx
// Ballmac UI: Ledger template route. https://ui.ballmac.com/templates/template-ledger
import type { Metadata } from "next"

import { LedgerChangelog } from "@/components/ballmac/templates/ledger/ledger-changelog"

export const metadata: Metadata = {
  title: "What’s new in Ledger",
  description: "Release notes for every version of Ledger.",
}

export default function Page() {
  return <LedgerChangelog />
}
```

### `registry/ballmac/app/ledger/support/page.tsx`

```tsx
// Ballmac UI: Ledger template route. https://ui.ballmac.com/templates/template-ledger
import type { Metadata } from "next"

import { LedgerSupport } from "@/components/ballmac/templates/ledger/ledger-support"

export const metadata: Metadata = {
  title: "Ledger support",
  description: "Help articles, shortcuts and ways to reach us.",
}

export default function Page() {
  return <LedgerSupport />
}
```
