{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agency-core",
  "title": "Agency: content and kit",
  "description": "Every word of the agency template and its client projects, plus the palette, tile mark, column, section labels, page heading, entrance, the drawn client sites and the notes' covers its sections share.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://eased.dev/r/eased-tokens.json",
    "https://eased.dev/r/eased-motion.json"
  ],
  "files": [
    {
      "path": "src/registry/eased/templates/agency/content.ts",
      "content": "/**\n * Every word and number on the agency template, in one place. Rename the studio, rewrite the\n * copy and swap the projects here; the sections only lay it out. Tessera is a made-up\n * independent design studio in Lisbon, and its clients are made up too, so the reel, the work,\n * the services and the latest notes tell one consistent story.\n *\n * Links starting with \"/\" are pages of the template (the site prefixes them in a preview);\n * \"/#\" links are sections of the home page. Point the rest at your own pages.\n *\n * The pages past the home page keep their longer copy in files of their own, beside this one:\n * content-work.ts (the case studies), content-services.ts, content-studio.ts,\n * content-careers.ts, content-journal.ts, content-contact.ts and content-legal.ts.\n */\n\nexport const brand = {\n  name: \"Tessera\",\n  /** The wordmark across the top of every page, set bold in the sans, mixed case. */\n  wordmark: { name: \"Tessera\", trade: \"Studio\" },\n  company: \"Tessera Studio, Lda.\",\n  city: \"Lisbon\",\n  /** The studio's IANA time zone, for the clock in the footer. */\n  timeZone: \"Europe/Lisbon\",\n};\n\n/** The line above the wordmark: when the studio can start, its pages as one list, and the way in. */\nexport const nav = {\n  availability: \"Taking new projects from February\",\n  links: [\n    { href: \"/work\", label: \"Work\" },\n    { href: \"/services\", label: \"Services\" },\n    { href: \"/studio\", label: \"Studio\" },\n    { href: \"/journal\", label: \"Journal\" },\n  ],\n  action: { href: \"/contact\", label: \"Start a project\" },\n};\n\n/* ---------- Client work ---------------------------------------------------------------- */\n\n/** The client sites the reel plays and the work grid shows. `art` picks the drawing in client-art.tsx. */\nexport type Discipline = \"Brand\" | \"Product\" | \"Website\" | \"Campaign\";\n\nexport type Project = {\n  id: \"kite\" | \"halden\" | \"atlas\" | \"mono\" | \"fenna\" | \"ostra\";\n  client: string;\n  /** What the browser's address bar types in the reel. */\n  domain: string;\n  title: string;\n  year: number;\n  disciplines: readonly Discipline[];\n  /** One line for the reel's caption and the work page. */\n  line: string;\n};\n\nexport const projects: readonly Project[] = [\n  {\n    id: \"kite\",\n    client: \"Kite\",\n    domain: \"kite.health\",\n    title: \"A breathing app that slows you down\",\n    year: 2026,\n    disciplines: [\"Product\", \"Brand\"],\n    line: \"Brand, app and launch site for a guided breathing app, built around one slow animation.\",\n  },\n  {\n    id: \"halden\",\n    client: \"Halden Coffee\",\n    domain: \"haldencoffee.com\",\n    title: \"A roaster's shop, rebuilt around the bag\",\n    year: 2026,\n    disciplines: [\"Website\", \"Brand\"],\n    line: \"A new identity and online shop for a small roaster on the Atlantic coast.\",\n  },\n  {\n    id: \"atlas\",\n    client: \"Atlas Rail\",\n    domain: \"atlasrail.eu\",\n    title: \"Booking night trains across Europe\",\n    year: 2025,\n    disciplines: [\"Product\", \"Website\"],\n    line: \"Journey planning and booking for a sleeper network of 41 cities.\",\n  },\n  {\n    id: \"mono\",\n    client: \"Mono Records\",\n    domain: \"mono.fm\",\n    title: \"An independent label, loud on purpose\",\n    year: 2025,\n    disciplines: [\"Brand\", \"Campaign\"],\n    line: \"Identity, sleeve system and release campaigns for a Porto record label.\",\n  },\n  {\n    id: \"fenna\",\n    client: \"Fenna\",\n    domain: \"fenna.co\",\n    title: \"Skincare with fewer steps\",\n    year: 2024,\n    disciplines: [\"Brand\", \"Website\", \"Campaign\"],\n    line: \"Packaging, site and launch films for a three-product skincare line.\",\n  },\n  {\n    id: \"ostra\",\n    client: \"Ostra\",\n    domain: \"ostra.bank\",\n    title: \"A bank account for people who make things\",\n    year: 2024,\n    disciplines: [\"Product\", \"Brand\"],\n    line: \"Brand and mobile banking for freelancers, from first sketch to public launch.\",\n  },\n];\n\n/** The reel plays these, in order. Three or four reads best: each holds for a few seconds. */\nexport const reel = {\n  label: \"Case study reel\",\n  projects: [\"halden\", \"kite\", \"atlas\", \"mono\"] as const satisfies readonly Project[\"id\"][],\n  /** The browser's empty tab before the first site loads. */\n  start: { title: \"Selected work\", line: \"Four recent projects, played in turn.\" },\n};\n\n/* ---------- Home page sections --------------------------------------------------------- */\n\n/** The page's h1, under the reel: what the studio is, in one sentence. */\nexport const intro = {\n  title: \"We are an independent design studio making brands, products and websites for people who care how things feel.\",\n  link: { href: \"/work\", label: \"View all work\" },\n};\n\nexport const work = {\n  label: \"Selected work\",\n  all: \"All\",\n  filters: [\"Brand\", \"Product\", \"Website\", \"Campaign\"] as const satisfies readonly Discipline[],\n  more: { href: \"/work\", label: \"See the full index\" },\n};\n\nexport const services = {\n  label: \"Services\",\n  title: \"We make brands, products, websites and campaigns.\",\n  body: \"One team takes a project from the first workshop to launch day, so the identity, the product and the site are designed together and tell the same story.\",\n  link: { href: \"/contact\", label: \"Start a project\" },\n  /** Each row links to its service's page, /services/<slug>, written in content-services.ts. */\n  more: \"More on\",\n  items: [\n    {\n      id: \"brand\",\n      slug: \"brand-identity\",\n      title: \"Brand identity\",\n      body: \"Naming, logo and type, colour and motion, and the guidelines that keep them consistent once we hand over.\",\n      deliverables: [\"Strategy\", \"Identity\", \"Motion\", \"Guidelines\"],\n    },\n    {\n      id: \"product\",\n      slug: \"product-design\",\n      title: \"Product design\",\n      body: \"Apps and tools designed with your engineers, prototyped early and tested with the people who will use them.\",\n      deliverables: [\"Research\", \"Prototyping\", \"Interface\", \"Design systems\"],\n    },\n    {\n      id: \"websites\",\n      slug: \"websites\",\n      title: \"Websites\",\n      body: \"Marketing sites and shops that load fast and are easy to edit, built in React and handed over with the source.\",\n      deliverables: [\"Content design\", \"Art direction\", \"Development\", \"CMS\"],\n    },\n    {\n      id: \"campaigns\",\n      slug: \"campaigns\",\n      title: \"Campaigns\",\n      body: \"Launch films, social and print for the day a product ships, made from the same system as the brand.\",\n      deliverables: [\"Concept\", \"Film\", \"Social\", \"Print\"],\n    },\n  ] as const,\n};\n\nexport type ServiceId = (typeof services.items)[number][\"id\"];\n\nexport const clients = {\n  label: \"Clients\",\n  title: \"Studios, start-ups and a few institutions we have worked with.\",\n  /** Set as wordmarks in the grid, each in the type its brand uses. */\n  names: [\n    { name: \"Kite\", style: \"sans\" },\n    { name: \"Halden\", style: \"serif\" },\n    { name: \"Atlas Rail\", style: \"sans\" },\n    { name: \"mono\", style: \"mono\" },\n    { name: \"Fenna\", style: \"serif\" },\n    { name: \"Ostra\", style: \"sans\" },\n    { name: \"Museu Maré\", style: \"serif\" },\n    { name: \"Northfold\", style: \"sans\" },\n  ] as const,\n  numbers: [\n    { value: 11, label: \"Years as an independent studio\" },\n    { value: 140, label: \"Projects launched\" },\n    { value: 24, label: \"Designers and engineers in Lisbon\" },\n  ],\n};\n\n/**\n * The careers and latest strip: press, writing and open roles, newest first. Each note links to\n * its journal post or job post; `art` picks its drawn cover in covers.tsx.\n */\nexport const latest = {\n  label: \"Latest\",\n  title: \"News, notes and open roles.\",\n  items: [\n    { kind: \"Press\", date: \"2026-10-02\", title: \"Studio of the year at the Lisbon Design Awards\", art: \"award\", href: \"/journal/studio-of-the-year\" },\n    { kind: \"Article\", date: \"2026-09-18\", title: \"How we design motion before we design screens\", art: \"motion\", href: \"/journal/motion-before-screens\" },\n    { kind: \"Careers\", date: \"2026-09-04\", title: \"We are hiring a senior product designer\", art: \"roles\", href: \"/careers/senior-product-designer\" },\n    { kind: \"Article\", date: \"2026-08-21\", title: \"Kite, one year on: what slow design measured\", art: \"rings\", href: \"/journal/kite-one-year-on\" },\n  ] as const,\n  roles: {\n    title: \"Open roles\",\n    /** Each role's post is /careers/<slug>, written in content-careers.ts. */\n    items: [\n      { slug: \"senior-product-designer\", title: \"Senior product designer\", place: \"Lisbon or remote in Europe\" },\n      { slug: \"design-engineer\", title: \"Design engineer\", place: \"Lisbon\" },\n      { slug: \"producer\", title: \"Producer\", place: \"Lisbon\" },\n    ] as const,\n    link: { href: \"mailto:jobs@tessera.studio\", label: \"jobs@tessera.studio\" },\n  },\n};\n\nexport type RoleSlug = (typeof latest.roles.items)[number][\"slug\"];\n\nexport const contact = {\n  label: \"Contact\",\n  title: \"Have a project in mind?\",\n  body: \"Tell us what you are making and when it needs to ship. We reply to every message within two working days.\",\n  email: \"hello@tessera.studio\",\n  copy: \"Copy email\",\n  copied: \"Copied\",\n};\n\nexport const footer = {\n  newsletter: {\n    title: \"Field notes, four times a year\",\n    body: \"What we made, what we learned and what we are reading. No more than that.\",\n    placeholder: \"Email address\",\n    action: \"Subscribe\",\n    done: \"Thanks. The next issue comes out in January.\",\n  },\n  columns: [\n    {\n      title: \"Studio\",\n      links: [\n        { href: \"/work\", label: \"Work\" },\n        { href: \"/services\", label: \"Services\" },\n        { href: \"/studio\", label: \"Studio\" },\n        { href: \"/journal\", label: \"Journal\" },\n        { href: \"/careers\", label: \"Careers\" },\n      ],\n    },\n    {\n      title: \"Contact\",\n      links: [\n        { href: \"/contact\", label: \"Start a project\" },\n        { href: \"mailto:hello@tessera.studio\", label: \"hello@tessera.studio\" },\n        { href: \"mailto:jobs@tessera.studio\", label: \"jobs@tessera.studio\" },\n      ],\n    },\n  ],\n  address: [\"Rua da Boavista 71\", \"1200-066 Lisbon\"],\n  year: 2026,\n  legal: [\n    { href: \"/privacy\", label: \"Privacy\" },\n    { href: \"/imprint\", label: \"Imprint\" },\n  ],\n};\n\n/* ---------- The work page -------------------------------------------------------------- */\n\nexport const workPage = {\n  title: \"Work\",\n  lead: \"Six of the projects we are proudest of, from first identity to public launch.\",\n  columns: { client: \"Client\", project: \"Project\", disciplines: \"Disciplines\", year: \"Year\" },\n};\n\n/* ---------- Not found ------------------------------------------------------------------ */\n\nexport const notFound = {\n  code: \"404\",\n  title: \"This page is not in the studio.\",\n  body: \"It may have moved when we rebuilt the site, or the link has a typo. These are the pages people usually want.\",\n  links: [\n    { href: \"/\", label: \"Home\" },\n    { href: \"/work\", label: \"Work\" },\n    { href: \"/journal\", label: \"Journal\" },\n    { href: \"/contact\", label: \"Start a project\" },\n  ],\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-work.ts",
      "content": "/**\n * The case studies: one page per project, /work/<id>, in the order of `projects` in content.ts\n * (the last one's next project is the first). The client, title, year and disciplines come from\n * the project; this file holds the longer story. Credits name the people on content-studio.ts.\n */\n\nimport type { Project } from \"./content\";\n\nexport type CaseStudy = {\n  /** How long it took and who worked on it, for the facts beside the title. */\n  duration: string;\n  team: string;\n  challenge: readonly string[];\n  /** What the studio made, one line each. */\n  made: readonly { title: string; body: string }[];\n  /** Three numbers measured after launch; `value` is set as written. */\n  outcomes: readonly { value: string; label: string }[];\n  credits: readonly { role: string; names: readonly string[] }[];\n};\n\nexport const caseStudyPage = {\n  back: { href: \"/work\", label: \"Work\" },\n  facts: { client: \"Client\", year: \"Year\", disciplines: \"Disciplines\", duration: \"Duration\", team: \"Team\" },\n  challenge: \"The challenge\",\n  made: \"What we made\",\n  outcomes: \"Outcomes\",\n  credits: \"Credits\",\n  next: \"Next project\",\n};\n\nexport const caseStudies: Record<Project[\"id\"], CaseStudy> = {\n  kite: {\n    duration: \"16 weeks\",\n    team: \"Five people\",\n    challenge: [\n      \"Kite came to us with a working prototype and a problem: people opened it, breathed for thirty seconds and left. Every competing app counted streaks and minutes, and Kite's founders worried that a calm app had to shout to be noticed.\",\n      \"We wanted to prove the opposite. If the app slowed down first, people would follow it. The whole brief became one question: what does four minutes of breathing look like, if nothing on the screen is in a hurry?\",\n    ],\n    made: [\n      { title: \"One slow shape\", body: \"A single ring that expands for four seconds and settles for six. It is the logo, the loading state, the timer and the launch film.\" },\n      { title: \"The app\", body: \"Three screens instead of eleven. No streaks, no badges; a short line after each session about how your breath changed.\" },\n      { title: \"The brand\", body: \"A green that fades to blue over the day, a rounded sans and a voice that never uses an exclamation mark.\" },\n      { title: \"Launch site\", body: \"One page that breathes with you before it asks you to download anything.\" },\n    ],\n    outcomes: [\n      { value: \"4 min\", label: \"Average session, up from 40 seconds in the prototype\" },\n      { value: \"61%\", label: \"Still breathing daily after 30 days\" },\n      { value: \"4.8\", label: \"App Store rating from 12,000 reviews\" },\n    ],\n    credits: [\n      { role: \"Creative direction\", names: [\"Inês Carvalho\"] },\n      { role: \"Product design\", names: [\"Daniel Okoro\", \"Aiko Tanaka\"] },\n      { role: \"Motion\", names: [\"Leah Whitfield\"] },\n      { role: \"Engineering\", names: [\"Rui Antunes\"] },\n    ],\n  },\n  halden: {\n    duration: \"12 weeks\",\n    team: \"Four people\",\n    challenge: [\n      \"Halden roasts every Tuesday in a converted boat shed in Ericeira. Their coffee was sold in three cafés and through a shop that looked like every other shop: a grid of identical bags on white.\",\n      \"The owners wanted to sell more online without losing what made the cafés work, which is talking to the person who roasted your beans. The bag was the one thing every customer held, so we started there.\",\n    ],\n    made: [\n      { title: \"A new bag\", body: \"One colour per origin, a hand-set serif name and the roast date printed large, so the bag tells you when it was made before it tells you anything else.\" },\n      { title: \"The identity\", body: \"A low terracotta sun from the view out of the roastery door, used as the mark and as the shop's one illustration.\" },\n      { title: \"The shop\", body: \"Built around the three bags on the shelf this week, with a subscription that ships the day after roasting.\" },\n      { title: \"Tasting notes\", body: \"Short notes written by the roasters themselves, edited by us into one consistent voice.\" },\n    ],\n    outcomes: [\n      { value: \"2.3×\", label: \"Online orders in the first quarter after launch\" },\n      { value: \"38%\", label: \"Of orders are now subscriptions\" },\n      { value: \"1.4 s\", label: \"Largest paint on a mid-range phone\" },\n    ],\n    credits: [\n      { role: \"Design direction\", names: [\"Marta Lopes\"] },\n      { role: \"Brand design\", names: [\"Hugo Matos\"] },\n      { role: \"Development\", names: [\"Rui Antunes\"] },\n      { role: \"Production\", names: [\"Sofia Brandão\"] },\n    ],\n  },\n  atlas: {\n    duration: \"9 months\",\n    team: \"Eight people\",\n    challenge: [\n      \"Atlas Rail runs sleeper trains between 41 cities. Booking one meant seven steps, three of them about berths, and most people gave up on their phone and finished on a laptop, if they finished at all.\",\n      \"Night trains are sold as a feeling, but bought as a timetable. The product had to make a 22:40 departure from Lisbon feel simple without hiding the connections, the cabins and the border rules underneath.\",\n    ],\n    made: [\n      { title: \"Journey planner\", body: \"Search by city, not by station, with overnight legs and day connections shown on one line.\" },\n      { title: \"Berth picker\", body: \"A drawn carriage you tap to choose a berth, replacing three forms. It shows who you share with before you pay.\" },\n      { title: \"Design system\", body: \"Sixty components shared by the app, the site and the ticket machines, documented with Atlas's engineers.\" },\n      { title: \"Marketing site\", body: \"Route pages for every city pair, written to answer the questions people searched for before they booked.\" },\n    ],\n    outcomes: [\n      { value: \"3\", label: \"Steps from search to berth, down from seven\" },\n      { value: \"27%\", label: \"More bookings completed on phones\" },\n      { value: \"41\", label: \"Cities bookable in one journey\" },\n    ],\n    credits: [\n      { role: \"Partner\", names: [\"Tomás Reis\"] },\n      { role: \"Product design\", names: [\"Daniel Okoro\", \"Aiko Tanaka\"] },\n      { role: \"Design engineering\", names: [\"Rui Antunes\"] },\n      { role: \"Production\", names: [\"Sofia Brandão\"] },\n    ],\n  },\n  mono: {\n    duration: \"Ongoing since 2025\",\n    team: \"Three people\",\n    challenge: [\n      \"Mono Records is a Porto label with twelve artists and no two of them sound alike. Every sleeve had been designed from scratch by a different friend, which made each record beautiful and the label invisible.\",\n      \"They asked for an identity loud enough to be recognised from across a record shop, and loose enough that the artists would still want their name on it.\",\n    ],\n    made: [\n      { title: \"The name as the logo\", body: \"Set in lower case, as large as the format allows, cropped differently on every release.\" },\n      { title: \"Sleeve system\", body: \"One grid for LPs, singles, cassettes and streaming covers. The artist owns the top half; the label owns the bottom.\" },\n      { title: \"Release campaigns\", body: \"Posters, short films and a social kit for each record, built from the sleeve in a day rather than a month.\" },\n      { title: \"Black and yellow\", body: \"Two colours that print cheaply on any stock and stay readable on a phone screen in a dark club.\" },\n    ],\n    outcomes: [\n      { value: \"12\", label: \"Releases on the sleeve system so far\" },\n      { value: \"3\", label: \"Pressings sold out in their first week\" },\n      { value: \"1 day\", label: \"From finished sleeve to full campaign\" },\n    ],\n    credits: [\n      { role: \"Creative direction\", names: [\"Inês Carvalho\"] },\n      { role: \"Brand design\", names: [\"Hugo Matos\"] },\n      { role: \"Motion\", names: [\"Leah Whitfield\"] },\n    ],\n  },\n  fenna: {\n    duration: \"20 weeks\",\n    team: \"Six people\",\n    challenge: [\n      \"Fenna started with three products and a promise that a routine should take two minutes. The category it was entering sells ten-step routines with photographs of dewy skin.\",\n      \"The founders had already chosen their formulas. They needed the packaging, the site and the launch to say less, and to make saying less look like a choice rather than a budget.\",\n    ],\n    made: [\n      { title: \"Packaging\", body: \"Three soft bottles in three heights, so the routine reads left to right on any shelf.\" },\n      { title: \"The site\", body: \"A shop with three products and one page that explains the whole routine in two minutes, timed.\" },\n      { title: \"Launch films\", body: \"Two films shot in one bathroom over one morning, with nothing on screen but hands and the bottles.\" },\n      { title: \"Voice\", body: \"Short, warm sentences and an italic serif for the one line on each bottle.\" },\n    ],\n    outcomes: [\n      { value: \"18,000\", label: \"Visitors in the first week\" },\n      { value: \"2 min\", label: \"Routine, timed on the site\" },\n      { value: \"54%\", label: \"Of first orders were the full set of three\" },\n    ],\n    credits: [\n      { role: \"Design direction\", names: [\"Marta Lopes\"] },\n      { role: \"Packaging\", names: [\"Hugo Matos\"] },\n      { role: \"Film\", names: [\"Leah Whitfield\"] },\n      { role: \"Development\", names: [\"Rui Antunes\"] },\n      { role: \"Production\", names: [\"Sofia Brandão\"] },\n    ],\n  },\n  ostra: {\n    duration: \"11 months\",\n    team: \"Seven people\",\n    challenge: [\n      \"Ostra is a bank for freelancers: illustrators, developers, photographers, people who invoice. Its founders had a banking licence, a team of engineers and nothing yet that a customer could see.\",\n      \"Most banks for freelancers look like accounting software. Ostra wanted the opposite: an account that feels like it belongs to someone who makes things, and that still does the taxes properly.\",\n    ],\n    made: [\n      { title: \"The brand\", body: \"A deep green and a mint card, a name in lower case, and a voice that explains money without jargon.\" },\n      { title: \"Mobile banking\", body: \"Accounts, invoices and a tax pot that sets money aside from every payment, designed and tested over eleven months.\" },\n      { title: \"Onboarding\", body: \"Account opening in two minutes, with the identity check moved to after you have seen the app.\" },\n      { title: \"Design system\", body: \"A component library shared with Ostra's team, who have shipped forty releases on it since.\" },\n    ],\n    outcomes: [\n      { value: \"40,000\", label: \"Accounts opened in the first year\" },\n      { value: \"2 min\", label: \"To open an account, from download\" },\n      { value: \"4.7\", label: \"App Store rating\" },\n    ],\n    credits: [\n      { role: \"Partner\", names: [\"Tomás Reis\"] },\n      { role: \"Creative direction\", names: [\"Inês Carvalho\"] },\n      { role: \"Product design\", names: [\"Daniel Okoro\"] },\n      { role: \"Design engineering\", names: [\"Rui Antunes\"] },\n    ],\n  },\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-work.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-services.ts",
      "content": "/**\n * The services pages: the index at /services and one page per service, /services/<slug>. Each\n * service's title, line and deliverables come from `services` in content.ts; this file holds\n * the longer copy and the projects each page points to.\n */\n\nimport type { Project, ServiceId } from \"./content\";\n\nexport const servicesPage = {\n  title: \"Services\",\n  lead: \"Four things we do, usually together. Most clients start with one and stay for the next.\",\n  back: { href: \"/services\", label: \"Services\" },\n  labels: { includes: \"What you get\", process: \"How it runs\", engagement: \"A typical project\", work: \"Recent work\" },\n  facts: { duration: \"Duration\", team: \"Team\", start: \"Next start\" },\n};\n\nexport type ServicePage = {\n  lead: string;\n  body: readonly string[];\n  /** One line per deliverable, in the order of the service's badges. */\n  includes: readonly { title: string; body: string }[];\n  process: readonly { title: string; weeks: string; body: string }[];\n  engagement: { duration: string; team: string; start: string };\n  work: readonly Project[\"id\"][];\n};\n\nexport const servicePages: Record<ServiceId, ServicePage> = {\n  brand: {\n    lead: \"A name, a mark and a way of speaking that still feel right in five years, with the guidelines that keep them that way after we leave.\",\n    body: [\n      \"We start with what the company believes and who it is for, written down in one page you can disagree with. Everything after, from the logo to the motion, is checked against that page.\",\n      \"We design for the places a brand is actually seen: a phone screen, a shop shelf, a two-second clip. A logo that only works on a presentation slide is not finished.\",\n    ],\n    includes: [\n      { title: \"Strategy\", body: \"Workshops, interviews with your customers and a one-page position the whole team can repeat.\" },\n      { title: \"Identity\", body: \"Logo, type, colour, layout and voice, drawn and tested on real material.\" },\n      { title: \"Motion\", body: \"How the brand moves: a logo animation, transitions and a set of timing rules.\" },\n      { title: \"Guidelines\", body: \"A living site, not a PDF, with every file your team and suppliers need.\" },\n    ],\n    process: [\n      { title: \"Listen\", weeks: \"Weeks 1 to 2\", body: \"Workshops with your team and calls with your customers. We share what we heard before we design anything.\" },\n      { title: \"Explore\", weeks: \"Weeks 3 to 6\", body: \"Three routes, each shown on real material, then one chosen together.\" },\n      { title: \"Refine\", weeks: \"Weeks 7 to 10\", body: \"The chosen route drawn properly, tested in print and on screens.\" },\n      { title: \"Hand over\", weeks: \"Weeks 11 to 12\", body: \"Guidelines, files and a session with whoever looks after the brand next.\" },\n    ],\n    engagement: { duration: \"10 to 14 weeks\", team: \"Three to four people\", start: \"February 2027\" },\n    work: [\"halden\", \"mono\", \"kite\"],\n  },\n  product: {\n    lead: \"Apps and tools designed with your engineers in the room, prototyped in the first fortnight and tested with the people who will use them.\",\n    body: [\n      \"We work in your tools and your sprints. A designer from our side joins your stand-ups, and our design engineers write the front-end code alongside your team when that is faster than a handover.\",\n      \"We test early and often. Every project has real users on a real prototype by week three, so the arguments are about what people did, not what we think they will do.\",\n    ],\n    includes: [\n      { title: \"Research\", body: \"Interviews, analytics and a map of what people are trying to get done.\" },\n      { title: \"Prototyping\", body: \"Working prototypes in code or Figma, tested with your users every fortnight.\" },\n      { title: \"Interface\", body: \"Every screen and state, including the empty, slow and broken ones.\" },\n      { title: \"Design systems\", body: \"Components and tokens your engineers can build with, documented in their repository.\" },\n    ],\n    process: [\n      { title: \"Frame\", weeks: \"Weeks 1 to 2\", body: \"Research, a map of the product and a list of the decisions that matter most.\" },\n      { title: \"Prototype\", weeks: \"Weeks 3 to 6\", body: \"The riskiest flows built first and put in front of users.\" },\n      { title: \"Design\", weeks: \"Weeks 7 to 16\", body: \"The full product, screen by screen, in your sprints with your engineers.\" },\n      { title: \"Ship\", weeks: \"From week 16\", body: \"Launch support, then a design system your team can grow on its own.\" },\n    ],\n    engagement: { duration: \"4 to 12 months\", team: \"Four to eight people\", start: \"March 2027\" },\n    work: [\"atlas\", \"ostra\", \"kite\"],\n  },\n  websites: {\n    lead: \"Marketing sites and shops that load fast, read well and are easy for your team to change, built in React and handed over with the source.\",\n    body: [\n      \"We write the words with you before we design the pages, because most websites are slow to make for the same reason: nobody agreed what they should say.\",\n      \"We build what we design. The site you approve in the browser is the site that launches, and the code is yours, in your repository, from day one.\",\n    ],\n    includes: [\n      { title: \"Content design\", body: \"A site map, the copy for every page and a plan for what changes after launch.\" },\n      { title: \"Art direction\", body: \"Photography, illustration or drawn work, briefed and directed by us.\" },\n      { title: \"Development\", body: \"Next.js and React, fast on a mid-range phone, accessible to WCAG 2.2 AA.\" },\n      { title: \"CMS\", body: \"An editor your team can use without us, set up with the fields each page needs.\" },\n    ],\n    process: [\n      { title: \"Write\", weeks: \"Weeks 1 to 3\", body: \"The site map and the words, agreed before any page is designed.\" },\n      { title: \"Design\", weeks: \"Weeks 4 to 7\", body: \"Pages designed in the browser, reviewed on your own phone.\" },\n      { title: \"Build\", weeks: \"Weeks 6 to 10\", body: \"Development alongside design, with a preview link that updates every day.\" },\n      { title: \"Launch\", weeks: \"Weeks 11 to 12\", body: \"Redirects, analytics, a training session and two weeks of fixes.\" },\n    ],\n    engagement: { duration: \"8 to 12 weeks\", team: \"Three to five people\", start: \"February 2027\" },\n    work: [\"halden\", \"fenna\", \"atlas\"],\n  },\n  campaigns: {\n    lead: \"Launch films, social and print for the day a product ships, made from the same system as the brand so the launch looks like the company.\",\n    body: [\n      \"A launch is the first time most people meet a brand. We plan it as one idea in many sizes: the film, the posts, the poster in the station and the email that goes out at nine.\",\n      \"We keep crews small and shoots short. Most of our films are made in a day, in one place, with the product as the only star.\",\n    ],\n    includes: [\n      { title: \"Concept\", body: \"One idea for the launch, written as a sentence before it is drawn.\" },\n      { title: \"Film\", body: \"Launch films from six seconds to two minutes, shot and edited by our team.\" },\n      { title: \"Social\", body: \"Posts and stories for every channel, with templates for the weeks after.\" },\n      { title: \"Print\", body: \"Posters, packaging inserts and anything else that needs paper.\" },\n    ],\n    process: [\n      { title: \"Idea\", weeks: \"Weeks 1 to 2\", body: \"Three concepts as sentences and sketches, one chosen.\" },\n      { title: \"Make\", weeks: \"Weeks 3 to 5\", body: \"Shoot, edit and design every format from one plan.\" },\n      { title: \"Launch\", weeks: \"Week 6\", body: \"Everything scheduled, with someone from our team on call on the day.\" },\n    ],\n    engagement: { duration: \"4 to 8 weeks\", team: \"Three to six people\", start: \"February 2027\" },\n    work: [\"mono\", \"fenna\"],\n  },\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-services.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-studio.ts",
      "content": "/**\n * The studio page, /studio: who works here, how a project runs, the numbers and the clients.\n * The numbers and the client names are `clients` in content.ts, so the home page and this page\n * always agree; the people here are the ones credited on the case studies.\n */\n\nexport const studio = {\n  title: \"Studio\",\n  lead: \"Tessera is twenty-four designers and engineers in one room on Rua da Boavista. We started in 2015 with three people and a borrowed table, and we have stayed independent since.\",\n  people: {\n    label: \"People\",\n    title: \"The people you will work with.\",\n    body: \"Every project is led by someone on this page. We do not sell a partner and send a junior; the person in the first meeting is in the last one.\",\n    items: [\n      { name: \"Inês Carvalho\", role: \"Founder, creative director\" },\n      { name: \"Tomás Reis\", role: \"Partner, technology\" },\n      { name: \"Marta Lopes\", role: \"Design director\" },\n      { name: \"Daniel Okoro\", role: \"Product design lead\" },\n      { name: \"Sofia Brandão\", role: \"Head of production\" },\n      { name: \"Rui Antunes\", role: \"Lead design engineer\" },\n      { name: \"Leah Whitfield\", role: \"Motion designer\" },\n      { name: \"Hugo Matos\", role: \"Brand designer\" },\n      { name: \"Aiko Tanaka\", role: \"Product designer\" },\n      { name: \"Joana Pires\", role: \"Studio manager\" },\n    ],\n    more: \"And fourteen more designers, engineers and producers.\",\n  },\n  approach: {\n    label: \"How we work\",\n    title: \"Small teams, one room, and the work shown every week.\",\n    items: [\n      { title: \"One team, start to finish\", body: \"The people who run the first workshop design the product and build the site. Nothing is handed from one department to another.\" },\n      { title: \"Shown, not presented\", body: \"Every Thursday you see the work as it is, in the browser or on paper. No polished decks, no surprises at the end.\" },\n      { title: \"Built by designers\", body: \"A third of the studio writes code. What you approve on a screen is what ships, not a picture of it.\" },\n      { title: \"Handed over properly\", body: \"Source files, code and guidelines are yours from day one, and we train whoever looks after them next.\" },\n    ],\n  },\n  careers: { title: \"Want to work here?\", body: \"We have three open roles.\", link: { href: \"/careers\", label: \"See open roles\" } },\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-studio.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-careers.ts",
      "content": "/**\n * The careers pages: the index at /careers and one post per open role, /careers/<slug>. The\n * roles' titles and places are `latest.roles` in content.ts, so the home page lists the same\n * three; this file holds each post and what every role shares.\n */\n\nimport type { RoleSlug } from \"./content\";\n\nexport const careers = {\n  title: \"Careers\",\n  lead: \"We hire slowly and keep people for a long time: the average designer here has been with us for five years. If one of these roles reads like you, write to us.\",\n  roles: { label: \"Open roles\", apply: \"Apply\" },\n  benefits: {\n    label: \"What everyone gets\",\n    items: [\n      { title: \"Four-day weeks in August\", body: \"The studio runs Monday to Thursday all summer, on full pay.\" },\n      { title: \"€1,500 a year to learn\", body: \"Spend it on courses, books or conferences, without asking.\" },\n      { title: \"Health insurance\", body: \"Private cover for you and your family from your first day.\" },\n      { title: \"Lunch on Thursdays\", body: \"The whole studio eats together after the weekly show and tell.\" },\n    ],\n  },\n  process: {\n    label: \"How we hire\",\n    items: [\n      { title: \"Your work\", body: \"Send three projects and a few lines about you. We reply to everyone within two weeks.\" },\n      { title: \"A conversation\", body: \"An hour with two of us about your work, on a call or in the studio.\" },\n      { title: \"A paid day\", body: \"One day working on a real brief with the team, paid at the role's day rate.\" },\n      { title: \"An answer\", body: \"Within a week of your day with us, whichever way it goes.\" },\n    ],\n  },\n  back: { href: \"/careers\", label: \"Careers\" },\n  post: {\n    facts: { team: \"Team\", place: \"Where\", type: \"Type\", salary: \"Salary\" },\n    labels: { role: \"The role\", doing: \"What you will do\", bring: \"What you bring\", bonus: \"Good to have\" },\n    apply: {\n      title: \"Apply for this role\",\n      body: \"Write to us with three projects you are proud of and a few lines about why this role. No cover letter, no CV required.\",\n      email: \"jobs@tessera.studio\",\n    },\n  },\n};\n\nexport type JobPost = {\n  team: string;\n  type: string;\n  salary: string;\n  intro: readonly string[];\n  doing: readonly string[];\n  bring: readonly string[];\n  bonus: readonly string[];\n};\n\nexport const jobPosts: Record<RoleSlug, JobPost> = {\n  \"senior-product-designer\": {\n    team: \"Product, with Daniel Okoro\",\n    type: \"Full time, permanent\",\n    salary: \"€58,000 to €72,000\",\n    intro: [\n      \"Our product team designs apps and tools that people use every day: a bank, a sleeper-train booking system, a breathing app. We are looking for a senior designer to lead projects like these from the first research call to launch.\",\n      \"You will work closely with our design engineers and with the client's own team, and you will be the person they trust to make the call when a decision needs making.\",\n    ],\n    doing: [\n      \"Lead one or two product projects at a time, with a team of two to four.\",\n      \"Run research and usability sessions, and turn what you hear into decisions.\",\n      \"Design flows, interfaces and the design systems behind them.\",\n      \"Present work every week to clients, in the browser rather than in decks.\",\n      \"Help hire and mentor the designers who join after you.\",\n    ],\n    bring: [\n      \"Six or more years designing digital products that shipped.\",\n      \"A portfolio that shows how you reasoned, not only how it looked.\",\n      \"Comfort with prototyping in code or in Figma, whichever is faster.\",\n      \"Clear writing in English. Portuguese is welcome but not needed.\",\n    ],\n    bonus: [\"Experience in finance, travel or health products.\", \"Some front-end code, enough to read a pull request.\"],\n  },\n  \"design-engineer\": {\n    team: \"Engineering, with Rui Antunes\",\n    type: \"Full time, permanent\",\n    salary: \"€52,000 to €66,000\",\n    intro: [\n      \"A third of the studio writes code. Our design engineers build the websites we launch and the prototypes we test, and they sit with the designers, not in another room.\",\n      \"We are looking for an engineer who cares about the last ten percent: the easing on a menu, the focus ring, the page that still loads on a slow train.\",\n    ],\n    doing: [\n      \"Build marketing sites and shops in Next.js, React and Tailwind.\",\n      \"Prototype product ideas quickly enough to test them the same week.\",\n      \"Write the motion: transitions, reveals and the moments clients remember.\",\n      \"Keep our sites fast and accessible to WCAG 2.2 AA.\",\n      \"Hand over clean code and train client teams to work in it.\",\n    ],\n    bring: [\n      \"Four or more years of front-end work in React and TypeScript.\",\n      \"A good eye: you notice when a margin is two pixels off.\",\n      \"Experience with animation on the web, in CSS or Motion.\",\n      \"A habit of testing on real phones, not only in the browser's device mode.\",\n    ],\n    bonus: [\"Experience with a headless CMS.\", \"Some design work of your own.\"],\n  },\n  producer: {\n    team: \"Production, with Sofia Brandão\",\n    type: \"Full time, permanent\",\n    salary: \"€42,000 to €52,000\",\n    intro: [\n      \"Producers keep our projects calm. You will plan the work, look after the budget and make sure the client always knows what happens next.\",\n      \"It is a role for someone organised and kind, who enjoys being the person everyone checks with before they say yes.\",\n    ],\n    doing: [\n      \"Run three to five projects at once, from the proposal to the final invoice.\",\n      \"Plan each week with the team and share it with the client every Monday.\",\n      \"Look after budgets and scope, and say so early when either is at risk.\",\n      \"Organise shoots, workshops and launches, including the ones abroad.\",\n    ],\n    bring: [\n      \"Three or more years producing projects in a studio or agency.\",\n      \"Calm, clear writing and a talent for the short, useful email.\",\n      \"Comfort with numbers: estimates, timesheets and forecasts.\",\n      \"Fluent English and Portuguese.\",\n    ],\n    bonus: [\"Experience producing film or photography shoots.\", \"A driving licence, for the shoots.\"],\n  },\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-careers.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-journal.ts",
      "content": "/**\n * The journal: the index at /journal and one post per entry, /journal/<slug>, newest first.\n * The home page's latest strip links the same posts. A post's body is a list of blocks: a\n * paragraph, a subheading, a pull quote or a list. `art` picks its drawn cover in covers.tsx.\n */\n\nimport type { NoteArt } from \"./covers\";\n\nexport const journal = {\n  title: \"Journal\",\n  lead: \"Notes on what we made, what we learned making it and the occasional piece of news.\",\n  back: { href: \"/journal\", label: \"Journal\" },\n  by: \"By\",\n  read: \"min read\",\n  more: \"More from the journal\",\n};\n\nexport type PostBlock =\n  | { type: \"p\"; text: string }\n  | { type: \"h2\"; text: string }\n  | { type: \"quote\"; text: string; by?: string }\n  | { type: \"list\"; items: readonly string[] };\n\nexport type Post = {\n  slug: string;\n  kind: \"Press\" | \"Article\";\n  date: string;\n  title: string;\n  dek: string;\n  author: string;\n  minutes: number;\n  art: NoteArt;\n  body: readonly PostBlock[];\n};\n\nexport const posts: readonly Post[] = [\n  {\n    slug: \"studio-of-the-year\",\n    kind: \"Press\",\n    date: \"2026-10-02\",\n    title: \"Studio of the year at the Lisbon Design Awards\",\n    dek: \"The jury named Tessera studio of the year, and gave the Atlas Rail booking flow the award for digital product.\",\n    author: \"Joana Pires\",\n    minutes: 2,\n    art: \"award\",\n    body: [\n      { type: \"p\", text: \"On Thursday night at the Cinema São Jorge, the Lisbon Design Awards named Tessera studio of the year. It is the first time the award has gone to a studio of fewer than fifty people, and the first time it has gone to us.\" },\n      { type: \"p\", text: \"The jury looked at everything we launched between September 2025 and August 2026. They singled out three projects: the Atlas Rail booking flow, the Halden Coffee shop and the Mono Records sleeve system. Atlas also won the award for digital product.\" },\n      { type: \"quote\", text: \"The work is quiet in the best sense. Nothing is there to impress the jury; everything is there for the person using it.\", by: \"From the jury's citation\" },\n      { type: \"h2\", text: \"Who made it\" },\n      { type: \"p\", text: \"An award like this goes to a studio, but it is earned by people. Twenty-four of them, and the clients who trusted us with work that mattered to them. Thank you to Atlas Rail, Halden, Mono, Kite, Fenna and Ostra for letting us show what we made together.\" },\n      { type: \"p\", text: \"The trophy is on the shelf by the door. Come and see it; we are taking new projects from February.\" },\n    ],\n  },\n  {\n    slug: \"motion-before-screens\",\n    kind: \"Article\",\n    date: \"2026-09-18\",\n    title: \"How we design motion before we design screens\",\n    dek: \"We used to animate a product once it was finished. Now we decide how it moves first, and the screens follow.\",\n    author: \"Leah Whitfield\",\n    minutes: 6,\n    art: \"motion\",\n    body: [\n      { type: \"p\", text: \"For years our process ended with motion. The screens were designed, approved and built, and then someone was asked to make them feel nice. By then every decision that shapes how a product moves had already been made by accident.\" },\n      { type: \"p\", text: \"On Kite we tried the opposite. Before we drew a single screen, we spent a week deciding how one shape should breathe: four seconds out, six seconds back, no bounce. Everything else in the app was then designed to stay out of that shape's way.\" },\n      { type: \"h2\", text: \"Start with one moment\" },\n      { type: \"p\", text: \"Every product has one moment people remember. For Kite it was the breath; for Atlas Rail it was the berth you choose before you pay. We find that moment in the first workshop and prototype it in code in the first fortnight.\" },\n      { type: \"list\", items: [\"Name the one moment the product is about.\", \"Prototype it in the browser, not in a video.\", \"Write its timing down as numbers the engineers can use.\", \"Design the screens around it, never on top of it.\"] },\n      { type: \"h2\", text: \"Then take motion away\" },\n      { type: \"p\", text: \"Once the one moment is right, most of our work is removing motion. Menus open at once. Lists do not stagger when you type. A button presses down in a tenth of a second and that is all it does.\" },\n      { type: \"quote\", text: \"If everything moves, nothing does. The one moment only works because the rest of the product is still.\" },\n      { type: \"p\", text: \"It also makes the products kinder. People who turn on reduced motion get the same product, with every move replaced by a short fade, because we planned for it on day one instead of patching it at the end.\" },\n    ],\n  },\n  {\n    slug: \"kite-one-year-on\",\n    kind: \"Article\",\n    date: \"2026-08-21\",\n    title: \"Kite, one year on: what slow design measured\",\n    dek: \"A year after Kite's first beta, we looked at what people actually did with an app that refuses to hurry them.\",\n    author: \"Daniel Okoro\",\n    minutes: 5,\n    art: \"rings\",\n    body: [\n      { type: \"p\", text: \"Kite went into beta in August 2025 and launched publicly in January. We designed it around a bet: that a breathing app with no streaks, no badges and no reminders would keep people longer than one that nagged them. A year later, Kite's team shared the numbers with us.\" },\n      { type: \"h2\", text: \"What we measured\" },\n      { type: \"list\", items: [\"The average session is four minutes. In the prototype it was forty seconds.\", \"61% of people who breathe on their first day are still breathing daily a month later.\", \"Fewer than 2% of people turned on the optional reminder.\"] },\n      { type: \"p\", text: \"That last number surprised us most. We had argued for weeks about whether to include a reminder at all. In the end we hid it in settings, and almost nobody went looking for it.\" },\n      { type: \"h2\", text: \"What we would change\" },\n      { type: \"p\", text: \"Not everything worked. The evening session, which slows the shape further, is used by only one person in ten. People told us it felt too slow to follow, which is a lesson we will take into the next version: slow is a choice, not a virtue on its own.\" },\n      { type: \"quote\", text: \"We were told calm does not sell. A year in, it is the reason people stay.\", by: \"Mira Haas, Kite's co-founder\" },\n      { type: \"p\", text: \"We are working with Kite again this winter, on a version for children. It will breathe at the same pace.\" },\n    ],\n  },\n  {\n    slug: \"mono-sleeve-system\",\n    kind: \"Article\",\n    date: \"2026-07-16\",\n    title: \"Twelve sleeves, one grid: the Mono Records system\",\n    dek: \"How we gave a Porto record label an identity without taking the sleeves away from its artists.\",\n    author: \"Hugo Matos\",\n    minutes: 4,\n    art: \"sleeves\",\n    body: [\n      { type: \"p\", text: \"Mono Records has twelve artists, and before last year it had twelve ways of designing a sleeve. Every record was beautiful. None of them looked like Mono.\" },\n      { type: \"h2\", text: \"Split the sleeve in two\" },\n      { type: \"p\", text: \"The system is one rule. The artist owns the top half of every sleeve and can put anything there: a photograph, a painting, nothing at all. The label owns the bottom half, where the name is set in lower case, as large as the format allows, and cropped differently every time.\" },\n      { type: \"list\", items: [\"One grid for LPs, singles, cassettes and streaming covers.\", \"Two colours, black and yellow, that print cheaply on any stock.\", \"A release number on every sleeve, so the shelf reads in order.\"] },\n      { type: \"quote\", text: \"The artists stopped asking for exceptions once they saw the shelf. Twelve records side by side make a better poster than any one of them.\", by: \"Inês Carvalho\" },\n      { type: \"p\", text: \"Twelve releases in, the system has not needed a single change. The next one, Lumen's Tidal Hours, is out on vinyl on 14 November.\" },\n    ],\n  },\n];\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-journal.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-contact.ts",
      "content": "/**\n * The contact page, /contact: the email, a project enquiry form and the studio's address. The\n * email and address are the ones on the home page and in the footer. Wire the form's `onSubmit`\n * in enquiry.tsx to your inbox or CRM.\n */\n\nexport const contactPage = {\n  title: \"Start a project\",\n  lead: \"Tell us what you are making and when it needs to ship. We read every enquiry and reply within two working days.\",\n  email: { label: \"Email\", href: \"mailto:hello@tessera.studio\", value: \"hello@tessera.studio\" },\n  form: {\n    label: \"Project enquiry\",\n    fields: {\n      name: { label: \"Your name\", placeholder: \"Ana Ferreira\", required: \"Tell us your name.\" },\n      email: { label: \"Email\", placeholder: \"ana@company.com\", required: \"Enter your email address.\", invalid: \"That address looks incomplete.\" },\n      company: { label: \"Company\", placeholder: \"Optional\" },\n      service: { label: \"What do you need?\", placeholder: \"Choose a service\", required: \"Choose what you need, or Not sure yet.\" },\n      budget: { label: \"Budget\", placeholder: \"Choose a range\", required: \"Choose a range, or Not sure yet.\" },\n      timeline: { label: \"When should it launch?\", placeholder: \"Choose a timeline\", required: \"Choose a timeline.\" },\n      message: { label: \"About the project\", placeholder: \"What are you making, who is it for, and what does good look like?\", required: \"Tell us a little about the project.\", short: \"A few more words would help us reply properly.\" },\n    },\n    services: [\"Brand identity\", \"Product design\", \"Website\", \"Campaign\", \"Not sure yet\"],\n    budgets: [\"Under €25,000\", \"€25,000 to €50,000\", \"€50,000 to €100,000\", \"Over €100,000\", \"Not sure yet\"],\n    timelines: [\"Within a month\", \"In one to three months\", \"In three to six months\", \"Later than that\"],\n    submit: \"Send enquiry\",\n    note: \"We only use these details to reply to you.\",\n    sent: {\n      title: \"Thank you. Your enquiry is with us.\",\n      body: \"Someone from the studio will reply within two working days, usually sooner. If it is urgent, write to hello@tessera.studio.\",\n      again: \"Send another\",\n    },\n  },\n  visit: {\n    label: \"Visit\",\n    address: [\"Tessera Studio\", \"Rua da Boavista 71, 2nd floor\", \"1200-066 Lisbon, Portugal\"],\n    hours: \"Monday to Friday, 9:30 to 18:30\",\n    directions: \"Five minutes from Cais do Sodré station. Ring the bell marked Tessera.\",\n  },\n  jobs: { label: \"Jobs\", body: \"Looking for work rather than offering it?\", link: { href: \"/careers\", label: \"See open roles\" } },\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-contact.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/content-legal.ts",
      "content": "/**\n * The legal pages, /privacy and /imprint: plain-English sample text with a note at the top.\n * Replace both with your own before you launch.\n */\n\nexport type LegalDoc = {\n  title: string;\n  updated: string;\n  intro: string;\n  sections: readonly { title: string; body: readonly string[] }[];\n};\n\nexport const legalNote = \"Sample text. Replace with your own before you launch; this is not legal advice.\";\nexport const legalUpdated = \"Last updated\";\n\nexport const legal: Record<\"privacy\" | \"imprint\", LegalDoc> = {\n  privacy: {\n    title: \"Privacy\",\n    updated: \"2026-10-01\",\n    intro: \"This page explains what personal data Tessera Studio, Lda. collects through this website, why, and what you can ask us to do with it.\",\n    sections: [\n      {\n        title: \"Who we are\",\n        body: [\"Tessera Studio, Lda. is the controller of your data. You can reach us at Rua da Boavista 71, 1200-066 Lisbon, Portugal, or at privacy@tessera.studio.\"],\n      },\n      {\n        title: \"What we collect\",\n        body: [\n          \"When you send a project enquiry: your name, email, company and what you tell us about the project.\",\n          \"When you subscribe to field notes: your email address.\",\n          \"When you apply for a role: your email, your work and anything else you choose to send.\",\n          \"We do not use advertising cookies. We count visits with a privacy-friendly analytics tool that stores no personal data.\",\n        ],\n      },\n      {\n        title: \"Why we use it\",\n        body: [\"To reply to you, to send the newsletter you asked for, and to consider your application. Our legal basis is your consent or the steps needed before entering a contract with you.\"],\n      },\n      {\n        title: \"How long we keep it\",\n        body: [\n          \"Enquiries that do not become projects are deleted after two years. Applications are deleted after twelve months unless you ask us to keep them longer. You can unsubscribe from field notes at any time from any issue.\",\n        ],\n      },\n      {\n        title: \"Who we share it with\",\n        body: [\"Only the providers that run our email and website, all within the European Union or covered by an adequacy decision. We never sell your data.\"],\n      },\n      {\n        title: \"Your rights\",\n        body: [\n          \"You can ask to see, correct or delete your data, to restrict how we use it or to take a copy with you. Write to privacy@tessera.studio and we will answer within a month.\",\n          \"If you are unhappy with our answer, you can complain to the Comissão Nacional de Proteção de Dados (CNPD), Portugal's data protection authority.\",\n        ],\n      },\n    ],\n  },\n  imprint: {\n    title: \"Imprint\",\n    updated: \"2026-10-01\",\n    intro: \"The company responsible for this website.\",\n    sections: [\n      {\n        title: \"Company\",\n        body: [\"Tessera Studio, Lda.\", \"Rua da Boavista 71, 2nd floor\", \"1200-066 Lisbon, Portugal\"],\n      },\n      {\n        title: \"Registration\",\n        body: [\"Registered at the Commercial Registry of Lisbon under number 515 482 907, which is also our tax number (NIPC).\", \"VAT number: PT 515 482 907\", \"Share capital: €5,000\"],\n      },\n      {\n        title: \"Managing directors\",\n        body: [\"Inês Carvalho and Tomás Reis\"],\n      },\n      {\n        title: \"Contact\",\n        body: [\"hello@tessera.studio\", \"+351 21 340 7712\"],\n      },\n      {\n        title: \"Responsible for content\",\n        body: [\"Inês Carvalho, at the address above.\"],\n      },\n      {\n        title: \"Dispute resolution\",\n        body: [\"We are not obliged to, and do not, take part in dispute resolution proceedings before a consumer arbitration board.\"],\n      },\n    ],\n  },\n};\n",
      "type": "registry:lib",
      "target": "@components/eased/templates/agency/content-legal.ts"
    },
    {
      "path": "src/registry/eased/templates/agency/kit.tsx",
      "content": "\"use client\";\n\nimport { motion } from \"motion/react\";\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport { enter, reducedFade, stagger, useSafeReducedMotion } from \"../../lib/motion\";\n\n/**\n * The pieces every section of the agency template shares: the page column, a section with its\n * small label, the one entrance (rise 8px, 80ms a line, once), the studio's palette and tile mark, the arrow link and the links\n * between the template's pages.\n */\n\n/** The page column: the full 1440px canvas less a 16px gutter (24px from sm), as a studio site runs edge to edge. */\nexport const COLUMN = \"mx-auto w-full max-w-[90rem] px-4 sm:px-6\";\n\n/** Small labels over sections (\"Selected work\", \"Services\"): 13px, muted, never caps above 12px (rule 48). */\nexport const LABEL = \"text-ui-sm font-medium text-text-muted\";\n\nconst Base = createContext(\"\");\n\n/** Where the template is mounted: \"\" in your app, the preview's path on eased.dev. */\nexport function BaseProvider({ base, children }: { base: string; children: ReactNode }) {\n  return <Base.Provider value={base}>{children}</Base.Provider>;\n}\n\n/** A content link with the base in front when it names a page (\"/work\", \"/#services\"). */\nexport function useHref() {\n  const base = useContext(Base);\n  return (href: string) => {\n    if (!base || !href.startsWith(\"/\")) return href;\n    if (href === \"/\") return base;\n    // \"/#services\" is the home page's section: base#services, with no slash before the hash.\n    return href.startsWith(\"/#\") ? `${base}${href.slice(1)}` : `${base}${href}`;\n  };\n}\n\n/**\n * Rises 8px and fades in the first time it scrolls into view, `index` lines after the first\n * (rule 89). Under reduced motion it only fades.\n */\nexport function Reveal({ index = 0, className = \"\", children }: { index?: number; className?: string; children: ReactNode }) {\n  const reduced = useSafeReducedMotion();\n  return (\n    <motion.div\n      className={className}\n      initial={reduced ? { opacity: 0 } : { opacity: 0, y: 8 }}\n      whileInView={{ opacity: 1, y: 0 }}\n      viewport={{ once: true, amount: 0.3 }}\n      transition={reduced ? reducedFade : { ...enter, duration: 0.4, delay: index * stagger.line }}\n    >\n      {children}\n    </motion.div>\n  );\n}\n\n/**\n * A section: an anchor for the nav, a small label on a rule, and the content. Sections are\n * 96px apart (64px on phones); the label sits on a hairline, as on a studio's index.\n */\nexport function Section({ id, label, children, className = \"\" }: { id: string; label: string; children: ReactNode; className?: string }) {\n  return (\n    <section id={id} aria-labelledby={`${id}-label`} className={`${COLUMN} scroll-mt-4 py-group sm:py-group-lg`}>\n      <h2 id={`${id}-label`} className={`${LABEL} border-t border-line-subtle pt-in`}>\n        {label}\n      </h2>\n      <div className={`mt-pad-lg ${className}`}>{children}</div>\n    </section>\n  );\n}\n\n/**\n * The studio's palette, set once on the page root: a deep ink blue for the showcase panel and\n * a vermilion tile for its marks (the wordmark's tile, the reel's progress and load lines). They\n * are the studio's brand, not UI states, so they hold in both themes; change them here. The\n * canvas is the theme's own, cooled 3% toward the ink so the page and the panel belong together.\n */\nexport const PALETTE = [\n  \"[--tessera-ink:oklch(27%_0.075_262)]\",\n  \"[--tessera-tile:oklch(65%_0.19_36)]\",\n  \"bg-[color-mix(in_oklab,var(--eased-bg),var(--tessera-ink)_3%)]\",\n].join(\" \");\n\n/**\n * The studio's mark: four tiles, one set in vermilion, as a tessera is one tile of a mosaic. It\n * sits in the wordmark and by the panel's label; it never moves.\n */\nexport function Tiles({ className = \"\" }: { className?: string }) {\n  return (\n    <svg viewBox=\"0 0 10 10\" className={`shrink-0 ${className}`} aria-hidden>\n      <rect x=\"0\" y=\"0\" width=\"4.5\" height=\"4.5\" rx=\"0.6\" fill=\"currentColor\" fillOpacity=\".35\" />\n      <rect x=\"5.5\" y=\"0\" width=\"4.5\" height=\"4.5\" rx=\"0.6\" fill=\"var(--tessera-tile)\" />\n      <rect x=\"0\" y=\"5.5\" width=\"4.5\" height=\"4.5\" rx=\"0.6\" fill=\"currentColor\" fillOpacity=\".35\" />\n      <rect x=\"5.5\" y=\"5.5\" width=\"4.5\" height=\"4.5\" rx=\"0.6\" fill=\"currentColor\" fillOpacity=\".35\" />\n    </svg>\n  );\n}\n\n/** A trailing arrow that nudges 2px on its link's hover: the template's one link glyph. */\nexport function Arrow({ className = \"\" }: { className?: string }) {\n  return (\n    <svg\n      viewBox=\"0 0 16 16\"\n      className={`size-4 shrink-0 transition-transform duration-(--eased-duration-fast) ease-standard group-hover-fine:translate-x-0.5 reduced:transition-none ${className}`}\n      fill=\"none\"\n      aria-hidden\n    >\n      <path d=\"M3.5 8h9M8.75 4.25 12.5 8l-3.75 3.75\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n    </svg>\n  );\n}\n\n/** A text link with the trailing arrow, underlined, for links inside prose and captions. */\nexport function ArrowLink({ href, children, className = \"\" }: { href: string; children: ReactNode; className?: string }) {\n  const to = useHref();\n  return (\n    <a\n      href={to(href)}\n      className={`group focus-ring inline-flex items-center gap-1.5 rounded-control-xs text-ui font-medium text-text underline decoration-line-strong underline-offset-4 transition-colors duration-(--eased-duration-fast) ease-standard hover-fine:decoration-text ${className}`}\n    >\n      {children}\n      <Arrow />\n    </a>\n  );\n}\n\n/**\n * A page's opening, past the home page: on a detail page a small link up to its index, then the\n * page's h1 (`#page-title`, for the page's own aria-labelledby) and a lead line, rising in as\n * one. `size` \"lg\" is an index (\"Services\", \"Journal\"); \"md\" a detail page whose title is a sentence.\n */\nexport function PageHead({\n  back,\n  title,\n  lead,\n  size = \"lg\",\n  children,\n}: {\n  back?: { href: string; label: string };\n  title: ReactNode;\n  lead?: ReactNode;\n  size?: \"lg\" | \"md\";\n  children?: ReactNode;\n}) {\n  const to = useHref();\n  return (\n    <Reveal className=\"flex flex-col gap-pad-base\">\n      {back && (\n        <a\n          href={to(back.href)}\n          className={`${LABEL} focus-ring self-start rounded-control-xs transition-colors duration-(--eased-duration-fast) ease-standard hover-fine:text-text`}\n        >\n          {back.label}\n        </a>\n      )}\n      <h1\n        id=\"page-title\"\n        className={`max-w-5xl font-display font-medium tracking-display text-balance text-text ${\n          size === \"lg\" ? \"text-display-5xl sm:text-display-6xl\" : \"text-display-4xl sm:text-display-5xl\"\n        }`}\n      >\n        {title}\n      </h1>\n      {lead && <p className=\"max-w-2xl text-ui-md text-pretty text-text-secondary sm:text-ui-lg\">{lead}</p>}\n      {children}\n    </Reveal>\n  );\n}\n\n/** Dates as the template writes them, \"2 Oct 2026\", from an ISO day read as UTC so the server and the visitor agree. */\nconst DATE = new Intl.DateTimeFormat(\"en-GB\", { day: \"numeric\", month: \"short\", year: \"numeric\", timeZone: \"UTC\" });\nexport const formatDay = (day: string) => DATE.format(new Date(`${day}T00:00:00Z`));\n",
      "type": "registry:component",
      "target": "@components/eased/templates/agency/kit.tsx"
    },
    {
      "path": "src/registry/eased/templates/agency/client-art.tsx",
      "content": "\"use client\";\n\nimport { motion, type Variants } from \"motion/react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport type { Project } from \"./content\";\n\n/**\n * The clients' sites, drawn: gradient, type and shapes, no photos. The reel plays them in its\n * browser and the work grid uses them as covers, so each one fills whatever box it is given and\n * lays itself out by its own width (container queries) and sizes its type in container units.\n *\n * They are pictures of other brands' work, like a screenshot, so their colours are the clients'\n * own and stay the same in light and dark: they are the one place the template uses literal\n * colours. Inside the reel, each `Line` rises in turn as the page loads (the reel's variants);\n * as a cover, nothing animates.\n *\n * Small text keeps a floor (9–12px) so a phone-width reel stays legible. A thumbnail sets\n * `--art-min: 0` on an ancestor to drop it, so the page shrinks whole, like a screenshot.\n */\n\nexport const LINE: Variants = {\n  hidden: { opacity: 0, y: 8 },\n  shown: { opacity: 1, y: 0 },\n};\n\n/** One piece of a drawn page that rises with the page's load (variants set by the reel). */\nfunction Line({ className = \"\", style, children }: { className?: string; style?: CSSProperties; children?: ReactNode }) {\n  return (\n    <motion.div variants={LINE} className={className} style={style}>\n      {children}\n    </motion.div>\n  );\n}\n\n/** A drawn page's top bar: its logo, and its links once the page is wide enough to show them. */\nfunction SiteNav({ logo, links, className = \"\" }: { logo: ReactNode; links: readonly string[]; className?: string }) {\n  return (\n    <Line className={`flex items-center justify-between gap-[3cqw] px-[4cqw] py-[3cqw] text-[length:max(calc(var(--art-min,1)*9px),1.6cqw)] ${className}`}>\n      {logo}\n      <span className=\"flex gap-[2.5cqw] @max-lg:hidden\">\n        {links.map((l) => (\n          <span key={l}>{l}</span>\n        ))}\n      </span>\n    </Line>\n  );\n}\n\nconst ROOT = \"@container absolute inset-0 flex flex-col overflow-hidden select-none\";\n\n/** Kite, a breathing app: a green field that fades to blue, rings breathing out from the word. */\nfunction Kite() {\n  return (\n    <div\n      className={ROOT}\n      style={{\n        color: \"oklch(95% 0.17 125)\",\n        background:\n          \"radial-gradient(70% 60% at 30% 35%, oklch(78% 0.17 135), transparent 70%), radial-gradient(80% 70% at 85% 100%, oklch(56% 0.15 255), transparent 75%), oklch(66% 0.15 160)\",\n      }}\n    >\n      <SiteNav logo={<span className=\"text-[length:max(calc(var(--art-min,1)*11px),2.2cqw)] font-semibold\">kite</span>} links={[\"Method\", \"Science\", \"Download\"]} />\n      <div className=\"relative flex flex-1 flex-col justify-center px-[6cqw]\">\n        <svg viewBox=\"0 0 200 200\" aria-hidden className=\"absolute top-1/2 left-[62%] w-[52cqw] -translate-x-1/2 -translate-y-1/2 opacity-40\">\n          {[36, 56, 76, 96].map((r) => (\n            <circle key={r} cx=\"100\" cy=\"100\" r={r} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"0.75\" />\n          ))}\n        </svg>\n        <Line className=\"relative text-[12cqw] leading-[0.9] font-semibold tracking-[-0.05em]\">breathe in</Line>\n        <Line className=\"relative mt-[3cqw] max-w-[44cqw] text-[length:max(calc(var(--art-min,1)*10px),2cqw)] leading-snug opacity-90\">\n          Four minutes, twice a day. Kite paces your breath with one slow shape.\n        </Line>\n      </div>\n      <Line className=\"flex items-center gap-[2cqw] px-[6cqw] pb-[5cqw] text-[length:max(calc(var(--art-min,1)*9px),1.6cqw)]\">\n        <span className=\"rounded-full px-[2.4cqw] py-[1cqw]\" style={{ background: \"oklch(95% 0.17 125)\", color: \"oklch(30% 0.08 160)\" }}>\n          Get the app\n        </span>\n        <span>Free for 14 days</span>\n      </Line>\n    </div>\n  );\n}\n\n/** Halden Coffee: paper, a low terracotta sun, a serif headline and the three bags. */\nfunction Halden() {\n  const ink = \"oklch(28% 0.05 45)\";\n  const bags = [\"oklch(62% 0.15 40)\", \"oklch(45% 0.07 150)\", \"oklch(30% 0.04 50)\"];\n  return (\n    <div className={ROOT} style={{ color: ink, background: \"oklch(95% 0.025 80)\" }}>\n      <SiteNav logo={<span className=\"font-serif text-[length:max(calc(var(--art-min,1)*12px),2.6cqw)]\">Halden</span>} links={[\"Shop\", \"Cafés\", \"Journal\", \"Bag (0)\"]} />\n      <div className=\"relative flex flex-1 items-center px-[6cqw]\">\n        <div aria-hidden className=\"absolute top-[8%] right-[8cqw] size-[30cqw] rounded-full\" style={{ background: \"oklch(66% 0.15 42)\" }} />\n        <div className=\"relative flex flex-col gap-[2.5cqw]\">\n          <Line className=\"max-w-[56cqw] font-serif text-[8cqw] leading-[0.95] tracking-[-0.02em]\">Roasted slowly, on the coast.</Line>\n          <Line className=\"text-[length:max(calc(var(--art-min,1)*9px),1.7cqw)]\">Single origins, roasted every Tuesday in Ericeira.</Line>\n        </div>\n      </div>\n      <Line className=\"flex gap-[3cqw] px-[6cqw] pb-[5cqw]\">\n        {bags.map((c, i) => (\n          <div key={c} className=\"flex h-[14cqw] w-[11cqw] flex-col justify-end rounded-t-[1.5cqw] rounded-b-[0.6cqw] p-[1.2cqw]\" style={{ background: c }}>\n            <div className=\"h-[2.4cqw] rounded-[0.4cqw]\" style={{ background: \"oklch(95% 0.025 80)\", opacity: 0.9 - i * 0.1 }} />\n          </div>\n        ))}\n      </Line>\n    </div>\n  );\n}\n\n/** Atlas Rail: a night-blue map of routes and stations, the headline and tonight's departures. */\nfunction Atlas() {\n  const routes = [\n    { d: \"M10 150 C 70 120, 110 140, 160 90 S 260 40, 330 60\", c: \"oklch(74% 0.16 55)\" },\n    { d: \"M20 60 C 90 80, 150 60, 200 110 S 290 170, 340 150\", c: \"oklch(72% 0.12 195)\" },\n  ];\n  return (\n    <div className={ROOT} style={{ color: \"oklch(96% 0.01 250)\", background: \"radial-gradient(90% 80% at 75% 20%, oklch(33% 0.08 265), oklch(20% 0.05 262))\" }}>\n      <SiteNav logo={<span className=\"text-[length:max(calc(var(--art-min,1)*11px),2.1cqw)] font-medium\">Atlas Rail</span>} links={[\"Routes\", \"Sleepers\", \"Passes\", \"Sign in\"]} />\n      <svg viewBox=\"0 0 350 200\" aria-hidden className=\"absolute right-0 bottom-[18%] w-[68cqw] opacity-90\">\n        {routes.map((r) => (\n          <path key={r.d} d={r.d} fill=\"none\" stroke={r.c} strokeWidth=\"2.5\" strokeLinecap=\"round\" />\n        ))}\n        {[\n          [10, 150],\n          [160, 90],\n          [330, 60],\n          [20, 60],\n          [200, 110],\n          [340, 150],\n        ].map(([x, y]) => (\n          <circle key={`${x}-${y}`} cx={x} cy={y} r=\"4.5\" fill=\"oklch(20% 0.05 262)\" stroke=\"currentColor\" strokeWidth=\"2\" />\n        ))}\n      </svg>\n      <div className=\"relative flex flex-1 flex-col justify-center px-[5cqw]\">\n        <Line className=\"max-w-[46cqw] text-[6.4cqw] leading-[1] font-medium tracking-[-0.03em]\">Night trains across Europe.</Line>\n      </div>\n      <Line className=\"relative mx-[5cqw] mb-[5cqw] grid max-w-[54cqw] gap-[0.8cqw] font-mono text-[length:max(calc(var(--art-min,1)*8px),1.45cqw)] tabular-nums\">\n        {[\n          [\"22:40\", \"Lisbon → Madrid\", \"On time\"],\n          [\"23:05\", \"Paris → Berlin\", \"Platform 4\"],\n        ].map(([t, r, s]) => (\n          <div key={t} className=\"grid grid-cols-[auto_1fr_auto] gap-[2cqw] border-t border-current/20 pt-[0.8cqw]\">\n            <span>{t}</span>\n            <span>{r}</span>\n            <span style={{ color: \"oklch(80% 0.13 75)\" }}>{s}</span>\n          </div>\n        ))}\n      </Line>\n    </div>\n  );\n}\n\n/** Mono Records: black and yellow, the name as big as the page, a record sliding out of its sleeve. */\nfunction Mono() {\n  const yellow = \"oklch(90% 0.18 102)\";\n  return (\n    <div className={ROOT} style={{ color: yellow, background: \"oklch(16% 0.005 100)\" }}>\n      <SiteNav logo={<span className=\"font-mono text-[length:max(calc(var(--art-min,1)*10px),1.8cqw)]\">mono.fm</span>} links={[\"Releases\", \"Artists\", \"Shop\"]} />\n      <div className=\"relative flex-1\">\n        <div\n          aria-hidden\n          className=\"absolute top-[-6%] right-[-16cqw] size-[42cqw] rounded-full\"\n          style={{\n            background: `radial-gradient(circle, ${yellow} 0 16%, oklch(16% 0.005 100) 16.5% 18%, transparent 18.5%), repeating-radial-gradient(circle, oklch(24% 0.005 100) 0 0.5cqw, oklch(19% 0.005 100) 0.5cqw 1cqw)`,\n          }}\n        />\n        <Line className=\"absolute top-[6%] left-[4cqw] max-w-[40cqw] font-mono text-[length:max(calc(var(--art-min,1)*9px),1.6cqw)] leading-snug\">\n          New: Lumen, Tidal Hours (LP). Out on vinyl 14 November.\n        </Line>\n      </div>\n      <Line className=\"relative px-[3cqw] pb-[2cqw] text-[36cqw] leading-[0.78] font-bold tracking-[-0.07em]\">mono</Line>\n    </div>\n  );\n}\n\n/** Fenna: blush paper, three soft bottles standing in a row and a quiet italic line. */\nfunction Fenna() {\n  const ink = \"oklch(32% 0.04 20)\";\n  const bottles = [\n    { h: \"34cqw\", c: \"oklch(84% 0.06 25)\" },\n    { h: \"26cqw\", c: \"oklch(90% 0.04 60)\" },\n    { h: \"30cqw\", c: \"oklch(76% 0.07 15)\" },\n  ];\n  return (\n    <div className={ROOT} style={{ color: ink, background: \"radial-gradient(80% 70% at 70% 80%, oklch(90% 0.05 25), oklch(95% 0.02 40))\" }}>\n      <SiteNav logo={<span className=\"font-serif text-[length:max(calc(var(--art-min,1)*12px),2.6cqw)]\">Fenna</span>} links={[\"Routine\", \"Ingredients\", \"Shop\"]} />\n      <div className=\"flex flex-1 items-end justify-between gap-[4cqw] px-[6cqw] pb-[6cqw]\">\n        <div className=\"flex flex-col gap-[2cqw] self-center\">\n          <Line className=\"font-serif text-[7cqw] leading-[1] italic\">Less, but daily.</Line>\n          <Line className=\"max-w-[34cqw] text-[length:max(calc(var(--art-min,1)*9px),1.6cqw)]\">Three products. One routine. Two minutes.</Line>\n        </div>\n        <Line className=\"flex items-end gap-[2cqw]\">\n          {bottles.map((b) => (\n            <div key={b.c} className=\"flex w-[9cqw] flex-col items-center\" style={{ height: b.h }}>\n              <div className=\"h-[3cqw] w-[4cqw] rounded-t-[0.6cqw]\" style={{ background: ink }} />\n              <div className=\"w-full flex-1 rounded-[3cqw]\" style={{ background: b.c }} />\n            </div>\n          ))}\n        </Line>\n      </div>\n    </div>\n  );\n}\n\n/** Ostra: deep green, a mint card on its edge and the promise in two lines. */\nfunction Ostra() {\n  const mint = \"oklch(90% 0.08 165)\";\n  return (\n    <div className={ROOT} style={{ color: mint, background: \"radial-gradient(90% 90% at 20% 10%, oklch(42% 0.09 165), oklch(26% 0.05 170))\" }}>\n      <SiteNav logo={<span className=\"text-[length:max(calc(var(--art-min,1)*11px),2.2cqw)] font-semibold tracking-[-0.03em]\">ostra</span>} links={[\"Accounts\", \"Invoices\", \"Taxes\", \"Log in\"]} />\n      <div className=\"relative flex flex-1 items-center px-[6cqw]\">\n        <Line className=\"max-w-[50cqw] text-[6.2cqw] leading-[1.02] font-medium tracking-[-0.03em]\">Banking for people who make things.</Line>\n        <div\n          aria-hidden\n          className=\"absolute top-1/2 right-[6cqw] flex aspect-[1.586] w-[36cqw] -translate-y-1/2 rotate-[-8deg] flex-col justify-between rounded-[2.4cqw] p-[2.6cqw]\"\n          style={{ background: `linear-gradient(135deg, ${mint}, oklch(78% 0.1 175))`, color: \"oklch(26% 0.05 170)\" }}\n        >\n          <span className=\"text-[length:max(calc(var(--art-min,1)*9px),1.8cqw)] font-semibold\">ostra</span>\n          <span className=\"h-[4cqw] w-[6cqw] rounded-[0.8cqw]\" style={{ background: \"oklch(26% 0.05 170 / 0.25)\" }} />\n          <span className=\"font-mono text-[length:max(calc(var(--art-min,1)*8px),1.5cqw)] tabular-nums\">•••• 4821</span>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nconst ART: Record<Project[\"id\"], () => ReactNode> = { kite: Kite, halden: Halden, atlas: Atlas, mono: Mono, fenna: Fenna, ostra: Ostra };\n\n/** A client's drawn site, filling its positioned parent. */\nexport function ClientArt({ id }: { id: Project[\"id\"] }) {\n  const Art = ART[id];\n  return <Art />;\n}\n",
      "type": "registry:component",
      "target": "@components/eased/templates/agency/client-art.tsx"
    },
    {
      "path": "src/registry/eased/templates/agency/covers.tsx",
      "content": "\"use client\";\n\n/**\n * The notes' small drawn covers, for the latest strip and the journal, drawn in the text and\n * brand inks so they follow the theme. Each fills its positioned parent and is hidden from\n * assistive tech; the note's title says what it is.\n */\n\nexport type NoteArt = \"award\" | \"motion\" | \"roles\" | \"rings\" | \"sleeves\";\n\nexport function NoteCover({ art }: { art: NoteArt }) {\n  if (art === \"award\")\n    return (\n      <div aria-hidden className=\"absolute inset-0 flex flex-col items-center justify-center gap-in\">\n        <svg viewBox=\"0 0 64 64\" className=\"w-1/4 text-brand-text\" fill=\"none\">\n          <circle cx=\"32\" cy=\"32\" r=\"30\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n          <path d=\"m32 16 4.4 9.6 10.4 1.1-7.8 7 2.2 10.3L32 38.8 22.8 44l2.2-10.3-7.8-7 10.4-1.1L32 16Z\" fill=\"currentColor\" />\n        </svg>\n        <span className=\"font-serif text-display-4xl tabular-nums\">2026</span>\n      </div>\n    );\n  if (art === \"motion\")\n    return (\n      <svg aria-hidden viewBox=\"0 0 100 100\" className=\"absolute inset-0 size-full\" fill=\"none\">\n        <path d=\"M14 80 C 44 80, 50 20, 86 20\" stroke=\"currentColor\" strokeOpacity=\".25\" strokeWidth=\".75\" />\n        {[0.08, 0.3, 0.55, 0.78, 0.94].map((t, i) => {\n          // Points along the curve at even times: they bunch where the curve eases.\n          const x = (1 - t) ** 3 * 14 + 3 * (1 - t) ** 2 * t * 44 + 3 * (1 - t) * t ** 2 * 50 + t ** 3 * 86;\n          const y = (1 - t) ** 3 * 80 + 3 * (1 - t) ** 2 * t * 80 + 3 * (1 - t) * t ** 2 * 20 + t ** 3 * 20;\n          return <circle key={t} cx={x} cy={y} r={2.5 + i * 0.4} fill=\"currentColor\" fillOpacity={0.3 + i * 0.17} />;\n        })}\n      </svg>\n    );\n  if (art === \"roles\")\n    return (\n      <svg aria-hidden viewBox=\"0 0 100 100\" className=\"absolute inset-0 size-full\">\n        {Array.from({ length: 25 }, (_, i) => (\n          <circle\n            key={i}\n            cx={22 + (i % 5) * 14}\n            cy={22 + Math.floor(i / 5) * 14}\n            r=\"4\"\n            className={i === 17 ? \"fill-brand\" : \"fill-current\"}\n            fillOpacity={i === 17 ? 1 : 0.18}\n          />\n        ))}\n      </svg>\n    );\n  if (art === \"sleeves\")\n    // Three sleeves fanned on a shelf, each split in two: the artist's half and the label's.\n    return (\n      <svg aria-hidden viewBox=\"0 0 100 100\" className=\"absolute inset-0 size-full\" fill=\"none\">\n        {[0, 1, 2].map((i) => (\n          <g key={i} transform={`translate(${24 + i * 10} ${24 + i * 6})`}>\n            <rect width=\"40\" height=\"40\" rx=\"1.5\" className=\"fill-surface-sunken\" stroke=\"currentColor\" strokeOpacity=\".35\" strokeWidth=\".75\" />\n            <rect y=\"20\" width=\"40\" height=\"20\" rx=\"1.5\" fill=\"currentColor\" fillOpacity={0.12 + i * 0.12} />\n          </g>\n        ))}\n      </svg>\n    );\n  return (\n    <svg aria-hidden viewBox=\"0 0 100 100\" className=\"absolute inset-0 size-full\" fill=\"none\">\n      {[10, 18, 26, 34, 42].map((r, i) => (\n        <circle key={r} cx=\"50\" cy=\"50\" r={r} stroke=\"currentColor\" strokeOpacity={0.5 - i * 0.08} strokeWidth=\".75\" />\n      ))}\n    </svg>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/eased/templates/agency/covers.tsx"
    }
  ],
  "meta": {
    "template": "agency",
    "kind": "template-lib",
    "industries": [
      "media"
    ]
  },
  "type": "registry:lib"
}