{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "template-northwind",
  "title": "Northwind: B2B SaaS",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A warm, editorial five-page site for a B2B product: serif headlines on cream paper, an interactive product tour with numbered pins, case studies, pricing, an about page and a demo request form.",
  "dependencies": [
    "lucide-react",
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/accordion.json",
    "https://ui.ballmac.com/r/blur-fade.json",
    "https://ui.ballmac.com/r/number-ticker.json",
    "https://ui.ballmac.com/r/segmented-control.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-fonts.ts",
      "content": "// Ballmac UI: Northwind template fonts. https://ui.ballmac.com/templates/template-northwind\nimport { Figtree, Fraunces } from \"next/font/google\"\n\nexport const northwindSerif = Fraunces({ variable: \"--northwind-serif\", subsets: [\"latin\"], axes: [\"opsz\", \"SOFT\"], display: \"swap\", preload: false })\nexport const northwindSans = Figtree({ variable: \"--northwind-sans\", subsets: [\"latin\"], display: \"swap\", preload: false })\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-fonts.ts"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-theme.tsx",
      "content": "// Ballmac UI: Northwind template shell. https://ui.ballmac.com/templates/template-northwind\n\"use client\"\n\nimport * as React from \"react\"\nimport { Menu, X } from \"lucide-react\"\n\nimport { northwindSans, northwindSerif } from \"@/components/ballmac/templates/northwind/northwind-fonts\"\nimport { cn } from \"@/lib/utils\"\n\ntype NorthwindPage = \"home\" | \"pricing\" | \"customers\" | \"about\" | \"contact\"\ntype NorthwindHrefs = Record<NorthwindPage, string>\n\nconst defaultHrefs: NorthwindHrefs = { home: \"/northwind\", pricing: \"/northwind/pricing\", customers: \"/northwind/customers\", about: \"/northwind/about\", contact: \"/northwind/contact\" }\n\n/** Northwind's palette: cream paper, forest ink and one terracotta. Dark mode turns to deep green. */\nconst northwindCss = `\n.northwind-theme{--background:oklch(0.975 0.014 88);--foreground:oklch(0.24 0.035 155);--card:oklch(0.99 0.01 88);--card-foreground:oklch(0.24 0.035 155);--popover:oklch(0.99 0.01 88);--popover-foreground:oklch(0.24 0.035 155);--primary:oklch(0.36 0.075 158);--primary-foreground:oklch(0.975 0.014 88);--secondary:oklch(0.94 0.02 88);--secondary-foreground:oklch(0.24 0.035 155);--muted:oklch(0.94 0.02 88);--muted-foreground:oklch(0.48 0.03 150);--accent:oklch(0.925 0.025 95);--accent-foreground:oklch(0.24 0.035 155);--border:oklch(0.24 0.035 155 / 13%);--input:oklch(0.24 0.035 155 / 18%);--ring:oklch(0.5 0.1 158);--surface:oklch(0.955 0.018 88);--destructive:oklch(0.55 0.2 28);--chart-1:oklch(0.45 0.09 158);--chart-2:oklch(0.5 0.15 45);--chart-3:oklch(0.76 0.13 85);--chart-4:oklch(0.5 0.12 330);--chart-5:oklch(0.52 0.08 240);--radius:0.5rem}\n.dark .northwind-theme{--background:oklch(0.19 0.025 158);--foreground:oklch(0.95 0.015 88);--card:oklch(0.23 0.03 158);--card-foreground:oklch(0.95 0.015 88);--popover:oklch(0.25 0.03 158);--popover-foreground:oklch(0.95 0.015 88);--primary:oklch(0.84 0.09 150);--primary-foreground:oklch(0.2 0.03 158);--secondary:oklch(0.27 0.032 158);--secondary-foreground:oklch(0.95 0.015 88);--muted:oklch(0.26 0.03 158);--muted-foreground:oklch(0.74 0.03 130);--accent:oklch(0.3 0.035 158);--accent-foreground:oklch(0.95 0.015 88);--border:oklch(0.95 0.015 88 / 14%);--input:oklch(0.95 0.015 88 / 18%);--ring:oklch(0.74 0.12 150);--surface:oklch(0.21 0.027 158);--destructive:oklch(0.7 0.18 28);--chart-1:oklch(0.78 0.11 150);--chart-2:oklch(0.76 0.13 50);--chart-3:oklch(0.84 0.12 90);--chart-4:oklch(0.74 0.12 335);--chart-5:oklch(0.76 0.09 240)}\n`\n\nconst links: { key: NorthwindPage; label: string }[] = [\n  { key: \"home\", label: \"Product\" },\n  { key: \"pricing\", label: \"Pricing\" },\n  { key: \"customers\", label: \"Customers\" },\n  { key: \"about\", label: \"About\" },\n]\n\nfunction NorthwindMark({ className }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 32 32\" aria-hidden=\"true\" className={cn(\"size-7\", className)} fill=\"none\">\n      <circle cx=\"16\" cy=\"16\" r=\"14\" fill=\"var(--primary)\" />\n      <path d=\"M16 6 20.5 16 16 26 11.5 16Z\" fill=\"var(--primary-foreground)\" />\n      <path d=\"M16 6v20\" stroke=\"var(--primary)\" strokeWidth=\"1.2\" />\n    </svg>\n  )\n}\n\n/** A serif heading. Wrap one word in `<em>` for the italic accent. */\nfunction NorthwindHeading({ as: Tag = \"h2\", className, ...props }: React.ComponentProps<\"h2\"> & { as?: \"h1\" | \"h2\" | \"h3\" }) {\n  return <Tag className={cn(\"[font-family:var(--northwind-serif),ui-serif,Georgia,serif] font-medium tracking-[-0.025em] text-balance [&_em]:text-chart-2 [&_em]:font-normal\", className)} {...props} />\n}\n\ntype NorthwindShellProps = React.ComponentProps<\"div\"> & {\n  /** The page being shown, so its nav link is marked current. */\n  page: NorthwindPage\n  /** Override where pages live (used by previews). */\n  hrefs?: Partial<NorthwindHrefs>\n}\n\n/** Northwind's frame: cream paper, a serif wordmark header and an editorial footer. Wrap every Northwind page in it. */\nfunction NorthwindShell({ page, hrefs: overrides, className, style, children, ...props }: NorthwindShellProps) {\n  const hrefs = { ...defaultHrefs, ...overrides }\n  const [open, setOpen] = React.useState(false)\n  return (\n    <div\n      data-slot=\"northwind\"\n      className={cn(\"northwind-theme bg-background text-foreground relative min-h-dvh overflow-x-clip\", northwindSerif.variable, northwindSans.variable, className)}\n      style={{ fontFamily: \"var(--northwind-sans), ui-sans-serif, system-ui, sans-serif\", ...style }}\n      {...props}\n    >\n      <style>{northwindCss}</style>\n      <header className=\"bg-background/90 sticky top-0 z-40 border-b backdrop-blur-lg\">\n        <div className=\"mx-auto flex h-16 max-w-6xl items-center justify-between px-4 sm:px-6\">\n          <a href={hrefs.home} className=\"focus-visible:ring-ring/50 flex items-center gap-2.5 rounded-md outline-none focus-visible:ring-[3px]\">\n            <NorthwindMark />\n            <span className=\"text-xl font-medium tracking-tight [font-family:var(--northwind-serif),ui-serif,Georgia,serif]\">Northwind</span>\n          </a>\n          <nav aria-label=\"Main\" className=\"hidden items-center gap-8 md:flex\">\n            {links.map((l) => (\n              <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 aria-[current=page]:underline decoration-chart-2 focus-visible:ring-ring/50 rounded text-[15px] underline-offset-[10px] outline-none transition-colors focus-visible:ring-[3px] aria-[current=page]:decoration-2\">\n                {l.label}\n              </a>\n            ))}\n          </nav>\n          <div className=\"flex items-center gap-2\">\n            <a href={hrefs.contact} aria-current={page === \"contact\" ? \"page\" : undefined} className=\"text-muted-foreground hover:text-foreground hidden px-2 text-[15px] sm:block\">Contact</a>\n            <a href={hrefs.contact} className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 inline-flex h-10 items-center rounded-full px-5 text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">Book a demo</a>\n            <button type=\"button\" aria-label={open ? \"Close menu\" : \"Open menu\"} aria-expanded={open} aria-controls=\"northwind-mobile-menu\" onClick={() => setOpen((v) => !v)} className=\"text-muted-foreground focus-visible:ring-ring/50 inline-flex size-10 items-center justify-center rounded-md outline-none focus-visible:ring-[3px] md:hidden\">\n              {open ? <X className=\"size-5\" aria-hidden=\"true\" /> : <Menu className=\"size-5\" aria-hidden=\"true\" />}\n            </button>\n          </div>\n        </div>\n        {open && (\n          <nav id=\"northwind-mobile-menu\" aria-label=\"Mobile\" className=\"border-t px-4 py-2 md:hidden\">\n            {[...links, { key: \"contact\" as const, label: \"Contact\" }].map((l) => <a key={l.key} href={hrefs[l.key]} className=\"hover:bg-accent block rounded-md px-3 py-3 text-base\">{l.label}</a>)}\n          </nav>\n        )}\n      </header>\n      {children}\n      <footer className=\"bg-primary text-primary-foreground\">\n        <div className=\"mx-auto max-w-6xl px-4 py-16 sm:px-6\">\n          <div className=\"grid gap-12 lg:grid-cols-[1.5fr_repeat(3,1fr)]\">\n            <div>\n              <p className=\"text-3xl leading-tight font-medium tracking-tight [font-family:var(--northwind-serif),ui-serif,Georgia,serif]\">Know where every dollar goes.</p>\n              <a href={hrefs.contact} className=\"bg-primary-foreground text-primary focus-visible:ring-ring mt-6 inline-flex h-11 items-center rounded-full px-6 text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">Book a demo</a>\n            </div>\n            {[\n              { title: \"Product\", items: [[\"Cards\", \"home\"], [\"Approvals\", \"home\"], [\"Reporting\", \"home\"], [\"Pricing\", \"pricing\"]] },\n              { title: \"Company\", items: [[\"About\", \"about\"], [\"Customers\", \"customers\"], [\"Careers\", \"about\"], [\"Contact\", \"contact\"]] },\n              { title: \"Resources\", items: [[\"Guides\", \"home\"], [\"Security\", \"home\"], [\"Status\", \"home\"], [\"Press\", \"about\"]] },\n            ].map((c) => (\n              <div key={c.title}>\n                <h2 className=\"text-sm font-semibold opacity-80\">{c.title}</h2>\n                <ul className=\"mt-4 space-y-3\">\n                  {c.items.map(([label, key]) => <li key={label}><a href={hrefs[key as NorthwindPage]} className=\"text-sm opacity-90 hover:underline\">{label}</a></li>)}\n                </ul>\n              </div>\n            ))}\n          </div>\n          <p className=\"mt-14 border-t border-current/20 pt-6 text-xs opacity-80\">© 2026 Northwind Financial, Inc. Banking services provided by partner banks, Members FDIC.</p>\n        </div>\n      </footer>\n    </div>\n  )\n}\n\nexport { NorthwindHeading, NorthwindMark, NorthwindShell, defaultHrefs as northwindDefaultHrefs, type NorthwindHrefs, type NorthwindPage, type NorthwindShellProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-theme.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-tour.tsx",
      "content": "// Ballmac UI: Northwind product tour. https://ui.ballmac.com/templates/template-northwind\n\"use client\"\n\nimport * as React from \"react\"\nimport { BarChart3, Check, CreditCard, Home, ListChecks, Settings, Wallet } from \"lucide-react\"\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\"\n\nimport { NorthwindHeading } from \"@/components/ballmac/templates/northwind/northwind-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst steps = [\n  { id: \"cards\", title: \"Issue a card in seconds\", text: \"Virtual or physical, with a limit, a merchant lock and an owner. Employees never wait on finance to buy what they need.\" },\n  { id: \"approvals\", title: \"Approve from anywhere\", text: \"Requests reach the right person by policy, with the receipt and context attached. One tap from a phone is enough.\" },\n  { id: \"budgets\", title: \"Budgets that warn early\", text: \"Every team sees what is left as they spend, and finance is told at 80 percent, not after the overrun.\" },\n  { id: \"close\", title: \"Close the month in days\", text: \"Receipts are matched, coded and exported to your ledger as spend happens, so close day stops being a project.\" },\n] as const\n\ntype StepId = (typeof steps)[number][\"id\"]\n\nconst cardRows = [\n  [\"Design tools\", \"•••• 4821\", \"$2,400\", \"Maya R.\"],\n  [\"Team offsite\", \"•••• 1190\", \"$8,000\", \"Dev P.\"],\n  [\"Cloud hosting\", \"•••• 7745\", \"$14,500\", \"Engineering\"],\n]\n\nconst queue = [\n  [\"Figma annual\", \"$1,440\", \"Maya R.\"],\n  [\"Flight to Lisbon\", \"$612\", \"Sam O.\"],\n  [\"Conference pass\", \"$890\", \"Dev P.\"],\n]\n\nconst budgets = [\n  [\"Marketing\", 82, \"bg-chart-2\"],\n  [\"Engineering\", 64, \"bg-chart-1\"],\n  [\"Operations\", 38, \"bg-chart-5\"],\n]\n\nfunction Region({ id, active, children, className }: { id: StepId; active: StepId; children: React.ReactNode; className?: string }) {\n  const on = id === active\n  return (\n    <div\n      data-region={id}\n      data-active={on}\n      className={cn(\"bg-card relative rounded-xl border p-3.5 transition-[box-shadow,opacity,border-color] duration-500 motion-reduce:transition-none\", on ? \"border-chart-2 shadow-[0_0_0_3px_color-mix(in_oklab,var(--chart-2)_22%,transparent)]\" : \"border-dashed\", className)}\n    >\n      {children}\n    </div>\n  )\n}\n\n/** A numbered pin on the mock. It repeats the step list for pointer users, so it is hidden from keyboard and screen readers. */\nfunction Pin({ n, id, active, onSelect }: { n: number; id: StepId; active: StepId; onSelect: (id: StepId) => void }) {\n  const on = id === active\n  return (\n    <button\n      type=\"button\"\n      tabIndex={-1}\n      aria-hidden=\"true\"\n      data-pin={id}\n      onClick={() => onSelect(id)}\n      className={cn(\"absolute -top-3 -start-3 z-10 flex size-7 items-center justify-center rounded-full border-2 text-xs font-semibold shadow-sm outline-none transition-colors\", on ? \"bg-chart-2 border-card text-white\" : \"bg-card text-foreground hover:bg-accent\")}\n    >\n      {n}\n    </button>\n  )\n}\n\n/** An interactive product tour: numbered pins on a mock dashboard, tied to a list of steps. */\nfunction NorthwindTour({ className }: { className?: string }) {\n  const [active, setActive] = React.useState<StepId>(\"cards\")\n  const current = steps.find((s) => s.id === active)!\n  return (\n    <div data-slot=\"northwind-tour\" className={cn(\"grid items-center gap-10 lg:grid-cols-[0.8fr_1.3fr] lg:gap-14\", className)}>\n      <div>\n        <RadioGroupPrimitive.Root value={active} onValueChange={(v) => setActive(v as StepId)} aria-label=\"Product tour steps\" className=\"space-y-1.5\">\n          {steps.map((s, i) => {\n            const on = s.id === active\n            return (\n              <RadioGroupPrimitive.Item\n                key={s.id}\n                value={s.id}\n                className={cn(\"focus-visible:ring-ring/50 group block w-full rounded-xl border border-transparent p-4 text-start outline-none transition-colors focus-visible:ring-[3px]\", on ? \"bg-card border-border shadow-sm\" : \"hover:bg-accent/60\")}\n              >\n                <span className=\"flex items-center gap-3\">\n                  <span className={cn(\"flex size-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold transition-colors\", on ? \"bg-chart-2 text-white\" : \"bg-secondary\")}>{i + 1}</span>\n                  <NorthwindHeading as=\"h3\" className=\"text-xl\">{s.title}</NorthwindHeading>\n                </span>\n                {on && <span className=\"text-muted-foreground mt-2 block ps-10 text-[15px] leading-relaxed text-pretty\">{s.text}</span>}\n              </RadioGroupPrimitive.Item>\n            )\n          })}\n        </RadioGroupPrimitive.Root>\n      </div>\n\n      <div className=\"bg-surface relative rounded-2xl border p-2.5 shadow-[0_30px_70px_-40px_oklch(0.3_0.05_155/0.5)] sm:p-3\">\n        <p className=\"sr-only\" aria-live=\"polite\">Step {steps.indexOf(current) + 1} of {steps.length}: {current.title}. {current.text}</p>\n        <div aria-hidden=\"true\" className=\"@container grid min-w-0 grid-cols-[2.75rem_1fr] gap-2.5 sm:grid-cols-[9rem_1fr]\">\n          <div className=\"bg-card hidden rounded-xl border p-2.5 sm:block\">\n            <p className=\"flex items-center gap-2 text-sm font-semibold\"><span className=\"bg-primary size-5 rounded-full\" />Acme Co</p>\n            <ul className=\"mt-4 space-y-1 text-[13px]\">\n              {[[Home, \"Home\"], [CreditCard, \"Cards\"], [ListChecks, \"Approvals\"], [Wallet, \"Budgets\"], [BarChart3, \"Reports\"], [Settings, \"Settings\"]].map(([Icon, label], i) => {\n                const I = Icon as typeof Home\n                return <li key={label as string} className={cn(\"flex items-center gap-2 rounded-md px-2 py-1.5\", i === 1 ? \"bg-accent font-medium\" : \"text-muted-foreground\")}><I className=\"size-3.5\" />{label as string}</li>\n              })}\n            </ul>\n          </div>\n          <div className=\"bg-card flex flex-col items-center gap-3 rounded-xl border py-3 sm:hidden\">\n            {[Home, CreditCard, ListChecks, Wallet].map((I, i) => <I key={i} className={cn(\"size-4\", i === 1 ? \"text-foreground\" : \"text-muted-foreground\")} />)}\n          </div>\n          <div className=\"grid min-w-0 gap-2.5 @lg:grid-cols-2\">\n            <Region id=\"cards\" active={active} className=\"@lg:col-span-2\">\n              <Pin n={1} id=\"cards\" active={active} onSelect={setActive} />\n              <p className=\"text-xs font-semibold\">Cards</p>\n              <ul className=\"mt-2 divide-y text-xs\">\n                {cardRows.map((r) => (\n                  <li key={r[1]} className=\"grid grid-cols-[1fr_auto] items-center gap-2 py-1.5 @md:grid-cols-[1.2fr_1fr_auto_auto]\">\n                    <span className=\"font-medium\">{r[0]}</span>\n                    <span className=\"text-muted-foreground hidden @md:block\">{r[1]}</span>\n                    <span className=\"text-muted-foreground hidden @md:block\">{r[3]}</span>\n                    <span className=\"tabular-nums\">{r[2]}</span>\n                  </li>\n                ))}\n              </ul>\n            </Region>\n            <Region id=\"approvals\" active={active}>\n              <Pin n={2} id=\"approvals\" active={active} onSelect={setActive} />\n              <p className=\"text-xs font-semibold\">Needs approval <span className=\"bg-chart-2/20 ms-1 rounded-full px-1.5\">3</span></p>\n              <ul className=\"mt-2 space-y-1.5 text-xs\">\n                {queue.map((q) => (\n                  <li key={q[0]} className=\"bg-secondary/70 flex items-center justify-between gap-2 rounded-lg px-2.5 py-1.5\">\n                    <span className=\"min-w-0 truncate\"><span className=\"font-medium\">{q[0]}</span> <span className=\"text-muted-foreground\">{q[2]}</span></span>\n                    <span className=\"flex shrink-0 items-center gap-1.5 tabular-nums\">{q[1]}<Check className=\"text-chart-1 size-3.5\" /></span>\n                  </li>\n                ))}\n              </ul>\n            </Region>\n            <Region id=\"budgets\" active={active}>\n              <Pin n={3} id=\"budgets\" active={active} onSelect={setActive} />\n              <p className=\"text-xs font-semibold\">Budgets this quarter</p>\n              <ul className=\"mt-2 space-y-2.5 text-xs\">\n                {budgets.map(([name, pct, tone]) => (\n                  <li key={name as string}>\n                    <div className=\"flex justify-between\"><span>{name}</span><span className={cn(\"tabular-nums\", (pct as number) > 80 && \"text-chart-2 font-semibold\")}>{pct}%</span></div>\n                    <div className=\"bg-secondary mt-1 h-1.5 rounded-full\"><div className={cn(\"h-full rounded-full\", tone as string)} style={{ width: `${pct}%` }} /></div>\n                  </li>\n                ))}\n              </ul>\n            </Region>\n            <Region id=\"close\" active={active} className=\"@lg:col-span-2\">\n              <Pin n={4} id=\"close\" active={active} onSelect={setActive} />\n              <div className=\"flex items-center justify-between text-xs\">\n                <p className=\"font-semibold\">October close</p>\n                <p className=\"text-muted-foreground\">Day 2 of 3</p>\n              </div>\n              <div className=\"bg-secondary mt-2.5 h-2 rounded-full\"><div className=\"bg-chart-1 h-full w-[88%] rounded-full\" /></div>\n              <p className=\"text-muted-foreground mt-2 text-xs\">412 of 468 receipts matched · 56 waiting on employees</p>\n            </Region>\n          </div>\n        </div>\n      </div>\n    </div>\n  )\n}\n\nexport { NorthwindTour }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-tour.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-home.tsx",
      "content": "// Ballmac UI: Northwind home page. https://ui.ballmac.com/templates/template-northwind\n\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowRight, Check, Quote } from \"lucide-react\"\n\nimport { BlurFade } from \"@/components/ballmac/blur-fade\"\nimport { NumberTicker } from \"@/components/ballmac/number-ticker\"\nimport { NorthwindHeading, NorthwindShell, type NorthwindHrefs } from \"@/components/ballmac/templates/northwind/northwind-theme\"\nimport { NorthwindTour } from \"@/components/ballmac/templates/northwind/northwind-tour\"\nimport { cn } from \"@/lib/utils\"\n\nconst serif = \"[font-family:var(--northwind-serif),ui-serif,Georgia,serif]\"\n\nconst logos = [\"Harlow & Pine\", \"Fernhill\", \"Oakline\", \"Brightwell\", \"Tidewater\", \"Marlow Co\"]\n\nconst before = [\"Receipts in a shared inbox\", \"Spreadsheets for every team’s budget\", \"Cards approved by whoever answers Slack\", \"Nine days to close the month\"]\nconst after = [\"Receipts captured and matched at purchase\", \"Live budgets every team can see\", \"Policy routes each request to the right person\", \"Three days to close the month\"]\n\nfunction Mini({ children, label }: { children: React.ReactNode; label: string }) {\n  return (\n    <div role=\"img\" aria-label={label} className=\"bg-surface rounded-2xl border p-5 sm:p-7\">\n      <div aria-hidden=\"true\">{children}</div>\n    </div>\n  )\n}\n\ntype NorthwindHomeProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<NorthwindHrefs> }\n\n/** The Northwind home page: an editorial hero, an interactive product tour, proof, three product stories and a quote. */\nfunction NorthwindHome({ hrefs, ...props }: NorthwindHomeProps) {\n  const contact = hrefs?.contact ?? \"/northwind/contact\"\n  return (\n    <NorthwindShell page=\"home\" hrefs={hrefs} {...props}>\n      <main>\n        <section className=\"mx-auto max-w-6xl px-4 pt-16 pb-12 sm:px-6 sm:pt-24\">\n          <BlurFade>\n            <p className=\"text-chart-2 text-sm font-semibold tracking-wide uppercase\">Spend management for finance teams</p>\n            <NorthwindHeading as=\"h1\" className=\"mt-5 max-w-4xl text-5xl leading-[1.02] sm:text-7xl lg:text-[5.5rem]\">\n              Spend with <em>clarity</em>, close with calm.\n            </NorthwindHeading>\n            <div className=\"mt-8 flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between\">\n              <p className=\"text-muted-foreground max-w-xl text-lg text-pretty sm:text-xl\">\n                Northwind gives every team the cards, approvals and budgets they need, and gives finance a ledger that is already right on the first of the month.\n              </p>\n              <div className=\"flex flex-col gap-3 sm:flex-row\">\n                <a href={contact} className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 inline-flex h-13 items-center justify-center gap-2 rounded-full px-7 text-base font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">\n                  Book a demo <ArrowRight className=\"size-4 rtl:rotate-180\" aria-hidden=\"true\" />\n                </a>\n                <a href=\"#tour\" className=\"hover:bg-accent focus-visible:ring-ring/50 inline-flex h-13 items-center justify-center rounded-full border px-7 text-base font-medium outline-none transition-colors focus-visible:ring-[3px]\">Take the tour</a>\n              </div>\n            </div>\n          </BlurFade>\n        </section>\n\n        <section id=\"tour\" aria-label=\"Product tour\" className=\"mx-auto max-w-6xl scroll-mt-24 px-4 pb-20 sm:px-6\">\n          <NorthwindTour />\n        </section>\n\n        <section aria-label=\"Customers\" className=\"border-y\">\n          <div className=\"mx-auto max-w-6xl px-4 py-10 sm:px-6\">\n            <p className=\"text-muted-foreground text-center text-sm\">Trusted by 1,400 finance teams</p>\n            <ul className=\"mt-6 grid grid-cols-2 gap-x-6 gap-y-5 text-center sm:grid-cols-3 lg:grid-cols-6\">\n              {logos.map((l) => <li key={l} className={cn(\"text-muted-foreground text-xl font-medium tracking-tight\", serif)}>{l}</li>)}\n            </ul>\n          </div>\n        </section>\n\n        <section aria-label=\"Results\" className=\"mx-auto max-w-6xl px-4 py-20 sm:px-6\">\n          <dl className=\"grid gap-10 sm:grid-cols-3\">\n            {[\n              { v: 62, s: \"%\", l: \"Faster month-end close\", d: 0 },\n              { v: 4.2, s: \"B\", p: \"$\", l: \"Spend managed each year\", d: 1 },\n              { v: 97, s: \"%\", l: \"Receipts matched automatically\", d: 0 },\n            ].map((x) => (\n              <div key={x.l} className=\"border-t-2 border-current pt-5\">\n                <dd className={cn(\"text-6xl font-medium tracking-[-0.04em] tabular-nums sm:text-7xl\", serif)}>{x.p}<NumberTicker value={x.v} format={{ minimumFractionDigits: x.d, maximumFractionDigits: x.d }} />{x.s}</dd>\n                <dt className=\"text-muted-foreground mt-3\">{x.l}</dt>\n              </div>\n            ))}\n          </dl>\n        </section>\n\n        {/* Three stories */}\n        <section className=\"mx-auto max-w-6xl space-y-24 px-4 pb-24 sm:px-6\">\n          <div className=\"grid items-center gap-10 lg:grid-cols-2 lg:gap-16\">\n            <div>\n              <p className=\"text-chart-2 text-sm font-semibold tracking-wide uppercase\">Cards</p>\n              <NorthwindHeading className=\"mt-3 text-4xl sm:text-5xl\">Give every team a card that <em>knows</em> the rules.</NorthwindHeading>\n              <p className=\"text-muted-foreground mt-5 text-lg text-pretty\">Limits, merchants and approval policies travel with the card, so the right purchase just works and the wrong one never leaves the building.</p>\n              <ul className=\"mt-6 space-y-3\">{[\"Virtual cards for every subscription\", \"Merchant and category locks\", \"Instant freeze, from any device\"].map((x) => <li key={x} className=\"flex items-start gap-3\"><Check className=\"text-chart-1 mt-1 size-4 shrink-0\" aria-hidden=\"true\" />{x}</li>)}</ul>\n            </div>\n            <Mini label=\"Three virtual cards with limits\">\n              <div className=\"space-y-3\">\n                {[[\"Design tools\", \"$2,400\", \"bg-chart-1\"], [\"Cloud hosting\", \"$14,500\", \"bg-chart-2\"], [\"Team offsite\", \"$8,000\", \"bg-chart-5\"]].map(([n, a, t]) => (\n                  <div key={n} className=\"bg-card flex items-center gap-4 rounded-xl border p-4\">\n                    <span className={cn(\"h-10 w-14 rounded-md\", t)} />\n                    <div className=\"flex-1\"><p className=\"font-medium\">{n}</p><p className=\"text-muted-foreground text-sm\">Monthly limit</p></div>\n                    <p className=\"font-medium tabular-nums\">{a}</p>\n                  </div>\n                ))}\n              </div>\n            </Mini>\n          </div>\n\n          <div className=\"grid items-center gap-10 lg:grid-cols-2 lg:gap-16\">\n            <Mini label=\"Before and after comparison\">\n              <div className=\"grid gap-4 sm:grid-cols-2\">\n                <div><p className=\"text-muted-foreground text-sm font-semibold\">Before</p><ul className=\"mt-3 space-y-3 text-sm\">{before.map((x) => <li key={x} className=\"text-muted-foreground flex gap-2\"><span aria-hidden=\"true\">×</span>{x}</li>)}</ul></div>\n                <div><p className=\"text-chart-1 text-sm font-semibold\">With Northwind</p><ul className=\"mt-3 space-y-3 text-sm\">{after.map((x) => <li key={x} className=\"flex gap-2\"><Check className=\"text-chart-1 mt-0.5 size-4 shrink-0\" />{x}</li>)}</ul></div>\n              </div>\n            </Mini>\n            <div>\n              <p className=\"text-chart-2 text-sm font-semibold tracking-wide uppercase\">Close</p>\n              <NorthwindHeading className=\"mt-3 text-4xl sm:text-5xl\">The month closes itself, <em>nearly</em>.</NorthwindHeading>\n              <p className=\"text-muted-foreground mt-5 text-lg text-pretty\">Every transaction is matched to a receipt, coded to your chart of accounts and synced to your ledger as it happens. What is left on close day is review, not data entry.</p>\n            </div>\n          </div>\n\n          <div className=\"grid items-center gap-10 lg:grid-cols-2 lg:gap-16\">\n            <div>\n              <p className=\"text-chart-2 text-sm font-semibold tracking-wide uppercase\">Reporting</p>\n              <NorthwindHeading className=\"mt-3 text-4xl sm:text-5xl\">Answers for the board, <em>before</em> they ask.</NorthwindHeading>\n              <p className=\"text-muted-foreground mt-5 text-lg text-pretty\">Spend by team, vendor and project, always current. Build a view once and share it with a link.</p>\n            </div>\n            <Mini label=\"Bar chart of spend by team over six months\">\n              <div className=\"flex h-44 items-end gap-3\">\n                {[48, 56, 52, 70, 64, 82].map((h, i) => <div key={i} className=\"flex flex-1 flex-col justify-end gap-1\"><div className=\"bg-chart-1 rounded-t-md\" style={{ height: `${h * 0.55}%` }} /><div className=\"bg-chart-2 rounded-t-md\" style={{ height: `${h * 0.3}%` }} /></div>)}\n              </div>\n              <div className=\"text-muted-foreground mt-3 flex justify-between text-xs\"><span>May</span><span>Oct</span></div>\n            </Mini>\n          </div>\n        </section>\n\n        <section className=\"bg-primary text-primary-foreground\">\n          <figure className=\"mx-auto max-w-4xl px-4 py-24 text-center sm:px-6\">\n            <Quote className=\"mx-auto size-8 opacity-50 rtl:-scale-x-100\" aria-hidden=\"true\" />\n            <blockquote className={cn(\"mt-6 text-3xl leading-snug tracking-[-0.02em] text-balance sm:text-5xl\", serif)}>\n              “We went from nine days to three to close the books, and nobody on my team has asked me for a receipt since March.”\n            </blockquote>\n            <figcaption className=\"mt-8 text-sm opacity-80\"><span className=\"font-semibold\">Elena Marchetti</span> · Controller, Harlow &amp; Pine</figcaption>\n          </figure>\n        </section>\n\n        <section className=\"mx-auto max-w-6xl px-4 py-24 text-center sm:px-6\">\n          <NorthwindHeading className=\"mx-auto max-w-3xl text-4xl sm:text-6xl\">See it with <em>your</em> numbers.</NorthwindHeading>\n          <p className=\"text-muted-foreground mx-auto mt-5 max-w-lg text-lg text-pretty\">A 30-minute walkthrough, built around how your team spends today.</p>\n          <a href={contact} className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 mt-8 inline-flex h-13 items-center gap-2 rounded-full px-7 text-base font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">Book a demo <ArrowRight className=\"size-4 rtl:rotate-180\" aria-hidden=\"true\" /></a>\n        </section>\n      </main>\n    </NorthwindShell>\n  )\n}\n\nexport { NorthwindHome, type NorthwindHomeProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-home.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-pricing.tsx",
      "content": "// Ballmac UI: Northwind pricing page. https://ui.ballmac.com/templates/template-northwind\n\"use client\"\n\nimport * as React from \"react\"\nimport { Check } from \"lucide-react\"\n\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from \"@/components/ballmac/accordion\"\nimport { SegmentedControl, SegmentedControlItem } from \"@/components/ballmac/segmented-control\"\nimport { NorthwindHeading, NorthwindShell, type NorthwindHrefs } from \"@/components/ballmac/templates/northwind/northwind-theme\"\nimport { cn } from \"@/lib/utils\"\n\ntype Billing = \"annual\" | \"monthly\"\n\nconst plans = [\n  { id: \"starter\", name: \"Starter\", blurb: \"For teams of up to 25 that are moving off spreadsheets.\", price: 8, unit: \"per person / month\", features: [\"Virtual and physical cards\", \"Receipt capture and matching\", \"Basic approval rules\", \"Sync to QuickBooks or Xero\"] },\n  { id: \"growth\", name: \"Growth\", blurb: \"For finance teams that want budgets and policy.\", price: 14, unit: \"per person / month\", featured: true, features: [\"Everything in Starter\", \"Team budgets with alerts\", \"Multi-step approval policies\", \"Bill pay and reimbursements\", \"NetSuite and Sage Intacct sync\"] },\n  { id: \"enterprise\", name: \"Enterprise\", blurb: \"For multiple entities, countries and auditors.\", price: null, unit: \"annual contract\", features: [\"Everything in Growth\", \"Multi-entity and multi-currency\", \"SSO, SCIM and audit exports\", \"Dedicated implementation lead\", \"99.95% uptime SLA\"] },\n] as const\n\nconst faqs = [\n  { q: \"Do I pay for every employee?\", a: \"You pay for people who hold a card or approve spend. Employees who only submit reimbursements are free, so rolling out to the whole company doesn’t change your bill.\" },\n  { q: \"Is there a card fee?\", a: \"No. Cards are free, and we earn a small share of interchange from the card network. That is why we can keep seat prices low.\" },\n  { q: \"How long does setup take?\", a: \"Most teams are issuing cards in a day. Accounting sync and policy setup usually take a week, with a guide at your side.\" },\n  { q: \"What if we outgrow a plan?\", a: \"Change plans any time. We prorate to the day, and your cards, receipts and history carry over untouched.\" },\n]\n\ntype NorthwindPricingProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<NorthwindHrefs> }\n\n/** Northwind pricing: three plans with an annual saving, an included-everywhere list and questions. */\nfunction NorthwindPricing({ hrefs, ...props }: NorthwindPricingProps) {\n  const [billing, setBilling] = React.useState<Billing>(\"annual\")\n  const contact = hrefs?.contact ?? \"/northwind/contact\"\n  return (\n    <NorthwindShell page=\"pricing\" hrefs={hrefs} {...props}>\n      <main>\n        <section className=\"mx-auto max-w-6xl px-4 pt-16 sm:px-6 sm:pt-24\">\n          <div className=\"mx-auto max-w-3xl text-center\">\n            <NorthwindHeading as=\"h1\" className=\"text-5xl leading-[1.04] sm:text-7xl\">Pricing that <em>scales</em> with your team, not your spend.</NorthwindHeading>\n            <p className=\"text-muted-foreground mt-6 text-lg text-pretty\">No percentage of volume, no card fees, no surprise add-ons.</p>\n            <SegmentedControl aria-label=\"Billing period\" value={billing} onValueChange={(v) => setBilling(v as Billing)} className=\"mt-9\">\n              <SegmentedControlItem value=\"annual\">Annual · save 20%</SegmentedControlItem>\n              <SegmentedControlItem value=\"monthly\">Monthly</SegmentedControlItem>\n            </SegmentedControl>\n          </div>\n          <div className=\"mt-14 grid gap-5 lg:grid-cols-3\">\n            {plans.map((p) => {\n              const price = p.price === null ? null : billing === \"annual\" ? Math.round(p.price * 0.8) : p.price\n              const featured = \"featured\" in p && p.featured\n              return (\n                <article key={p.id} className={cn(\"flex flex-col rounded-3xl border p-8\", featured ? \"bg-primary text-primary-foreground border-transparent\" : \"bg-card\")}>\n                  <h2 className=\"text-[26px] font-medium tracking-tight [font-family:var(--northwind-serif),ui-serif,Georgia,serif]\">{p.name}</h2>\n                  <p className={cn(\"mt-2 min-h-12 text-[15px] text-pretty\", featured ? \"opacity-80\" : \"text-muted-foreground\")}>{p.blurb}</p>\n                  <p className=\"mt-6 flex items-baseline gap-2\">\n                    {price === null ? <span className=\"text-5xl font-medium tracking-tight [font-family:var(--northwind-serif),ui-serif,Georgia,serif]\">Let’s talk</span> : (\n                      <>\n                        <span className=\"text-6xl font-medium tracking-[-0.04em] tabular-nums [font-family:var(--northwind-serif),ui-serif,Georgia,serif]\">${price}</span>\n                        <span className={cn(\"text-sm\", featured ? \"opacity-70\" : \"text-muted-foreground\")}>{p.unit}</span>\n                      </>\n                    )}\n                  </p>\n                  <a href={contact} className={cn(\"focus-visible:ring-ring/50 mt-7 inline-flex h-12 items-center justify-center rounded-full text-[15px] font-medium outline-none transition-opacity focus-visible:ring-[3px]\", featured ? \"bg-primary-foreground text-primary hover:opacity-90\" : \"bg-primary text-primary-foreground hover:opacity-90\")}>\n                    {price === null ? \"Contact sales\" : \"Book a demo\"}\n                  </a>\n                  <ul className=\"mt-8 space-y-3.5 border-t border-current/15 pt-7 text-[15px]\">\n                    {p.features.map((f) => <li key={f} className=\"flex items-start gap-3\"><Check className={cn(\"mt-1 size-4 shrink-0\", featured ? \"\" : \"text-chart-1\")} aria-hidden=\"true\" />{f}</li>)}\n                  </ul>\n                </article>\n              )\n            })}\n          </div>\n        </section>\n\n        <section aria-labelledby=\"nw-included\" className=\"mx-auto max-w-6xl px-4 py-24 sm:px-6\">\n          <NorthwindHeading id=\"nw-included\" className=\"max-w-xl text-3xl sm:text-5xl\">Included with every plan.</NorthwindHeading>\n          <ul className=\"mt-10 grid gap-x-10 gap-y-6 sm:grid-cols-2 lg:grid-cols-3\">\n            {[\"Unlimited virtual cards\", \"Real-time spend alerts\", \"Mobile app for iOS and Android\", \"Receipt capture by email and text\", \"CSV and API exports\", \"Bank-level encryption and 2FA\", \"US-based support, weekdays\", \"Free onboarding session\"].map((x) => (\n              <li key={x} className=\"flex items-start gap-3 border-t pt-4\"><Check className=\"text-chart-1 mt-1 size-4 shrink-0\" aria-hidden=\"true\" />{x}</li>\n            ))}\n          </ul>\n        </section>\n\n        <section className=\"bg-surface border-y\" aria-labelledby=\"nw-faq\">\n          <div className=\"mx-auto grid max-w-6xl gap-10 px-4 py-24 sm:px-6 lg:grid-cols-[1fr_1.4fr]\">\n            <NorthwindHeading id=\"nw-faq\" className=\"text-3xl sm:text-5xl\">Questions we hear <em>often</em>.</NorthwindHeading>\n            <Accordion type=\"single\" collapsible>\n              {faqs.map((f) => (\n                <AccordionItem key={f.q} value={f.q}>\n                  <AccordionTrigger className=\"text-lg\">{f.q}</AccordionTrigger>\n                  <AccordionContent className=\"text-muted-foreground text-[15px] text-pretty\">{f.a}</AccordionContent>\n                </AccordionItem>\n              ))}\n            </Accordion>\n          </div>\n        </section>\n      </main>\n    </NorthwindShell>\n  )\n}\n\nexport { NorthwindPricing, type NorthwindPricingProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-pricing.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-customers.tsx",
      "content": "// Ballmac UI: Northwind customers page. https://ui.ballmac.com/templates/template-northwind\nimport * as React from \"react\"\nimport { ArrowUpRight } from \"lucide-react\"\n\nimport { NorthwindHeading, NorthwindShell, type NorthwindHrefs } from \"@/components/ballmac/templates/northwind/northwind-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst serif = \"[font-family:var(--northwind-serif),ui-serif,Georgia,serif]\"\n\nconst stories = [\n  { name: \"Fernhill\", sector: \"Architecture\", stat: \"70%\", note: \"less time chasing receipts\", quote: \"Our partners finally see project costs while the project is still running.\", tone: \"bg-chart-1/15\" },\n  { name: \"Oakline\", sector: \"Logistics\", stat: \"$310k\", note: \"saved on duplicate vendors\", quote: \"We found three vendors we paid under different names within a week.\", tone: \"bg-chart-2/15\" },\n  { name: \"Tidewater\", sector: \"Hospitality\", stat: \"11 days → 4\", note: \"month-end close\", quote: \"Eighteen restaurants, one close process. It used to be eighteen.\", tone: \"bg-chart-5/15\" },\n  { name: \"Marlow Co\", sector: \"Consumer goods\", stat: \"4.9 / 5\", note: \"employee card satisfaction\", quote: \"Nobody has asked to expense a personal card in a year.\", tone: \"bg-chart-4/15\" },\n  { name: \"Brightwell\", sector: \"Healthcare\", stat: \"100%\", note: \"audit-ready receipts\", quote: \"Our auditors asked what we changed. We told them: everything.\", tone: \"bg-chart-3/20\" },\n  { name: \"Quill & Co\", sector: \"Publishing\", stat: \"2 hrs\", note: \"to onboard 60 people\", quote: \"Cards for the whole company were live before lunch.\", tone: \"bg-chart-1/15\" },\n]\n\ntype NorthwindCustomersProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<NorthwindHrefs> }\n\n/** Northwind customers: a featured case study with results, then a grid of shorter stories. */\nfunction NorthwindCustomers({ hrefs, ...props }: NorthwindCustomersProps) {\n  return (\n    <NorthwindShell page=\"customers\" hrefs={hrefs} {...props}>\n      <main>\n        <section className=\"mx-auto max-w-6xl px-4 pt-16 sm:px-6 sm:pt-24\">\n          <p className=\"text-chart-2 text-sm font-semibold tracking-wide uppercase\">Customers</p>\n          <NorthwindHeading as=\"h1\" className=\"mt-4 max-w-4xl text-5xl leading-[1.04] sm:text-7xl\">Finance teams that got their <em>weekends</em> back.</NorthwindHeading>\n        </section>\n\n        <section aria-labelledby=\"nw-featured\" className=\"mx-auto max-w-6xl px-4 py-14 sm:px-6\">\n          <article className=\"bg-primary text-primary-foreground grid overflow-hidden rounded-3xl lg:grid-cols-[1.1fr_1fr]\">\n            <div className=\"p-8 sm:p-12\">\n              <p className=\"text-sm font-semibold opacity-70\">Case study · Professional services</p>\n              <h2 id=\"nw-featured\" className={cn(\"mt-4 text-4xl leading-tight tracking-[-0.02em] text-balance sm:text-5xl\", serif)}>Harlow &amp; Pine closes the books in three days, not nine.</h2>\n              <p className=\"mt-5 max-w-lg text-lg opacity-85 text-pretty\">A 340-person consultancy replaced spreadsheets, a shared inbox and four card programs with one system finance trusts.</p>\n              <a href=\"#\" className=\"bg-primary-foreground text-primary focus-visible:ring-ring mt-8 inline-flex h-12 items-center gap-2 rounded-full px-6 text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">Read the story <ArrowUpRight className=\"size-4 rtl:-scale-x-100\" aria-hidden=\"true\" /></a>\n            </div>\n            <dl className=\"grid grid-cols-2 border-t border-current/20 lg:border-t-0 lg:border-s\">\n              {[[\"9 → 3\", \"days to close\"], [\"97%\", \"receipts matched\"], [\"$1.2M\", \"cash found in dormant subscriptions\"], [\"340\", \"employees, live in 2 weeks\"]].map(([v, l], i) => (\n                <div key={l} className={cn(\"flex flex-col justify-end p-6 sm:p-8\", i % 2 === 0 && \"border-e border-current/20\", i < 2 && \"border-b border-current/20\")}>\n                  <dd className={cn(\"text-4xl tracking-[-0.03em] sm:text-5xl\", serif)}>{v}</dd>\n                  <dt className=\"mt-2 text-sm opacity-75\">{l}</dt>\n                </div>\n              ))}\n            </dl>\n          </article>\n        </section>\n\n        <section aria-labelledby=\"nw-more\" className=\"mx-auto max-w-6xl px-4 pb-24 sm:px-6\">\n          <NorthwindHeading id=\"nw-more\" className=\"text-3xl sm:text-4xl\">More stories</NorthwindHeading>\n          <ul className=\"mt-8 grid gap-5 md:grid-cols-2 lg:grid-cols-3\">\n            {stories.map((s) => (\n              <li key={s.name} className=\"bg-card flex flex-col rounded-2xl border p-7\">\n                <div className=\"flex items-center gap-3\">\n                  <span className={cn(\"flex size-10 items-center justify-center rounded-full text-sm font-semibold\", s.tone)} aria-hidden=\"true\">{s.name[0]}</span>\n                  <div><h3 className=\"font-semibold\">{s.name}</h3><p className=\"text-muted-foreground text-xs\">{s.sector}</p></div>\n                </div>\n                <p className={cn(\"mt-6 text-5xl tracking-[-0.03em]\", serif)}>{s.stat}</p>\n                <p className=\"text-muted-foreground mt-1 text-sm\">{s.note}</p>\n                <blockquote className=\"mt-6 flex-1 border-t pt-5 text-[15px] text-pretty\">“{s.quote}”</blockquote>\n              </li>\n            ))}\n          </ul>\n        </section>\n      </main>\n    </NorthwindShell>\n  )\n}\n\nexport { NorthwindCustomers, type NorthwindCustomersProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-customers.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-about.tsx",
      "content": "// Ballmac UI: Northwind about page. https://ui.ballmac.com/templates/template-northwind\nimport * as React from \"react\"\n\nimport { NorthwindHeading, NorthwindShell, type NorthwindHrefs } from \"@/components/ballmac/templates/northwind/northwind-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst serif = \"[font-family:var(--northwind-serif),ui-serif,Georgia,serif]\"\n\nconst values = [\n  { title: \"Plain beats clever\", text: \"If a customer needs a manual to use a feature, we haven’t finished it.\" },\n  { title: \"Money is personal\", text: \"Behind every transaction is someone trying to do their job. We design for them first.\" },\n  { title: \"Boring is a feature\", text: \"Finance software should be dependable enough to forget about. We aim to be forgotten.\" },\n]\n\nconst timeline = [\n  [\"2019\", \"Two former controllers start Northwind in a spare bedroom in Portland.\"],\n  [\"2021\", \"First 100 customers, and our first real month-end close with no spreadsheets.\"],\n  [\"2023\", \"Launch bill pay and reimbursements. Reach $1B in annual spend managed.\"],\n  [\"2025\", \"Open offices in Toronto and London. 1,000 customers across 14 countries.\"],\n  [\"2026\", \"Today: 1,400 finance teams, 140 people, still answering email ourselves.\"],\n]\n\nconst team = [\n  [\"Ines Alvarado\", \"Co-founder, CEO\", \"bg-chart-1/25\"], [\"Tobias Wren\", \"Co-founder, CTO\", \"bg-chart-2/25\"], [\"Amara Nwosu\", \"VP Finance\", \"bg-chart-5/25\"],\n  [\"Jonas Eklund\", \"Head of Design\", \"bg-chart-4/25\"], [\"Mei Tanaka\", \"Head of Security\", \"bg-chart-3/30\"], [\"Colm Brennan\", \"Head of Support\", \"bg-chart-1/25\"],\n] as const\n\ntype NorthwindAboutProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<NorthwindHrefs> }\n\n/** Northwind about: a long-form mission, values, a timeline and the team. */\nfunction NorthwindAbout({ hrefs, ...props }: NorthwindAboutProps) {\n  return (\n    <NorthwindShell page=\"about\" hrefs={hrefs} {...props}>\n      <main>\n        <section className=\"mx-auto max-w-4xl px-4 pt-16 sm:px-6 sm:pt-24\">\n          <p className=\"text-chart-2 text-sm font-semibold tracking-wide uppercase\">About</p>\n          <NorthwindHeading as=\"h1\" className=\"mt-4 text-5xl leading-[1.04] sm:text-7xl\">We think finance should feel <em>quiet</em>.</NorthwindHeading>\n          <div className=\"text-muted-foreground mt-10 space-y-6 text-xl leading-relaxed text-pretty\">\n            <p>Northwind began with a frustration. We had both run finance teams, and we had both spent the last week of every month chasing people for receipts that were, somewhere, already in an inbox.</p>\n            <p>The tools we used treated spending as a problem to police. We wanted the opposite: software that makes it easy for people to do the right thing, and shows finance what happened without asking.</p>\n            <p className={cn(\"text-foreground text-3xl leading-snug\", serif)}>The best month-end is the one you barely notice.</p>\n          </div>\n        </section>\n\n        <section aria-labelledby=\"nw-values\" className=\"mx-auto max-w-6xl px-4 py-24 sm:px-6\">\n          <NorthwindHeading id=\"nw-values\" className=\"text-3xl sm:text-5xl\">What we believe</NorthwindHeading>\n          <ol className=\"mt-10 grid gap-8 md:grid-cols-3\">\n            {values.map((v, i) => (\n              <li key={v.title} className=\"border-t-2 border-current pt-5\">\n                <span className={cn(\"text-chart-2 text-5xl\", serif)}>{i + 1}</span>\n                <h3 className=\"mt-4 text-xl font-semibold\">{v.title}</h3>\n                <p className=\"text-muted-foreground mt-2 text-pretty\">{v.text}</p>\n              </li>\n            ))}\n          </ol>\n        </section>\n\n        <section aria-labelledby=\"nw-history\" className=\"bg-surface border-y\">\n          <div className=\"mx-auto max-w-4xl px-4 py-24 sm:px-6\">\n            <NorthwindHeading id=\"nw-history\" className=\"text-3xl sm:text-5xl\">Seven years, briefly</NorthwindHeading>\n            <ol className=\"mt-10 divide-y\">\n              {timeline.map(([y, t]) => (\n                <li key={y} className=\"grid gap-2 py-6 sm:grid-cols-[7rem_1fr] sm:gap-8\"><span className={cn(\"text-chart-2 text-3xl\", serif)}>{y}</span><p className=\"text-lg text-pretty\">{t}</p></li>\n              ))}\n            </ol>\n          </div>\n        </section>\n\n        <section aria-labelledby=\"nw-team\" className=\"mx-auto max-w-6xl px-4 py-24 sm:px-6\">\n          <NorthwindHeading id=\"nw-team\" className=\"text-3xl sm:text-5xl\">The people</NorthwindHeading>\n          <ul className=\"mt-10 grid grid-cols-2 gap-6 md:grid-cols-3\">\n            {team.map(([n, r, t]) => (\n              <li key={n}>\n                <div className={cn(\"flex aspect-[4/3] items-end rounded-2xl p-5\", t)} aria-hidden=\"true\"><span className={cn(\"text-5xl tracking-tight\", serif)}>{n.split(\" \").map((w) => w[0]).join(\"\")}</span></div>\n                <h3 className=\"mt-3 font-semibold\">{n}</h3>\n                <p className=\"text-muted-foreground text-sm\">{r}</p>\n              </li>\n            ))}\n          </ul>\n          <div className=\"bg-primary text-primary-foreground mt-16 flex flex-col items-start justify-between gap-6 rounded-3xl p-8 sm:flex-row sm:items-center sm:p-12\">\n            <div><p className={cn(\"text-3xl sm:text-4xl\", serif)}>We’re hiring in Portland, Toronto and London.</p><p className=\"mt-2 opacity-80\">14 open roles across engineering, design and support.</p></div>\n            <a href=\"#\" className=\"bg-primary-foreground text-primary focus-visible:ring-ring inline-flex h-12 shrink-0 items-center rounded-full px-6 text-sm font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px]\">See open roles</a>\n          </div>\n        </section>\n      </main>\n    </NorthwindShell>\n  )\n}\n\nexport { NorthwindAbout, type NorthwindAboutProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-about.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/northwind/northwind-contact.tsx",
      "content": "// Ballmac UI: Northwind contact page. https://ui.ballmac.com/templates/template-northwind\n\"use client\"\n\nimport * as React from \"react\"\nimport { Check, Mail, MapPin, MessageCircle } from \"lucide-react\"\n\nimport { NorthwindHeading, NorthwindShell, type NorthwindHrefs } from \"@/components/ballmac/templates/northwind/northwind-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst serif = \"[font-family:var(--northwind-serif),ui-serif,Georgia,serif]\"\nconst field = \"bg-card focus-visible:ring-ring/50 mt-2 h-12 w-full rounded-xl border px-4 text-base outline-none focus-visible:ring-[3px] aria-[invalid=true]:border-destructive\"\n\ntype Errors = Partial<Record<\"name\" | \"email\" | \"size\", string>>\n\ntype NorthwindContactProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<NorthwindHrefs> }\n\n/** Northwind contact: a demo request form with inline validation, and the other ways to reach the team. */\nfunction NorthwindContact({ hrefs, ...props }: NorthwindContactProps) {\n  const [errors, setErrors] = React.useState<Errors>({})\n  const [sent, setSent] = React.useState<string | null>(null)\n  const [pending, setPending] = React.useState(false)\n  const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n  React.useEffect(() => () => clearTimeout(timer.current), [])\n\n  function submit(e: React.FormEvent<HTMLFormElement>) {\n    e.preventDefault()\n    const form = e.currentTarget\n    const f = new FormData(form)\n    const name = String(f.get(\"name\") ?? \"\").trim()\n    const email = String(f.get(\"email\") ?? \"\").trim()\n    const size = String(f.get(\"size\") ?? \"\")\n    const next: Errors = {}\n    if (!name) next.name = \"Tell us your name.\"\n    if (!/^\\S+@\\S+\\.\\S+$/.test(email)) next.email = \"Enter a work email like you@company.com.\"\n    if (!size) next.size = \"Choose your team size.\"\n    setErrors(next)\n    if (Object.keys(next).length) {\n      const first = ([\"name\", \"email\", \"size\"] as const).find((k) => next[k])\n      ;(form.elements.namedItem(first ?? \"name\") as HTMLElement | null)?.focus()\n      return\n    }\n    setPending(true)\n    timer.current = setTimeout(() => {\n      setPending(false)\n      setSent(name.split(\" \")[0])\n    }, 700)\n  }\n\n  return (\n    <NorthwindShell page=\"contact\" hrefs={hrefs} {...props}>\n      <main className=\"mx-auto grid max-w-6xl gap-14 px-4 py-16 sm:px-6 sm:py-24 lg:grid-cols-[1.1fr_1fr] lg:gap-20\">\n        <div>\n          <NorthwindHeading as=\"h1\" className=\"text-5xl leading-[1.04] sm:text-7xl\">Let’s talk about <em>your</em> spend.</NorthwindHeading>\n          <p className=\"text-muted-foreground mt-6 max-w-lg text-lg text-pretty\">Tell us a little about your team. We’ll book a 30-minute walkthrough built around how you spend today. No slides.</p>\n          {sent ? (\n            <div role=\"status\" className=\"bg-card mt-10 rounded-3xl border p-8\">\n              <span className=\"bg-chart-1/15 text-chart-1 flex size-12 items-center justify-center rounded-full\"><Check className=\"size-6\" aria-hidden=\"true\" /></span>\n              <p className={cn(\"mt-5 text-3xl\", serif)}>Thanks, {sent}.</p>\n              <p className=\"text-muted-foreground mt-2 text-pretty\">We’ll email you within one business day with a few times that work.</p>\n            </div>\n          ) : (\n            <form onSubmit={submit} noValidate className=\"mt-10 space-y-5\">\n              <div>\n                <label htmlFor=\"nw-name\" className=\"text-sm font-medium\">Full name</label>\n                <input id=\"nw-name\" name=\"name\" autoComplete=\"name\" aria-invalid={!!errors.name} aria-describedby={errors.name ? \"nw-name-err\" : undefined} className={field} />\n                {errors.name && <p id=\"nw-name-err\" className=\"text-destructive mt-1.5 text-sm\">{errors.name}</p>}\n              </div>\n              <div>\n                <label htmlFor=\"nw-email\" className=\"text-sm font-medium\">Work email</label>\n                <input id=\"nw-email\" name=\"email\" type=\"email\" autoComplete=\"email\" aria-invalid={!!errors.email} aria-describedby={errors.email ? \"nw-email-err\" : undefined} className={field} />\n                {errors.email && <p id=\"nw-email-err\" className=\"text-destructive mt-1.5 text-sm\">{errors.email}</p>}\n              </div>\n              <div>\n                <label htmlFor=\"nw-size\" className=\"text-sm font-medium\">Team size</label>\n                <select id=\"nw-size\" name=\"size\" defaultValue=\"\" aria-invalid={!!errors.size} aria-describedby={errors.size ? \"nw-size-err\" : undefined} className={field}>\n                  <option value=\"\" disabled>Choose one</option>\n                  <option>1–25 people</option><option>26–100 people</option><option>101–500 people</option><option>500+ people</option>\n                </select>\n                {errors.size && <p id=\"nw-size-err\" className=\"text-destructive mt-1.5 text-sm\">{errors.size}</p>}\n              </div>\n              <div>\n                <label htmlFor=\"nw-note\" className=\"text-sm font-medium\">What would you like to fix? <span className=\"text-muted-foreground font-normal\">(optional)</span></label>\n                <textarea id=\"nw-note\" name=\"note\" rows={4} className={cn(field, \"h-auto py-3\")} />\n              </div>\n              <button type=\"submit\" disabled={pending} className=\"bg-primary text-primary-foreground focus-visible:ring-ring/50 inline-flex h-13 items-center rounded-full px-8 text-base font-medium outline-none transition-opacity hover:opacity-90 focus-visible:ring-[3px] disabled:opacity-60\">\n                {pending ? \"Sending…\" : \"Request a demo\"}\n              </button>\n            </form>\n          )}\n        </div>\n        <aside className=\"space-y-4 lg:pt-4\" aria-label=\"Other ways to reach us\">\n          {[\n            { icon: Mail, title: \"Sales\", text: \"sales@northwind.example\", note: \"Replies within one business day.\" },\n            { icon: MessageCircle, title: \"Support\", text: \"help@northwind.example\", note: \"Existing customers: weekdays, 6am to 6pm Pacific.\" },\n            { icon: MapPin, title: \"Portland\", text: \"410 SW Alder St, Suite 300\", note: \"Toronto and London offices on request.\" },\n          ].map((c) => (\n            <div key={c.title} className=\"bg-card flex gap-4 rounded-2xl border p-6\">\n              <span className=\"bg-chart-2/15 text-chart-2 flex size-11 shrink-0 items-center justify-center rounded-full\"><c.icon className=\"size-5\" aria-hidden=\"true\" /></span>\n              <div><h2 className=\"font-semibold\">{c.title}</h2><p className=\"mt-0.5\">{c.text}</p><p className=\"text-muted-foreground mt-1 text-sm\">{c.note}</p></div>\n            </div>\n          ))}\n        </aside>\n      </main>\n    </NorthwindShell>\n  )\n}\n\nexport { NorthwindContact, type NorthwindContactProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/northwind/northwind-contact.tsx"
    },
    {
      "path": "registry/ballmac/app/northwind/page.tsx",
      "content": "// Ballmac UI: Northwind template route. https://ui.ballmac.com/templates/template-northwind\nimport type { Metadata } from \"next\"\n\nimport { NorthwindHome } from \"@/components/ballmac/templates/northwind/northwind-home\"\n\nexport const metadata: Metadata = {\n  title: \"Northwind: Spend with clarity\",\n  description: \"Cards, approvals and budgets for finance teams.\",\n}\n\nexport default function Page() {\n  return <NorthwindHome />\n}\n",
      "type": "registry:page",
      "target": "app/northwind/page.tsx"
    },
    {
      "path": "registry/ballmac/app/northwind/pricing/page.tsx",
      "content": "// Ballmac UI: Northwind template route. https://ui.ballmac.com/templates/template-northwind\nimport type { Metadata } from \"next\"\n\nimport { NorthwindPricing } from \"@/components/ballmac/templates/northwind/northwind-pricing\"\n\nexport const metadata: Metadata = {\n  title: \"Northwind pricing\",\n  description: \"Pricing that scales with your team, not your spend.\",\n}\n\nexport default function Page() {\n  return <NorthwindPricing />\n}\n",
      "type": "registry:page",
      "target": "app/northwind/pricing/page.tsx"
    },
    {
      "path": "registry/ballmac/app/northwind/customers/page.tsx",
      "content": "// Ballmac UI: Northwind template route. https://ui.ballmac.com/templates/template-northwind\nimport type { Metadata } from \"next\"\n\nimport { NorthwindCustomers } from \"@/components/ballmac/templates/northwind/northwind-customers\"\n\nexport const metadata: Metadata = {\n  title: \"Northwind customers\",\n  description: \"How finance teams closed faster with Northwind.\",\n}\n\nexport default function Page() {\n  return <NorthwindCustomers />\n}\n",
      "type": "registry:page",
      "target": "app/northwind/customers/page.tsx"
    },
    {
      "path": "registry/ballmac/app/northwind/about/page.tsx",
      "content": "// Ballmac UI: Northwind template route. https://ui.ballmac.com/templates/template-northwind\nimport type { Metadata } from \"next\"\n\nimport { NorthwindAbout } from \"@/components/ballmac/templates/northwind/northwind-about\"\n\nexport const metadata: Metadata = {\n  title: \"About Northwind\",\n  description: \"Why we think finance should feel quiet.\",\n}\n\nexport default function Page() {\n  return <NorthwindAbout />\n}\n",
      "type": "registry:page",
      "target": "app/northwind/about/page.tsx"
    },
    {
      "path": "registry/ballmac/app/northwind/contact/page.tsx",
      "content": "// Ballmac UI: Northwind template route. https://ui.ballmac.com/templates/template-northwind\nimport type { Metadata } from \"next\"\n\nimport { NorthwindContact } from \"@/components/ballmac/templates/northwind/northwind-contact\"\n\nexport const metadata: Metadata = {\n  title: \"Contact Northwind\",\n  description: \"Book a demo built around how your team spends.\",\n}\n\nexport default function Page() {\n  return <NorthwindContact />\n}\n",
      "type": "registry:page",
      "target": "app/northwind/contact/page.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "template",
      "b2b",
      "saas",
      "editorial",
      "serif",
      "landing page",
      "pricing",
      "case study",
      "contact"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "Installs a five-page editorial site for a B2B product, with a hotspot product tour on the home page. Change the palette in northwindCss, the tour steps in northwind-tour.tsx, and the copy in each page file.",
      "whenToUse": [
        "Marketing sites for finance, HR, legal and other B2B software",
        "Brands that want a warm, trustworthy, editorial voice instead of a tech look"
      ],
      "whenNotToUse": [
        "Dark developer tools (use an Orbit-style template)"
      ],
      "composesWith": [
        "pricing-3",
        "faq-2",
        "contact-1"
      ],
      "a11y": [],
      "customization": [
        "Edit northwindCss for the palette",
        "Edit steps and the mock dashboard in northwind-tour.tsx",
        "Swap fonts in northwind-fonts.ts"
      ]
    },
    "examples": [
      "template-northwind-demo",
      "template-northwind-pricing",
      "template-northwind-customers",
      "template-northwind-about",
      "template-northwind-contact"
    ],
    "url": "https://ui.ballmac.com/components/template-northwind"
  },
  "docs": "Pages are at /northwind, /northwind/pricing, /northwind/customers, /northwind/about and /northwind/contact. The light and dark palettes are the northwindCss string in northwind-theme.tsx.",
  "categories": [
    "templates"
  ],
  "type": "registry:block"
}