{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "template-studio",
  "title": "Studio: creative agency",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A loud, typographic five-page agency site: giant wide headlines, a work reel whose poster follows the pointer, numbered services that open, a work index with grid or table, project pages and a brief form.",
  "dependencies": [
    "lucide-react",
    "motion@^12"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/copy-button.json",
    "https://ui.ballmac.com/r/marquee.json",
    "https://ui.ballmac.com/r/media.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/templates/studio/studio-fonts.ts",
      "content": "// Ballmac UI: Studio template fonts. https://ui.ballmac.com/templates/template-studio\nimport { Archivo, Geist_Mono } from \"next/font/google\"\n\n/** Archivo has a width axis, so headlines can run wide and heavy while body copy stays normal. */\nexport const studioSans = Archivo({ variable: \"--studio-sans\", subsets: [\"latin\"], axes: [\"wdth\"], display: \"swap\", preload: false })\nexport const studioMono = Geist_Mono({ variable: \"--studio-mono\", subsets: [\"latin\"], display: \"swap\", preload: false })\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-fonts.ts"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-data.ts",
      "content": "import type { MediaSource } from \"@/components/ballmac/media\"\n\n// Ballmac UI: Studio sample content. https://ui.ballmac.com/templates/template-studio\n\nexport type StudioProject = { slug: string; name: string; client: string; year: string; discipline: \"Brand\" | \"Digital\" | \"Campaign\"; line: string; art: number; image?: MediaSource; imageAlt?: string }\n\nexport const projects: StudioProject[] = [\n  { slug: \"north-coast\", name: \"North Coast Rail\", client: \"North Coast\", year: \"2026\", discipline: \"Brand\", line: \"A railway identity that moves.\", art: 0 },\n  { slug: \"oat-and-ember\", name: \"Oat & Ember\", client: \"Oat & Ember\", year: \"2026\", discipline: \"Campaign\", line: \"A launch that sold out in nine days.\", art: 1 },\n  { slug: \"meridian\", name: \"Meridian Bank\", client: \"Meridian\", year: \"2025\", discipline: \"Digital\", line: \"Banking without the small print.\", art: 2 },\n  { slug: \"tidal\", name: \"Tidal Festival\", client: \"Tidal\", year: \"2025\", discipline: \"Brand\", line: \"Three days, one living identity.\", art: 3 },\n  { slug: \"pilot\", name: \"Pilot Coffee\", client: \"Pilot\", year: \"2025\", discipline: \"Campaign\", line: \"Packaging that earned its shelf.\", art: 4 },\n  { slug: \"alder\", name: \"Alder & Co\", client: \"Alder\", year: \"2024\", discipline: \"Digital\", line: \"A furniture shop built on stories.\", art: 5 },\n]\n\nexport const services = [\n  { n: \"01\", title: \"Brand identity\", text: \"Strategy, naming, logos, typefaces and the system that holds them together. We leave you with a brand people can actually use on a Tuesday.\", items: [\"Positioning\", \"Naming\", \"Visual identity\", \"Brand guidelines\"], from: \"From €45k\" },\n  { n: \"02\", title: \"Digital design\", text: \"Websites and products with a point of view. Designed in the browser, built with your team, and measured after launch.\", items: [\"Websites\", \"Product design\", \"Design systems\", \"Motion\"], from: \"From €60k\" },\n  { n: \"03\", title: \"Campaigns\", text: \"Ideas that travel. We direct, shoot and launch across print, social, film and the street.\", items: [\"Concept\", \"Art direction\", \"Film and photography\", \"Launch\"], from: \"From €35k\" },\n  { n: \"04\", title: \"Retained studio\", text: \"A senior team on call for a monthly fee. For brands that ship constantly and want one look across everything.\", items: [\"Monthly capacity\", \"Quarterly roadmap\", \"Priority access\"], from: \"From €12k / month\" },\n]\n\nexport const process = [\n  [\"Listen\", \"Two weeks of interviews, audits and arguments. We write down what is true before we make anything pretty.\"],\n  [\"Make\", \"Small, fast, ugly first. You see work every week, not a reveal at the end.\"],\n  [\"Launch\", \"We stay until it is live, then measure it. The work isn't finished when it is delivered.\"],\n]\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-data.ts"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-theme.tsx",
      "content": "// Ballmac UI: Studio template shell. https://ui.ballmac.com/templates/template-studio\n\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowUpRight, Menu, X } from \"lucide-react\"\n\nimport { studioMono, studioSans } from \"@/components/ballmac/templates/studio/studio-fonts\"\nimport { cn } from \"@/lib/utils\"\nimport { Media, type MediaSource } from \"@/components/ballmac/media\"\n\ntype StudioPage = \"home\" | \"work\" | \"project\" | \"services\" | \"contact\"\ntype StudioHrefs = Record<StudioPage, string>\n\nconst defaultHrefs: StudioHrefs = { home: \"/studio\", work: \"/studio/work\", project: \"/studio/work/north-coast\", services: \"/studio/services\", contact: \"/studio/contact\" }\n\n/** Studio's palette: stark white and black with a hot orange, plus electric blue, acid yellow, pink and mint for art. */\nconst studioCss = `\n.studio-theme,body:not(:has([data-gallery])):has(.studio-theme){--background:oklch(0.99 0 0);--foreground:oklch(0.12 0 0);--card:oklch(1 0 0);--card-foreground:oklch(0.12 0 0);--popover:oklch(1 0 0);--popover-foreground:oklch(0.12 0 0);--primary:oklch(0.12 0 0);--primary-foreground:oklch(0.99 0 0);--secondary:oklch(0.955 0 0);--secondary-foreground:oklch(0.12 0 0);--muted:oklch(0.955 0 0);--muted-foreground:oklch(0.44 0 0);--accent:oklch(0.93 0.02 40);--accent-foreground:oklch(0.12 0 0);--border:oklch(0.12 0 0 / 16%);--input:oklch(0.12 0 0 / 28%);--ring:oklch(0.12 0 0);--surface:oklch(0.965 0 0);--destructive:oklch(0.52 0.24 27);--chart-1:oklch(0.68 0.22 40);--chart-2:oklch(0.52 0.24 265);--chart-3:oklch(0.92 0.18 100);--chart-4:oklch(0.72 0.22 350);--chart-5:oklch(0.85 0.13 170);--studio-on-accent:oklch(0.12 0 0);--radius:0.25rem}\n.dark .studio-theme,.dark body:not(:has([data-gallery])):has(.studio-theme){--background:oklch(0.12 0 0);--foreground:oklch(0.97 0 0);--card:oklch(0.16 0 0);--card-foreground:oklch(0.97 0 0);--popover:oklch(0.18 0 0);--popover-foreground:oklch(0.97 0 0);--primary:oklch(0.97 0 0);--primary-foreground:oklch(0.12 0 0);--secondary:oklch(0.2 0 0);--secondary-foreground:oklch(0.97 0 0);--muted:oklch(0.2 0 0);--muted-foreground:oklch(0.72 0 0);--accent:oklch(0.24 0.02 40);--accent-foreground:oklch(0.97 0 0);--border:oklch(1 0 0 / 16%);--input:oklch(1 0 0 / 28%);--ring:oklch(0.97 0 0);--surface:oklch(0.15 0 0);--destructive:oklch(0.7 0.2 27);--chart-1:oklch(0.7 0.21 40);--chart-2:oklch(0.52 0.24 265);--chart-3:oklch(0.92 0.18 100);--chart-4:oklch(0.74 0.2 350);--chart-5:oklch(0.85 0.13 170);--studio-on-accent:oklch(0.12 0 0)}\nbody:not(:has([data-gallery])):has(.studio-theme){font-family:var(--studio-sans),ui-sans-serif,system-ui,sans-serif}\n`\n\n/** Wide, heavy headline settings for Archivo. */\nconst display = \"[font-stretch:112%] font-extrabold uppercase tracking-[-0.03em] leading-[0.88]\"\n\n/** A painted poster: bold shapes in the accent colours, standing in for project imagery. */\nfunction StudioArt({ variant, className, image, imageAlt }: { variant: number; className?: string; image?: MediaSource; imageAlt?: string }) {\n  const v = ((variant % 6) + 6) % 6\n  if (image) return <Media media={image} alt={imageAlt} fill className={cn(\"aspect-[4/3] w-full\", className)} />\n  return (\n    <div aria-hidden=\"true\" className={cn(\"relative isolate aspect-[4/3] w-full overflow-hidden\", className)}>\n      {v === 0 && (<><div className=\"bg-chart-1 absolute inset-0\" /><div className=\"bg-foreground absolute -bottom-[20%] -start-[10%] w-[80%] aspect-square rounded-full\" /><div className=\"bg-chart-3 absolute top-[12%] end-[12%] w-[26%] aspect-square rounded-full\" /></>)}\n      {v === 1 && (<><div className=\"bg-chart-2 absolute inset-0\" /><div className=\"bg-chart-3 absolute inset-y-0 start-[18%] w-[22%]\" /><div className=\"bg-chart-4 absolute inset-y-0 start-[48%] w-[12%]\" /><div className=\"bg-primary-foreground absolute top-[18%] end-[10%] h-[64%] w-[16%] rounded-full\" /></>)}\n      {v === 2 && (<><div className=\"bg-foreground absolute inset-0\" />{[0, 1, 2, 3, 4].map((i) => <div key={i} className=\"border-primary-foreground absolute inset-x-[10%] rounded-full border-[3px]\" style={{ top: `${10 + i * 12}%`, height: `${60 - i * 8}%`, opacity: 1 - i * 0.16 }} />)}<div className=\"bg-chart-1 absolute end-[14%] bottom-[14%] w-[20%] aspect-square rounded-full\" /></>)}\n      {v === 3 && (<><div className=\"bg-chart-4 absolute inset-0\" /><div className=\"absolute inset-[8%] grid grid-cols-4 grid-rows-3 gap-[2%]\">{Array.from({ length: 12 }, (_, i) => <div key={i} className={cn(\"rounded-full\", i % 5 === 0 ? \"bg-chart-3\" : i % 3 === 0 ? \"bg-foreground\" : \"bg-primary-foreground\")} />)}</div></>)}\n      {v === 4 && (<><div className=\"bg-chart-3 absolute inset-0\" /><div className=\"bg-foreground absolute top-[16%] start-[22%] h-[68%] w-[56%] rounded-t-[999px]\" /><div className=\"bg-chart-1 absolute bottom-[16%] start-[34%] w-[32%] aspect-square rounded-full\" /></>)}\n      {v === 5 && (<><div className=\"bg-chart-5 absolute inset-0\" /><div className=\"bg-foreground absolute inset-x-0 bottom-0 h-[38%]\" /><div className=\"bg-chart-2 absolute bottom-[38%] start-[12%] h-[34%] w-[28%]\" /><div className=\"bg-chart-1 absolute end-[14%] bottom-[38%] h-[52%] w-[22%] rounded-t-full\" /></>)}\n    </div>\n  )\n}\n\nconst links: { key: StudioPage; label: string }[] = [\n  { key: \"work\", label: \"Work\" },\n  { key: \"services\", label: \"Services\" },\n  { key: \"contact\", label: \"Contact\" },\n]\n\ntype StudioShellProps = React.ComponentProps<\"div\"> & {\n  /** The page being shown, so its nav link is marked current. */\n  page: StudioPage\n  /** Override where pages live (used by previews). */\n  hrefs?: Partial<StudioHrefs>\n}\n\n/** Studio's frame: a heavy wordmark, a plain nav, and a footer that is mostly the studio's name. */\nfunction StudioShell({ page, hrefs: overrides, className, style, children, ...props }: StudioShellProps) {\n  const hrefs = { ...defaultHrefs, ...overrides }\n  const [open, setOpen] = React.useState(false)\n  React.useEffect(() => {\n    const classes = [studioSans.variable, studioMono.variable].filter(Boolean)\n    document.body.classList.add(...classes)\n    return () => document.body.classList.remove(...classes)\n  }, [])\n  const active = page === \"project\" ? \"work\" : page\n  return (\n    <div\n      data-slot=\"studio\"\n      className={cn(\"studio-theme bg-background text-foreground relative min-h-dvh overflow-x-clip\", studioSans.variable, studioMono.variable, className)}\n      style={{ fontFamily: \"var(--studio-sans), ui-sans-serif, system-ui, sans-serif\", ...style }}\n      {...props}\n    >\n      <style>{studioCss}</style>\n      <header className=\"bg-background/90 sticky top-0 z-40 border-b backdrop-blur-lg\">\n        <div className=\"mx-auto flex h-14 max-w-[100rem] items-center justify-between px-4 sm:px-8\">\n          <a href={hrefs.home} className={cn(\"focus-visible:ring-ring/50 rounded text-xl outline-none focus-visible:ring-[3px]\", display)}>Hollis<span className=\"text-chart-1\">&amp;</span>Vane</a>\n          <nav aria-label=\"Main\" className=\"hidden items-center gap-8 md:flex\">\n            {links.map((l) => <a key={l.key} href={hrefs[l.key]} aria-current={active === l.key ? \"page\" : undefined} className=\"hover:text-chart-1 aria-[current=page]:underline decoration-chart-1 focus-visible:ring-ring/50 rounded text-sm font-semibold tracking-wide uppercase underline-offset-[10px] outline-none transition-colors aria-[current=page]:decoration-[3px] focus-visible:ring-[3px]\">{l.label}</a>)}\n          </nav>\n          <p className=\"text-muted-foreground hidden text-xs lg:block\" style={{ fontFamily: \"var(--studio-mono)\" }}>Lisbon · Berlin</p>\n          <button type=\"button\" aria-label={open ? \"Close menu\" : \"Open menu\"} aria-expanded={open} aria-controls=\"studio-mobile-menu\" onClick={() => setOpen((v) => !v)} className=\"hover:bg-accent focus-visible:ring-ring/50 inline-flex size-10 items-center justify-center rounded 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        {open && (\n          <nav id=\"studio-mobile-menu\" aria-label=\"Mobile\" className=\"border-t px-4 py-3 md:hidden\">\n            {links.map((l) => <a key={l.key} href={hrefs[l.key]} className={cn(\"hover:text-chart-1 block py-2.5 text-3xl\", display)}>{l.label}</a>)}\n          </nav>\n        )}\n      </header>\n      {children}\n      <footer className=\"bg-foreground text-background mt-24\">\n        <div className=\"mx-auto max-w-[100rem] px-4 pt-16 pb-8 sm:px-8\">\n          <div className=\"flex flex-wrap items-end justify-between gap-8\">\n            <p className=\"max-w-md text-xl text-pretty opacity-80\">New project, new idea, or just a question? We reply to every email within two working days.</p>\n            <a href={hrefs.contact} className=\"bg-chart-1 text-[var(--studio-on-accent)] focus-visible:ring-ring inline-flex h-14 items-center gap-2 rounded-full px-8 text-lg font-bold outline-none transition-transform hover:-translate-y-0.5 focus-visible:ring-[3px] motion-reduce:transition-none\">Start a project <ArrowUpRight className=\"size-5 rtl:-scale-x-100\" aria-hidden=\"true\" /></a>\n          </div>\n          <p aria-hidden=\"true\" className={cn(\"mt-14 text-[clamp(3.4rem,17.5vw,19rem)] whitespace-nowrap\", display)}>Hollis&amp;Vane</p>\n          <div className=\"mt-8 flex flex-wrap justify-between gap-4 border-t border-current/25 pt-6 text-sm opacity-70\">\n            <p>© 2026 Hollis &amp; Vane Studio Ltd.</p>\n            <ul className=\"flex gap-6\">{[\"Instagram\", \"Behance\", \"LinkedIn\", \"Newsletter\"].map((l) => <li key={l}><a href=\"#\" className=\"hover:underline\">{l}</a></li>)}</ul>\n          </div>\n        </div>\n      </footer>\n    </div>\n  )\n}\n\nexport { StudioArt, StudioShell, defaultHrefs as studioDefaultHrefs, display as studioDisplay, type StudioHrefs, type StudioPage, type StudioShellProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-theme.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-home.tsx",
      "content": "// Ballmac UI: Studio home page. https://ui.ballmac.com/templates/template-studio\n\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowUpRight } from \"lucide-react\"\nimport { useReducedMotion } from \"motion/react\"\n\nimport { Marquee } from \"@/components/ballmac/marquee\"\nimport { process, projects, services } from \"@/components/ballmac/templates/studio/studio-data\"\nimport { StudioArt, StudioShell, studioDisplay, type StudioHrefs } from \"@/components/ballmac/templates/studio/studio-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst mono = { fontFamily: \"var(--studio-mono)\" } as const\n\n/** A list of projects. Hover or focus a row and a poster follows the pointer (or sits beside the row for keyboard users). */\nfunction WorkReel({ href }: { href: string }) {\n  const reduce = useReducedMotion()\n  const [active, setActive] = React.useState<number | null>(null)\n  const [mode, setMode] = React.useState<\"pointer\" | \"focus\">(\"pointer\")\n  const box = React.useRef<HTMLDivElement>(null)\n  const card = React.useRef<HTMLDivElement>(null)\n\n  function move(e: React.PointerEvent) {\n    const r = box.current?.getBoundingClientRect()\n    if (!r || !card.current || reduce) return\n    card.current.style.transform = `translate(${e.clientX - r.left + 28}px, ${e.clientY - r.top - 120}px)`\n  }\n  const shown = active !== null ? projects[active] : null\n\n  return (\n    <div ref={box} className=\"relative\" onPointerMove={move} onPointerLeave={() => setActive(null)}>\n      <ul className=\"border-t-2 border-current\">\n        {projects.map((p, i) => (\n          <li key={p.slug} className=\"border-b\">\n            <a\n              href={href}\n              onPointerEnter={() => { setMode(\"pointer\"); setActive(i) }}\n              onFocus={() => { setMode(\"focus\"); setActive(i) }}\n              onBlur={() => setActive(null)}\n              className=\"hover:bg-chart-1 hover:text-[var(--studio-on-accent)] focus-visible:bg-chart-1 focus-visible:text-[var(--studio-on-accent)] group grid grid-cols-[1fr_auto] items-baseline gap-4 px-2 py-6 outline-none transition-colors sm:grid-cols-[3rem_1fr_10rem_5rem] sm:px-4 motion-reduce:transition-none\"\n            >\n              <span className=\"text-muted-foreground group-hover:text-[var(--studio-on-accent)] group-focus-visible:text-[var(--studio-on-accent)] hidden text-sm sm:block\" style={mono}>{String(i + 1).padStart(2, \"0\")}</span>\n              <span className={cn(\"text-[clamp(2rem,6.4vw,5.5rem)] text-balance\", studioDisplay)}>{p.name}</span>\n              <span className=\"group-hover:text-[var(--studio-on-accent)] group-focus-visible:text-[var(--studio-on-accent)] text-muted-foreground hidden text-sm sm:block\" style={mono}>{p.discipline}</span>\n              <span className=\"group-hover:text-[var(--studio-on-accent)] group-focus-visible:text-[var(--studio-on-accent)] text-muted-foreground text-end text-sm tabular-nums\" style={mono}>{p.year}</span>\n            </a>\n          </li>\n        ))}\n      </ul>\n      <div\n        ref={card}\n        aria-hidden=\"true\"\n        className={cn(\"pointer-events-none absolute top-0 start-0 z-10 hidden w-72 will-change-transform md:block\", shown ? \"opacity-100\" : \"opacity-0\", \"transition-opacity duration-200 motion-reduce:transition-none\")}\n        style={mode === \"focus\" || reduce ? { transform: `translate(calc(100% - 20rem), ${(active ?? 0) * 7.2}rem)` } : undefined}\n      >\n        {shown && (<div className=\"border-foreground bg-background overflow-hidden rounded border-2 shadow-2xl\"><StudioArt variant={shown.art} image={shown.image} imageAlt={shown.imageAlt} /><p className=\"border-t-2 px-3 py-2 text-sm font-semibold\">{shown.line}</p></div>)}\n      </div>\n    </div>\n  )\n}\n\ntype StudioHomeProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<StudioHrefs> }\n\n/** The Studio home page: a huge statement, a hover-reveal work reel, services, process and a loud call to action. */\nfunction StudioHome({ hrefs, ...props }: StudioHomeProps) {\n  const h = { work: hrefs?.work ?? \"/studio/work\", project: hrefs?.project ?? \"/studio/work/north-coast\", services: hrefs?.services ?? \"/studio/services\", contact: hrefs?.contact ?? \"/studio/contact\" }\n  return (\n    <StudioShell page=\"home\" hrefs={hrefs} {...props}>\n      <main>\n        <section className=\"mx-auto max-w-[100rem] px-4 pt-10 pb-12 sm:px-8 sm:pt-16\">\n          <p className=\"text-muted-foreground text-sm\" style={mono}>Independent branding &amp; digital studio · est. 2016</p>\n          <h1 className={cn(\"mt-6 text-[clamp(2.8rem,9.2vw,10rem)]\", studioDisplay)}>\n            <span className=\"block\">We make</span>\n            <span className=\"block\">brands that</span>\n            <span className=\"block\"><span className=\"bg-chart-1 inline-block px-[0.12em] text-[var(--studio-on-accent)]\">move</span> people.</span>\n          </h1>\n          <div className=\"mt-12 grid gap-8 sm:grid-cols-[1fr_auto] sm:items-end\">\n            <p className=\"max-w-xl text-xl text-pretty sm:text-2xl\">Hollis &amp; Vane is a 14-person studio in Lisbon and Berlin. We design identities, websites and campaigns for people who would rather be remembered than liked.</p>\n            <a href={h.work} className=\"bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-14 items-center gap-2 rounded-full px-8 text-lg font-bold outline-none transition-transform hover:-translate-y-0.5 focus-visible:ring-[3px] motion-reduce:transition-none\">See the work <ArrowUpRight className=\"size-5 rtl:-scale-x-100\" aria-hidden=\"true\" /></a>\n          </div>\n        </section>\n\n        <section aria-label=\"Clients\" className=\"border-foreground bg-foreground text-background border-y-2 py-5\">\n          <Marquee speed={60} gap={56} pauseOnHover>\n            {[\"North Coast\", \"Oat & Ember\", \"Meridian\", \"Tidal\", \"Pilot\", \"Alder & Co\", \"Fernhill\", \"Kestrel\"].map((c) => <span key={c} className={cn(\"flex items-center gap-14 text-4xl sm:text-5xl\", studioDisplay)}>{c}<span className=\"bg-chart-1 size-4 rounded-full\" aria-hidden=\"true\" /></span>)}\n          </Marquee>\n        </section>\n\n        <section id=\"work\" aria-labelledby=\"st-work\" className=\"mx-auto max-w-[100rem] px-4 py-24 sm:px-8\">\n          <div className=\"mb-10 flex items-end justify-between gap-4\"><h2 id=\"st-work\" className={cn(\"text-[clamp(2.4rem,7vw,6rem)]\", studioDisplay)}>Selected work</h2><p className=\"text-muted-foreground hidden text-sm sm:block\" style={mono}>Hover a row</p></div>\n          <WorkReel href={h.project} />\n        </section>\n\n        <section aria-labelledby=\"st-services\" className=\"border-t-2 border-current\">\n          <div className=\"mx-auto grid max-w-[100rem] gap-12 px-4 py-24 sm:px-8 lg:grid-cols-[1fr_2fr]\">\n            <div><h2 id=\"st-services\" className={cn(\"text-[clamp(2.4rem,6vw,5rem)]\", studioDisplay)}>What we do</h2><a href={h.services} className=\"mt-6 inline-flex items-center gap-2 font-bold underline decoration-2 underline-offset-8\">All services <ArrowUpRight className=\"size-4 rtl:-scale-x-100\" aria-hidden=\"true\" /></a></div>\n            <ul className=\"divide-y-2 border-y-2\">\n              {services.map((s) => (\n                <li key={s.n} className=\"grid gap-3 py-8 sm:grid-cols-[4rem_1fr]\">\n                  <span className=\"bg-chart-1 h-fit w-fit px-2 py-0.5 text-lg font-bold text-[var(--studio-on-accent)]\" style={mono}>{s.n}</span>\n                  <div><h3 className={cn(\"text-3xl\", studioDisplay)}>{s.title}</h3><p className=\"text-muted-foreground mt-3 max-w-xl text-pretty\">{s.text}</p></div>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </section>\n\n        <section aria-labelledby=\"st-process\" className=\"bg-chart-2 text-white\">\n          <div className=\"mx-auto max-w-[100rem] px-4 py-24 sm:px-8\">\n            <h2 id=\"st-process\" className={cn(\"text-[clamp(2.4rem,6vw,5rem)]\", studioDisplay)}>How we work</h2>\n            <ol className=\"mt-14 grid gap-px bg-white/30 md:grid-cols-3\">\n              {process.map(([t, d], i) => (\n                <li key={t} className=\"bg-chart-2 p-8 md:p-10\">\n                  <span className=\"text-sm\" style={mono}>Step {i + 1}</span>\n                  <h3 className={cn(\"mt-4 text-5xl\", studioDisplay)}>{t}</h3>\n                  <p className=\"mt-5 text-lg text-pretty opacity-90\">{d}</p>\n                </li>\n              ))}\n            </ol>\n          </div>\n        </section>\n\n        <section className=\"mx-auto max-w-[100rem] px-4 py-24 sm:px-8\">\n          <blockquote className={cn(\"max-w-6xl text-[clamp(1.8rem,4.6vw,4rem)] leading-[1.05] normal-case\", studioDisplay.replace(\"uppercase\", \"\"))}>“They didn’t give us a logo. They gave us a way of behaving, and now the whole company does it.”</blockquote>\n          <p className=\"text-muted-foreground mt-8 text-sm\" style={mono}>Anaïs Dupont · CMO, North Coast Rail</p>\n        </section>\n\n        <section className=\"bg-chart-1 text-[var(--studio-on-accent)]\">\n          <a href={h.contact} className=\"group focus-visible:ring-ring mx-auto flex max-w-[100rem] items-center justify-between gap-6 px-4 py-16 outline-none focus-visible:ring-[3px] focus-visible:ring-inset sm:px-8 sm:py-24\">\n            <span className={cn(\"text-[clamp(3rem,11vw,10rem)]\", studioDisplay)}>Let’s talk</span>\n            <ArrowUpRight className=\"size-[clamp(3rem,10vw,9rem)] shrink-0 transition-transform duration-300 group-hover:translate-x-2 group-hover:-translate-y-2 motion-reduce:transition-none rtl:-scale-x-100 rtl:group-hover:-translate-x-2\" strokeWidth={1.5} aria-hidden=\"true\" />\n          </a>\n        </section>\n      </main>\n    </StudioShell>\n  )\n}\n\nexport { StudioHome, type StudioHomeProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-home.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-work.tsx",
      "content": "// Ballmac UI: Studio work page. https://ui.ballmac.com/templates/template-studio\n\"use client\"\n\nimport * as React from \"react\"\nimport { LayoutGrid, List } from \"lucide-react\"\n\nimport { projects } from \"@/components/ballmac/templates/studio/studio-data\"\nimport { StudioArt, StudioShell, studioDisplay, type StudioHrefs } from \"@/components/ballmac/templates/studio/studio-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst mono = { fontFamily: \"var(--studio-mono)\" } as const\ntype Filter = \"All\" | \"Brand\" | \"Digital\" | \"Campaign\"\nconst filters: Filter[] = [\"All\", \"Brand\", \"Digital\", \"Campaign\"]\n\ntype StudioWorkProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<StudioHrefs> }\n\n/** The Studio work index: every project as a table or a poster grid, filtered by discipline. */\nfunction StudioWork({ hrefs, ...props }: StudioWorkProps) {\n  const [filter, setFilter] = React.useState<Filter>(\"All\")\n  const [view, setView] = React.useState<\"list\" | \"grid\">(\"grid\")\n  const shown = projects.filter((p) => filter === \"All\" || p.discipline === filter)\n  const href = hrefs?.project ?? \"/studio/work/north-coast\"\n  return (\n    <StudioShell page=\"work\" hrefs={hrefs} {...props}>\n      <main className=\"mx-auto max-w-[100rem] px-4 pt-10 sm:px-8 sm:pt-16\">\n        <h1 className={cn(\"text-[clamp(3rem,13vw,13rem)]\", studioDisplay)}>Work <span className=\"text-muted-foreground text-[0.22em] align-top tabular-nums\" style={mono}>({shown.length})</span></h1>\n        <div className=\"mt-10 flex flex-wrap items-center gap-3 border-y-2 py-4\">\n          <div role=\"group\" aria-label=\"Filter by discipline\" className=\"flex flex-wrap gap-2\">\n            {filters.map((f) => <button key={f} type=\"button\" aria-pressed={filter === f} onClick={() => setFilter(f)} className={cn(\"focus-visible:ring-ring/50 h-11 rounded-full border-2 border-current px-5 text-sm font-bold tracking-wide uppercase outline-none transition-colors focus-visible:ring-[3px]\", filter === f ? \"bg-foreground text-background\" : \"hover:bg-chart-1 hover:text-[var(--studio-on-accent)]\")}>{f}</button>)}\n          </div>\n          <div role=\"group\" aria-label=\"Layout\" className=\"ms-auto flex gap-1\">\n            {([[\"grid\", LayoutGrid, \"Grid\"], [\"list\", List, \"List\"]] as const).map(([v, Icon, l]) => <button key={v} type=\"button\" aria-pressed={view === v} aria-label={`${l} view`} onClick={() => setView(v)} className={cn(\"focus-visible:ring-ring/50 inline-flex size-11 items-center justify-center rounded-full border-2 outline-none focus-visible:ring-[3px]\", view === v ? \"bg-foreground text-background border-transparent\" : \"hover:bg-accent border-current\")}><Icon className=\"size-4\" aria-hidden=\"true\" /></button>)}\n          </div>\n        </div>\n        <p className=\"sr-only\" role=\"status\">{shown.length} projects shown</p>\n        {view === \"grid\" ? (\n          <ul className=\"mt-10 grid gap-x-6 gap-y-14 sm:grid-cols-2 lg:grid-cols-3\">\n            {shown.map((p) => (\n              <li key={p.slug}>\n                <a href={href} className=\"group focus-visible:ring-ring/50 block outline-none focus-visible:ring-[3px] focus-visible:ring-offset-4 focus-visible:ring-offset-background\">\n                  <div className=\"border-foreground overflow-hidden border-2\"><StudioArt variant={p.art} image={p.image} imageAlt={p.imageAlt} className=\"transition-transform duration-500 group-hover:scale-105 group-focus-visible:scale-105 motion-reduce:transition-none\" /></div>\n                  <div className=\"mt-4 flex items-baseline justify-between gap-3\"><h2 className={cn(\"text-3xl group-hover:underline decoration-chart-1 decoration-4 underline-offset-4\", studioDisplay)}>{p.name}</h2><span className=\"text-muted-foreground text-sm tabular-nums\" style={mono}>{p.year}</span></div>\n                  <p className=\"text-muted-foreground mt-1 text-sm\" style={mono}>{p.discipline} · {p.line}</p>\n                </a>\n              </li>\n            ))}\n          </ul>\n        ) : (\n          <div className=\"mt-6 overflow-x-auto\">\n            <table className=\"w-full min-w-[34rem] text-start\">\n              <caption className=\"sr-only\">All projects</caption>\n              <thead><tr className=\"text-muted-foreground border-b text-xs uppercase\" style={mono}><th scope=\"col\" className=\"py-3 font-normal\">Project</th><th scope=\"col\" className=\"py-3 font-normal\">Discipline</th><th scope=\"col\" className=\"py-3 font-normal\">Client</th><th scope=\"col\" className=\"py-3 text-end font-normal\">Year</th></tr></thead>\n              <tbody>\n                {shown.map((p) => (\n                  <tr key={p.slug} className=\"hover:bg-chart-1 hover:text-[var(--studio-on-accent)] border-b transition-colors motion-reduce:transition-none\">\n                    <th scope=\"row\" className=\"py-5 pe-4\"><a href={href} className={cn(\"focus-visible:ring-ring/50 rounded text-2xl outline-none focus-visible:ring-[3px] sm:text-4xl\", studioDisplay)}>{p.name}</a></th>\n                    <td className=\"py-5 text-sm\" style={mono}>{p.discipline}</td>\n                    <td className=\"py-5 text-sm\" style={mono}>{p.client}</td>\n                    <td className=\"py-5 text-end text-sm tabular-nums\" style={mono}>{p.year}</td>\n                  </tr>\n                ))}\n              </tbody>\n            </table>\n          </div>\n        )}\n      </main>\n    </StudioShell>\n  )\n}\n\nexport { StudioWork, type StudioWorkProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-work.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-project.tsx",
      "content": "// Ballmac UI: Studio project page. https://ui.ballmac.com/templates/template-studio\n\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowUpRight } from \"lucide-react\"\n\nimport { projects } from \"@/components/ballmac/templates/studio/studio-data\"\nimport { StudioArt, StudioShell, studioDisplay, type StudioHrefs } from \"@/components/ballmac/templates/studio/studio-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst mono = { fontFamily: \"var(--studio-mono)\" } as const\n\nconst facts = [[\"Client\", \"North Coast Rail\"], [\"Year\", \"2026\"], [\"Services\", \"Identity, signage, motion, website\"], [\"Team\", \"6 people, 11 months\"]]\nconst credits = [[\"Creative direction\", \"Inês Hollis\"], [\"Design lead\", \"Jonas Vane\"], [\"Motion\", \"Mei Tanaka\"], [\"Development\", \"Colm Brennan\"], [\"Strategy\", \"Amara Okafor\"]]\n\ntype StudioProjectProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<StudioHrefs> }\n\n/** A Studio project page: a full-bleed poster, a fact line, big imagery in two rhythms, a pull quote and credits. */\nfunction StudioProject({ hrefs, ...props }: StudioProjectProps) {\n  const next = projects[1]\n  return (\n    <StudioShell page=\"project\" hrefs={hrefs} {...props}>\n      <main>\n        <header className=\"mx-auto max-w-[100rem] px-4 pt-10 sm:px-8 sm:pt-16\">\n          <p className=\"text-muted-foreground text-sm\" style={mono}><a href={hrefs?.work ?? \"/studio/work\"} className=\"underline-offset-4 hover:underline\">Work</a> / Brand</p>\n          <h1 className={cn(\"mt-4 text-[clamp(3rem,12vw,12rem)]\", studioDisplay)}>North Coast Rail</h1>\n          <p className=\"mt-6 max-w-3xl text-2xl text-pretty sm:text-3xl\">A railway identity that moves: from a logo built from a single line to 400 trains that carry it.</p>\n        </header>\n        <StudioArt variant={0} className=\"border-foreground mt-12 aspect-[16/8] border-y-2\" />\n        <dl className=\"mx-auto grid max-w-[100rem] gap-6 px-4 py-10 sm:grid-cols-2 sm:px-8 lg:grid-cols-4\">\n          {facts.map(([k, v]) => <div key={k}><dt className=\"text-muted-foreground text-xs uppercase\" style={mono}>{k}</dt><dd className=\"mt-2 text-lg font-semibold\">{v}</dd></div>)}\n        </dl>\n        <section className=\"mx-auto max-w-[100rem] px-4 py-12 sm:px-8\">\n          <div className=\"grid gap-12 lg:grid-cols-[1fr_2fr]\">\n            <h2 className={cn(\"text-4xl\", studioDisplay)}>The idea</h2>\n            <p className=\"max-w-3xl text-2xl leading-snug text-pretty sm:text-3xl\">A railway is one long line with many stops. We drew the identity the same way: a single continuous stroke that bends into the letters, the map and the livery. It is simple enough to paint on a train and distinctive enough to recognise from the platform.</p>\n          </div>\n        </section>\n        <section className=\"mx-auto max-w-[100rem] space-y-4 px-4 sm:px-8\">\n          <div className=\"grid gap-4 sm:grid-cols-2\"><StudioArt variant={1} className=\"border-foreground border-2\" /><StudioArt variant={4} className=\"border-foreground border-2\" /></div>\n          <StudioArt variant={3} className=\"border-foreground aspect-[16/7] border-2\" />\n        </section>\n        <section className=\"bg-chart-1 mt-20 text-[var(--studio-on-accent)]\">\n          <figure className=\"mx-auto max-w-[100rem] px-4 py-20 sm:px-8 sm:py-28\">\n            <blockquote className={cn(\"max-w-6xl text-[clamp(2rem,5.6vw,5rem)]\", studioDisplay)}>“Passengers started photographing the trains. We have never had that before.”</blockquote>\n            <figcaption className=\"mt-8 text-sm\" style={mono}>Anaïs Dupont, CMO, North Coast Rail</figcaption>\n          </figure>\n        </section>\n        <section aria-labelledby=\"sp-credits\" className=\"mx-auto max-w-[100rem] px-4 py-20 sm:px-8\">\n          <h2 id=\"sp-credits\" className={cn(\"text-4xl\", studioDisplay)}>Credits</h2>\n          <dl className=\"mt-8 divide-y-2 border-y-2\">{credits.map(([k, v]) => <div key={k} className=\"grid grid-cols-2 py-4 sm:grid-cols-[16rem_1fr]\"><dt className=\"text-muted-foreground text-sm\" style={mono}>{k}</dt><dd className=\"font-semibold\">{v}</dd></div>)}</dl>\n        </section>\n        <a href={hrefs?.project ?? \"/studio/work/oat-and-ember\"} className=\"group border-foreground focus-visible:ring-ring/50 block border-t-2 outline-none focus-visible:ring-[3px] focus-visible:ring-inset\">\n          <div className=\"mx-auto flex max-w-[100rem] items-center justify-between gap-6 px-4 py-14 sm:px-8\">\n            <div><p className=\"text-muted-foreground text-sm\" style={mono}>Next project</p><p className={cn(\"group-hover:text-chart-1 mt-3 text-[clamp(2.4rem,8vw,8rem)] transition-colors motion-reduce:transition-none\", studioDisplay)}>{next.name}</p></div>\n            <ArrowUpRight className=\"size-14 shrink-0 transition-transform group-hover:translate-x-2 group-hover:-translate-y-2 motion-reduce:transition-none rtl:-scale-x-100 rtl:group-hover:-translate-x-2\" strokeWidth={1.5} aria-hidden=\"true\" />\n          </div>\n        </a>\n      </main>\n    </StudioShell>\n  )\n}\n\nexport { StudioProject, type StudioProjectProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-project.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-services.tsx",
      "content": "// Ballmac UI: Studio services page. https://ui.ballmac.com/templates/template-studio\n\"use client\"\n\nimport * as React from \"react\"\nimport { Minus, Plus } from \"lucide-react\"\n\nimport { services } from \"@/components/ballmac/templates/studio/studio-data\"\nimport { StudioShell, studioDisplay, type StudioHrefs } from \"@/components/ballmac/templates/studio/studio-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst mono = { fontFamily: \"var(--studio-mono)\" } as const\n\ntype StudioServicesProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<StudioHrefs> }\n\n/** The Studio services page: four numbered disciplines that open to show what's inside, with a starting price. */\nfunction StudioServices({ hrefs, ...props }: StudioServicesProps) {\n  const [open, setOpen] = React.useState<string | null>(\"01\")\n  return (\n    <StudioShell page=\"services\" hrefs={hrefs} {...props}>\n      <main className=\"mx-auto max-w-[100rem] px-4 pt-10 sm:px-8 sm:pt-16\">\n        <h1 className={cn(\"text-[clamp(3rem,12vw,12rem)]\", studioDisplay)}>Services</h1>\n        <p className=\"mt-6 max-w-2xl text-2xl text-pretty\">Four ways to work with us. Most projects start with one and grow into two.</p>\n        <ul className=\"mt-14 border-t-2\">\n          {services.map((s) => {\n            const on = open === s.n\n            return (\n              <li key={s.n} className=\"border-b-2\">\n                <h2>\n                  <button type=\"button\" aria-expanded={on} aria-controls={`ss-${s.n}`} onClick={() => setOpen(on ? null : s.n)} className=\"hover:bg-accent focus-visible:ring-ring/50 grid w-full grid-cols-[3.5rem_1fr_auto] items-center gap-4 px-2 py-7 text-start outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-inset sm:grid-cols-[6rem_1fr_auto] sm:px-4 motion-reduce:transition-none\">\n                    <span className=\"bg-chart-1 h-fit w-fit px-2 py-0.5 text-lg font-bold text-[var(--studio-on-accent)]\" style={mono}>{s.n}</span>\n                    <span className={cn(\"text-[clamp(1.8rem,5vw,4.5rem)]\", studioDisplay)}>{s.title}</span>\n                    {on ? <Minus className=\"size-8\" aria-hidden=\"true\" /> : <Plus className=\"size-8\" aria-hidden=\"true\" />}\n                  </button>\n                </h2>\n                <div id={`ss-${s.n}`} role=\"region\" aria-labelledby={undefined} hidden={!on} className=\"grid gap-8 px-2 pb-10 sm:grid-cols-[6rem_1fr_16rem] sm:px-4\">\n                  <span aria-hidden=\"true\" className=\"hidden sm:block\" />\n                  <div><p className=\"max-w-2xl text-xl text-pretty\">{s.text}</p><ul className=\"mt-6 flex flex-wrap gap-2\">{s.items.map((i) => <li key={i} className=\"rounded-full border-2 border-current px-4 py-1.5 text-sm font-semibold\">{i}</li>)}</ul></div>\n                  <p className=\"bg-chart-3 h-fit rounded px-4 py-3 text-lg font-bold text-[var(--studio-on-accent)]\" style={mono}>{s.from}</p>\n                </div>\n              </li>\n            )\n          })}\n        </ul>\n        <section aria-labelledby=\"ss-faq\" className=\"grid gap-10 py-24 lg:grid-cols-[1fr_2fr]\">\n          <h2 id=\"ss-faq\" className={cn(\"text-5xl\", studioDisplay)}>Before you ask</h2>\n          <dl className=\"divide-y-2 border-y-2\">\n            {[[\"Do you work with startups?\", \"Yes, if the founders are the ones in the room. We take two a quarter.\"], [\"How long does a project take?\", \"An identity takes 10 to 14 weeks. A website, 12 to 20. A campaign depends on the shoot.\"], [\"Can you work with our team?\", \"Always. Most of our best work was built alongside in-house designers and developers.\"]].map(([q, a]) => <div key={q} className=\"py-6\"><dt className=\"text-xl font-bold\">{q}</dt><dd className=\"text-muted-foreground mt-2 max-w-xl text-pretty\">{a}</dd></div>)}\n          </dl>\n        </section>\n      </main>\n    </StudioShell>\n  )\n}\n\nexport { StudioServices, type StudioServicesProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-services.tsx"
    },
    {
      "path": "registry/ballmac/components/templates/studio/studio-contact.tsx",
      "content": "// Ballmac UI: Studio contact page. https://ui.ballmac.com/templates/template-studio\n\"use client\"\n\nimport * as React from \"react\"\nimport { Check } from \"lucide-react\"\n\nimport { CopyButton } from \"@/components/ballmac/copy-button\"\nimport { StudioShell, studioDisplay, type StudioHrefs } from \"@/components/ballmac/templates/studio/studio-theme\"\nimport { cn } from \"@/lib/utils\"\n\nconst mono = { fontFamily: \"var(--studio-mono)\" } as const\nconst budgets = [\"Under €30k\", \"€30–60k\", \"€60–120k\", \"€120k+\"]\nconst field = \"bg-background focus-visible:ring-ring/50 mt-2 h-14 w-full border-2 border-current px-4 text-lg outline-none focus-visible:ring-[3px] aria-[invalid=true]:border-destructive\"\n\ntype StudioContactProps = React.ComponentProps<\"div\"> & { hrefs?: Partial<StudioHrefs> }\n\n/** The Studio contact page: a huge email, a project brief form with chips and inline errors, and the two offices. */\nfunction StudioContact({ hrefs, ...props }: StudioContactProps) {\n  const [budget, setBudget] = React.useState(\"\")\n  const [errors, setErrors] = React.useState<{ name?: string; email?: string; brief?: string }>({})\n  const [sent, setSent] = React.useState<string | null>(null)\n  function submit(e: React.FormEvent<HTMLFormElement>) {\n    e.preventDefault()\n    const f = new FormData(e.currentTarget)\n    const name = String(f.get(\"name\") ?? \"\").trim()\n    const email = String(f.get(\"email\") ?? \"\").trim()\n    const brief = String(f.get(\"brief\") ?? \"\").trim()\n    const next: typeof errors = {}\n    if (!name) next.name = \"Your name, please.\"\n    if (!/^\\S+@\\S+\\.\\S+$/.test(email)) next.email = \"That email doesn’t look right.\"\n    if (brief.length < 20) next.brief = \"Tell us a little more: at least a sentence.\"\n    setErrors(next)\n    const first = ([\"name\", \"email\", \"brief\"] as const).find((k) => next[k])\n    if (first) {\n      ;(e.currentTarget.elements.namedItem(first) as HTMLElement | null)?.focus()\n      return\n    }\n    setSent(name.split(\" \")[0])\n  }\n  return (\n    <StudioShell page=\"contact\" hrefs={hrefs} {...props}>\n      <main className=\"mx-auto max-w-[100rem] px-4 pt-10 sm:px-8 sm:pt-16\">\n        <h1 className={cn(\"text-[clamp(3rem,12vw,12rem)]\", studioDisplay)}>Say hello</h1>\n        <div className=\"mt-8 flex flex-wrap items-center gap-3\">\n          <a href=\"mailto:hello@hollisvane.example\" className=\"focus-visible:ring-ring/50 decoration-chart-1 rounded text-[clamp(1.4rem,4.4vw,3.6rem)] font-bold tracking-tight break-all underline decoration-4 underline-offset-8 outline-none focus-visible:ring-[3px]\">hello@hollisvane.example</a>\n          <CopyButton value=\"hello@hollisvane.example\" ariaLabel=\"Copy email address\" className=\"size-11\" />\n        </div>\n        <div className=\"mt-20 grid gap-16 lg:grid-cols-[1.4fr_1fr]\">\n          {sent ? (\n            <div role=\"status\" className=\"bg-chart-1 h-fit p-10 text-[var(--studio-on-accent)]\">\n              <Check className=\"size-12\" strokeWidth={2.5} aria-hidden=\"true\" />\n              <p className={cn(\"mt-6 text-5xl\", studioDisplay)}>Thanks, {sent}.</p>\n              <p className=\"mt-4 max-w-md text-xl text-pretty\">We read every brief. One of us will reply within two working days.</p>\n            </div>\n          ) : (\n            <form onSubmit={submit} noValidate className=\"space-y-8\">\n              <div className=\"grid gap-8 sm:grid-cols-2\">\n                <div><label htmlFor=\"sc-name\" className=\"text-sm font-bold uppercase\">Name</label><input id=\"sc-name\" name=\"name\" autoComplete=\"name\" aria-invalid={!!errors.name} aria-describedby={errors.name ? \"sc-name-err\" : undefined} className={field} />{errors.name && <p id=\"sc-name-err\" className=\"text-destructive mt-2 text-sm font-semibold\">{errors.name}</p>}</div>\n                <div><label htmlFor=\"sc-email\" className=\"text-sm font-bold uppercase\">Email</label><input id=\"sc-email\" name=\"email\" type=\"email\" autoComplete=\"email\" aria-invalid={!!errors.email} aria-describedby={errors.email ? \"sc-email-err\" : undefined} className={field} />{errors.email && <p id=\"sc-email-err\" className=\"text-destructive mt-2 text-sm font-semibold\">{errors.email}</p>}</div>\n              </div>\n              <fieldset><legend className=\"text-sm font-bold uppercase\">Budget</legend>\n                <div className=\"mt-3 flex flex-wrap gap-2\">{budgets.map((b) => <label key={b} className={cn(\"focus-within:ring-ring/50 cursor-pointer rounded-full border-2 border-current px-5 py-2.5 font-semibold transition-colors focus-within:ring-[3px]\", budget === b ? \"bg-foreground text-background\" : \"hover:bg-chart-3 hover:text-[var(--studio-on-accent)]\")}><input type=\"radio\" name=\"budget\" value={b} checked={budget === b} onChange={() => setBudget(b)} className=\"sr-only\" />{b}</label>)}</div>\n              </fieldset>\n              <div><label htmlFor=\"sc-brief\" className=\"text-sm font-bold uppercase\">The brief</label><textarea id=\"sc-brief\" name=\"brief\" rows={5} aria-invalid={!!errors.brief} aria-describedby={errors.brief ? \"sc-brief-err\" : undefined} className={cn(field, \"h-auto py-3\")} />{errors.brief && <p id=\"sc-brief-err\" className=\"text-destructive mt-2 text-sm font-semibold\">{errors.brief}</p>}</div>\n              <button type=\"submit\" className=\"bg-foreground text-background focus-visible:ring-ring/50 inline-flex h-16 items-center rounded-full px-10 text-lg font-bold outline-none transition-transform hover:-translate-y-0.5 focus-visible:ring-[3px] motion-reduce:transition-none\">Send the brief</button>\n            </form>\n          )}\n          <aside aria-label=\"Offices\" className=\"space-y-10\">\n            {[[\"Lisbon\", \"Rua da Boavista 12, 1200-066\", \"Mon to Fri, 9 to 18\"], [\"Berlin\", \"Oranienstraße 164, 10969\", \"Mon to Fri, 9 to 18\"]].map(([c, a, t]) => <div key={c} className=\"border-t-2 pt-4\"><h2 className={cn(\"text-4xl\", studioDisplay)}>{c}</h2><p className=\"mt-3 text-lg\">{a}</p><p className=\"text-muted-foreground text-sm\" style={mono}>{t}</p></div>)}\n            <p className=\"text-muted-foreground text-sm text-pretty\">Looking for a job instead? We hire one or two people a year, usually after seeing their work somewhere else. Send it anyway.</p>\n          </aside>\n        </div>\n      </main>\n    </StudioShell>\n  )\n}\n\nexport { StudioContact, type StudioContactProps }\n",
      "type": "registry:component",
      "target": "@components/ballmac/templates/studio/studio-contact.tsx"
    },
    {
      "path": "registry/ballmac/app/studio/page.tsx",
      "content": "// Ballmac UI: Studio template route. https://ui.ballmac.com/templates/template-studio\nimport type { Metadata } from \"next\"\n\nimport { StudioHome } from \"@/components/ballmac/templates/studio/studio-home\"\n\nexport const metadata: Metadata = {\n  title: \"Hollis & Vane: brands that move people\",\n  description: \"An independent branding and digital studio in Lisbon and Berlin.\",\n}\n\nexport default function Page() {\n  return <StudioHome />\n}\n",
      "type": "registry:page",
      "target": "app/studio/page.tsx"
    },
    {
      "path": "registry/ballmac/app/studio/work/page.tsx",
      "content": "// Ballmac UI: Studio template route. https://ui.ballmac.com/templates/template-studio\nimport type { Metadata } from \"next\"\n\nimport { StudioWork } from \"@/components/ballmac/templates/studio/studio-work\"\n\nexport const metadata: Metadata = {\n  title: \"Work · Hollis & Vane\",\n  description: \"Identities, websites and campaigns.\",\n}\n\nexport default function Page() {\n  return <StudioWork />\n}\n",
      "type": "registry:page",
      "target": "app/studio/work/page.tsx"
    },
    {
      "path": "registry/ballmac/app/studio/work/[slug]/page.tsx",
      "content": "// Ballmac UI: Studio template route. https://ui.ballmac.com/templates/template-studio\nimport type { Metadata } from \"next\"\n\nimport { StudioProject } from \"@/components/ballmac/templates/studio/studio-project\"\n\nexport const metadata: Metadata = {\n  title: \"North Coast Rail · Hollis & Vane\",\n  description: \"A railway identity that moves.\",\n}\n\n// One layout for every project. Load the project by params.slug from your data when you add more.\nexport default function Page() {\n  return <StudioProject />\n}\n",
      "type": "registry:page",
      "target": "app/studio/work/[slug]/page.tsx"
    },
    {
      "path": "registry/ballmac/app/studio/services/page.tsx",
      "content": "// Ballmac UI: Studio template route. https://ui.ballmac.com/templates/template-studio\nimport type { Metadata } from \"next\"\n\nimport { StudioServices } from \"@/components/ballmac/templates/studio/studio-services\"\n\nexport const metadata: Metadata = {\n  title: \"Services · Hollis & Vane\",\n  description: \"Brand identity, digital design, campaigns and a retained studio.\",\n}\n\nexport default function Page() {\n  return <StudioServices />\n}\n",
      "type": "registry:page",
      "target": "app/studio/services/page.tsx"
    },
    {
      "path": "registry/ballmac/app/studio/contact/page.tsx",
      "content": "// Ballmac UI: Studio template route. https://ui.ballmac.com/templates/template-studio\nimport type { Metadata } from \"next\"\n\nimport { StudioContact } from \"@/components/ballmac/templates/studio/studio-contact\"\n\nexport const metadata: Metadata = {\n  title: \"Contact · Hollis & Vane\",\n  description: \"Send us a brief. We reply within two working days.\",\n}\n\nexport default function Page() {\n  return <StudioContact />\n}\n",
      "type": "registry:page",
      "target": "app/studio/contact/page.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "template",
      "agency",
      "studio",
      "branding",
      "portfolio",
      "typography",
      "creative",
      "contact"
    ],
    "version": "1.0.0",
    "updated": "2026-10-01",
    "ai": {
      "summary": "Installs a five-page agency site. Change the content in studio-data.ts, the palette in studioCss, and the painted posters in StudioArt (or swap them for real imagery).",
      "whenToUse": [
        "Agencies, studios and collectives that want a loud, typographic site",
        "Any site where the work should carry the page"
      ],
      "whenNotToUse": [
        "A personal portfolio with long case studies (use template-portfolio)"
      ],
      "composesWith": [
        "marquee",
        "copy-button",
        "faq-2"
      ],
      "a11y": [
        {
          "keys": "Tab through the work reel",
          "action": "Shows the poster beside the focused row, the same as hovering it"
        },
        {
          "keys": "Enter / Space on a service",
          "action": "Opens or closes it"
        }
      ],
      "customization": [
        "Replace StudioArt with real images",
        "Edit studioCss for the light and dark palettes",
        "Tune the headline width in the studioDisplay class"
      ]
    },
    "examples": [
      "template-studio-demo",
      "template-studio-work",
      "template-studio-project",
      "template-studio-services",
      "template-studio-contact"
    ],
    "url": "https://ui.ballmac.com/components/template-studio"
  },
  "docs": "Pages are at /studio, /studio/work, /studio/work/[slug], /studio/services and /studio/contact. Edit projects and services in studio-data.ts; the palette is the studioCss string in studio-theme.tsx.",
  "categories": [
    "templates"
  ],
  "type": "registry:block"
}