Static landing page for businesspowers. This is not a Claude Code plugin; no agents, no skills. Plugins live under ../plugins/.
See README.md for the user-facing quick start. This file is contributor context — human or agent — for working on the site itself.
The site is a thin consumer of the powers-landing-shell package. It has no local components, layouts, or styles. All rendering is done by the shell; the site supplies:
src/config.ts—SiteConfigobject:brand: 'businesspowers',brandSymbol: '%', plugin catalogs (agents, skills, sources), locale definitions.src/locales/{en,ua,pl}.ts— translation dictionaries thatsatisfies ShellTranslationfrom the shell.src/pages/index.astro— one-liner:<RedirectShell site={site} />.src/pages/[locale]/index.astro— one-liner:<PageShell lang={locale} t={t} site={site} />.
To override a section of the page, use PageShell's named slots — don't fork shell components into the site.
- The
brandSymbolis%, not§. - Disclaimer says "not tax or accounting advice", not "not legal advice".
- Copy is about sole-trader registration, tax calculations, reporting deadlines, invoicing, and investment declarations — not lawsuits, courts, or legal proceedings.
- Plugin catalogs (
UA_AGENTS,PL_AGENTS,UA_SKILLS,PL_SKILLS) and sources live insrc/config.ts. These arrays must match the actual contents of../plugins/ua/agents/,../plugins/pl/agents/,../plugins/ua/skills/,../plugins/pl/skills/. When the monorepo bumps plugin contents, update the arrays and the label maps in every locale dictionary. Thesatisfies Record<..., string>constraint makes missing keys a type error — lean on that rather than grepping. - Hero stats (agent count, skill count) are computed from array lengths by the shell — don't hardcode numbers.
- Text stays factual and in the working language of each locale's audience. Skip marketing superlatives.
- Don't invent features. The landing reflects what the plugins actually do; if a claim isn't backed by an existing agent or skill in
../plugins/, don't put it on the landing. - Disclaimer copy (
t.disclaimer.*) must say "not tax or accounting advice" and make clear that a qualified human accountant or tax advisor owns the final filing. This is non-negotiable — same rule as the plugins.
- Locales:
en,ua,pl. Default:en. URL path:/{locale}/. - Root
/is a minimal redirect page (src/pages/index.astro) —RedirectShellhandles<meta http-equiv="refresh">plus an inline JS that readsnavigator.languagesand replaces location. - Dictionaries:
src/locales/{en,ua,pl}.ts.en.tsdefines the shape viaexport type Translation = typeof en.ua.tsandpl.tsdeclareexport const <locale>: Translation = { ... }, so missing keys failastro check. - Update all three locales together. Translation tone per locale: UA dictionary in Ukrainian, PL in Polish, EN in English. Don't leak English into UA / PL (except verbatim code snippets like
/plugin install ua@businesspowers). - hreflang uses ISO 639-1 (
en,ukfor Ukrainian — noteuk, notua— andpl). Our URL path usesuafor namespace parity with the plugin. The mapping is defined in thelocalesarray inconfig.ts.
- Per-page
<title>and<meta description>come fromt.seoin each locale — keep them unique per locale and under ~160 chars where possible. - Social-card image is
public/og.png(1200x630), referenced as/og.pngfrom OG / Twitter tags. The source isscripts/build-og.mjs(SVG authored in-script with%symbol and "Sole-trader tax & accounting" headline, rasterized withsharp); the generated PNG is committed. Regenerate withpnpm build:ogwhenever the source SVG changes. src/pages/index.astrois a redirect shell — it carriesnoindex, followand is filtered out of the sitemap inastro.config.mjs. Don't let it drift back in or it'll duplicate the/en/canonical.- Hreflang alternates must use ISO 639-1 (
en,uk,pl) — see the i18n section.x-defaultpoints to/en/.
| Thing | Version |
|---|---|
| Astro | ^6.1.8 |
@astrojs/sitemap |
^3.7.2 |
@astrojs/check |
^0.9.8 |
| TypeScript | ~6.0.2 |
| Node | >= 22.12.0 |
| Firebase CLI | ^15.15.0 (devDependency) |
powers-landing-shell |
github:crankshift/powers-landing-shell |
Package manager: pnpm. Lockfile (pnpm-lock.yaml) is the source of truth.
pnpm build # astro check + astro build → dist/
pnpm run deploy # pnpm build + firebase deploy --only hosting:businesspowersFirebase context:
- Project:
landings-d3578 - Multi-site target:
businesspowers→https://businesspowers.web.app/ - Target + public dir configured in
firebase.json - Web-SDK config (public identifiers) in
.env; see.env.examplefor shape
Do
- Keep the site as a thin shell consumer. Data in
config.ts, labels inlocales/, rendering in the shell. - Use
PageShellnamed slots if you need to override a section for businesspowers specifically. - Pre-render everything — that's the whole point.
- Run
pnpm checkbefore committing.
Don't
- Don't add local components, layouts, or styles unless there's no slot or shell-level solution. The shell exists to keep sites DRY.
- Don't add a runtime framework. If you reach for React, stop and rethink.
- Don't fetch from
tax.gov.ua,podatki.gov.pl, or any other live tax source at build or runtime. The landing is static marketing; live data fetches belong in the plugins. - Don't pipe real client data into locale strings. Placeholders only (
[ПІБ],[imię i nazwisko], etc.) — same rule as the plugins. - Don't mix UA and PL content in one component. Keep parallel, jurisdiction-separated, like the plugins themselves.
- Don't commit
.env— it's git-ignored for a reason. Use.env.exampleto document shape.