Skip to content
2 changes: 1 addition & 1 deletion .claude/project/design/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ theme + the mechanism + one commented example hook **only**. Concrete brand
themes (real colours, fonts, logos) and their assets live in the **private infra
repo** and are mounted/selected per deployment. Never commit brand overlays or
real logos here (see `rules.md` → Tabus). The prototype's base64 brand logos
(MTC/MHZ/Lorenz) were deliberately not ported for this reason.
were deliberately not ported for this reason.

## Logo slot

Expand Down
114 changes: 114 additions & 0 deletions .claude/skills/design-adoption/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
name: design-adoption
description: >-
Adopt a shop's visual identity into the Revoco withdrawal form at deploy time.
Use when setting up a Revoco deployment and you want the form to match a
specific shop's website (colours, fonts, logo). Scans the shop URL, extracts
its corporate identity, and generates a ready-to-place `--wf-*` brand theme
overlay plus a placement report. Deploy-time authoring only — not app runtime.
---

# Design Adoption — brand a Revoco deployment from a shop URL

This skill helps an operator (or their agency) make the Revoco withdrawal form
look like a specific shop's website, without hand-writing CSS. You scan the shop,
extract its corporate identity, and hand it to the deterministic generator command
`revoco:make-theme`, which produces a validated theme overlay and tells the
operator exactly where to put it.

**You do not write the CSS yourself.** Your job is extraction + mapping; the
command renders and validates the overlay against Revoco's `--wf-*` token contract
(see `.claude/project/design/theming.md`). This keeps the output correct and a11y-safe.

## Guardrails (read first)

- **Operator-reviewed, never auto-published.** You produce a draft overlay + a
placement report. The operator places the file and reviews the rendered form
before it goes live. Never claim the result is final.
- **Never commit brand output to this public repo.** The overlay and any logo are
operator/brand assets — they belong in the operator's *private* infra repo or
deployment mount, selected per deployment via `APP_THEME`. Committing them here
violates the project's public/private split (`rules.md` → Tabus).
- **Do not fabricate brand values.** If you cannot confidently extract a colour or
font, ask the operator to confirm it rather than guessing.

## Procedure

### 1. Ask for the shop URL

Ask the operator for the website whose look the form should adopt (e.g. the shop's
home page). If they also have a specific brand/style guide page, take that too.

### 2. Scan and extract the corporate identity

Load the page and extract the brand signals. Use whatever browser/fetch tooling is
available (the Claude-in-Chrome tools give you *computed* styles, which are more
reliable than raw HTML; a plain fetch of the CSS is a fallback). Extract:

- **Colours** — the primary accent (buttons/links), its hover state, body text,
heading text, muted text, the card/surface background, and border colour.
- **Fonts** — the body font stack and the heading/display font stack.
- **Logo** — the URL of the shop's logo image.
- **Brand name** — the shop/company name.

Prefer values taken from computed styles of prominent elements (primary button,
headings, body). When a value is ambiguous, show the operator your candidate and
confirm before using it.

### 3. Map to `revoco:make-theme` options

Each extracted value maps to one command option (which maps to one `--wf-*` token):

| Extracted signal | Option |
|---|---|
| accent / primary | `--accent` (+ `--accent-hover`) |
| body text | `--fg` |
| heading text | `--heading-fg` |
| muted text | `--muted` |
| card / surface bg | `--card-bg` |
| border | `--card-border` |
| button bg / hover / text | `--btn-bg` / `--btn-hover` / `--btn-fg` |
| body font stack | `--font` |
| heading font stack | `--font-display` |
| logo URL | `--logo-url` (guidance only) |
| brand name | `--brand-name` (guidance only) |

Only pass the options you actually extracted — unset tokens correctly fall through
to the neutral default. Colours may be hex, `rgb()/rgba()`, `hsl()/hsla()` or a CSS
named colour; the command rejects anything malformed.

### 4. Run the generator

Choose a short lowercase `--slug` for the deployment (e.g. the brand name) and
write the overlay to a file with `--output`:

```
task artisan -- revoco:make-theme \
--slug=<brand> \
--accent='#c1121f' --accent-hover='#a00e19' \
--fg='#1a1a1a' --heading-fg='#111111' \
--card-bg='#ffffff' --card-border='#e5e5e5' \
--font='"Inter", system-ui, sans-serif' \
--font-display='"Poppins", sans-serif' \
--logo-url='https://shop.example/logo.svg' \
--brand-name='Example Shop' \
--output=./<brand>-theme.css
```

(Outside the Docker/`task` setup, call `php artisan revoco:make-theme …` directly.)
If the command reports an invalid value, fix that one input and re-run — it writes
nothing on a validation error.

### 5. Hand the result to the operator

Show the operator the generated `<brand>-theme.css` and the command's **placement
report** verbatim, then walk them through it:

1. Move the overlay into their private infra repo / deployment mount (never this repo).
2. Set `APP_THEME=<slug>` in the deployment `.env`.
3. Wire the logo via `REVOCO_LOGO_URL` (and `REVOCO_BRAND_NAME`) in `.env`.
4. **Review** the rendered form against the shop, and check focus-ring/text contrast
against the new accent before going live.

End by reminding them the result is a starting draft they own and must review — the
form's final appearance and any brand-asset licensing are their responsibility.
109 changes: 109 additions & 0 deletions .claude/skills/legal-extraction/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: legal-extraction
description: >-
Import an operator's existing Impressum and privacy-policy pages into Revoco's
legal settings at deploy time. Use when setting up a Revoco deployment and the
operator already has legal pages elsewhere (their shop, a CMS) that should
populate the withdrawal form's Impressum/Datenschutz. Scrapes the given URLs,
extracts the content, and loads it via `revoco:import-legal` for review in
Filament. Deploy-time authoring only — not app runtime.
---

# Legal Extraction — populate Revoco's legal pages from existing URLs

This skill helps an operator fill Revoco's Impressum (§ 5 DDG) and privacy policy
from pages they already publish, without hand-copying field by field. You scrape
the pages, map the content to a JSON payload, and hand it to the deterministic
`revoco:import-legal` command, which validates, sanitises and persists it into the
Filament-managed legal settings.

**You do not decide what is legally correct.** Your job is faithful extraction +
mapping; the command validates the shape and sanitises the HTML. The operator
reviews and owns the result.

## Guardrails (read first)

- **Operator-reviewed, never auto-published.** The command fills the settings; the
operator reviews and edits them in **Filament → Legal** before they go live. The
panel is the review gate — never present the import as final or legally correct.
- **The operator is the data controller.** Revoco makes no legal-correctness
guarantee. Extract only what is actually on the pages; **do not invent, complete
or "improve" legal fields.** Leave anything you cannot find out of the payload.
- **Never clobber reviewed text silently.** If the command refuses because a field
is already populated, stop and ask the operator before re-running with
`--overwrite`.
- **Scope to the stated locale.** Per-locale fields (address, addendum, privacy
content) are written under the `--locale` you were given. Confirm which language
the scraped pages are in.

## Procedure

### 1. Ask for the URLs and the locale

Ask the operator for:
- the **Impressum** URL,
- the **privacy-policy** URL,
- the **locale** those pages are written in (e.g. `de`).

Either URL may be omitted if that page does not exist yet.

### 2. Scrape and extract

Load each page (Claude-in-Chrome tools or a fetch) and extract:

- **Impressum → § 5 DDG fields:** entity name, legal form, represented-by, full
postal address, e-mail, phone, register court + number, VAT ID, business ID, and
(only if present) supervisory authority, chamber, statutory job title,
professional rules, liquidation note. Any remaining free-form text becomes the
`addendum`.
- **Privacy → the policy body** as HTML (keep the meaningful structure — headings,
paragraphs, lists; drop the site chrome/navigation).

Take values verbatim from the page. When a field is ambiguous or absent, leave it out.

### 3. Build the JSON payload

Write a payload file matching this shape (include only the keys you actually found):

```json
{
"imprint": {
"name": "Muster GmbH",
"legal_form": "GmbH",
"represented_by": "Erika Musterfrau",
"address": "Musterstraße 1\n12345 Musterstadt",
"email": "kontakt@example.com",
"phone": "+49 123 456789",
"register_court": "Amtsgericht Musterstadt",
"register_number": "HRB 12345",
"vat_id": "DE123456789",
"addendum": "<p>optional free-form additions</p>"
},
"privacy": {
"content": "<h2>Datenschutzerklärung</h2><p>…</p>"
}
}
```

Structured imprint fields are global; `address`, `addendum` and `privacy.content`
are stored per the given locale. `addendum` and `privacy.content` are HTML (they are
sanitised on import); everything else is plain text.

### 4. Run the importer

```
task artisan -- revoco:import-legal --locale=de --input=./legal-payload.json
```

(Outside the Docker/`task` setup, call `php artisan revoco:import-legal …` directly.)
It writes nothing on a validation error (unknown key, malformed e-mail), and it
**refuses to overwrite already-populated fields** — if it does, report which fields
conflict and ask the operator before re-running with `--overwrite`.

### 5. Hand the result to the operator

Show the imported-fields summary, then direct the operator to **Filament → Legal**
to review and correct every field, and to check the rendered `/impressum` and
`/datenschutz` pages. Remind them that nothing is auto-published, the content is
theirs to verify, and legal correctness is their responsibility as the data
controller.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,74 @@ so changing them needs no redeploy. Key environment variables:

---

## Branding the form

The form is neutral by default. Its whole appearance is driven by one `--wf-*` CSS
token contract, switched via `data-theme` (`APP_THEME`). Concrete brand overlays —
real colours, fonts and logos — never ship in this public repo; they live in your
private infra repo / deployment mount (see
[`.claude/project/design/theming.md`](.claude/project/design/theming.md)).

### The `design-adoption` skill

To match a shop's look without hand-writing CSS, this repo ships a **deploy-time
Claude-Code skill**, `design-adoption` (in
[`.claude/skills/design-adoption/`](.claude/skills/design-adoption/SKILL.md)) — an
authoring tool, not an app-runtime feature. Run it inside a Claude Code session:
give it the shop's URL and it scans the site, extracts the corporate identity
(colours, fonts, logo), and produces a ready-to-place brand overlay plus a
placement report.

Under the hood it calls a deterministic Artisan command you can also run directly:

```bash
task artisan -- revoco:make-theme \
--slug=myshop \
--accent='#4f46e5' --fg='#1f2937' \
--font='"Inter", system-ui, sans-serif' \
--logo-url='https://myshop.example/logo.svg' \
--brand-name='My Shop' \
--output=./myshop-theme.css
```

The command validates every value against the `--wf-*` contract, rejects malformed
input, and never overrides accessibility-critical tokens (the focus ring stays
intact). The result is **operator-reviewed and operator-placed**: move the overlay
into your deployment, set `APP_THEME=<slug>`, wire the logo via `REVOCO_LOGO_URL`,
and review the rendered form before going live. Nothing is auto-published, and
brand assets are never committed to this repo.

---

## Legal pages (Impressum & privacy)

Revoco serves an Impressum (§ 5 DDG) and a privacy policy at `/impressum` and
`/datenschutz`, backed by operator-editable settings in the Filament **Legal** panel
(neutral "not configured yet" placeholders until filled). The operator is the data
controller and authors the substantive text — Revoco ships only the mechanism.

### The `legal-extraction` skill

If the operator already publishes an Impressum and privacy policy elsewhere (their shop,
a CMS), this repo ships a **deploy-time Claude-Code skill**, `legal-extraction` (in
[`.claude/skills/legal-extraction/`](.claude/skills/legal-extraction/SKILL.md)), to
import them instead of copying field by field. Give it the two URLs and the locale; it
scrapes the pages, extracts the § 5 DDG fields and the privacy content, and hands a JSON
payload to a deterministic Artisan command:

```bash
task artisan -- revoco:import-legal --locale=de --input=./legal-payload.json
```

The command validates the payload against the settings schema (unknown key or malformed
e-mail → nothing written), sanitises scraped HTML, scopes per-locale fields to `--locale`,
and **refuses to overwrite already-populated fields** unless `--overwrite` is given — your
reviewed legal text is never silently replaced. The import is **operator-reviewed**: open
Filament → Legal, correct every field, and check the rendered pages before relying on them.
Revoco makes no legal-correctness guarantee.

---

## CI / Release

GitHub Actions are configured in [`.github/workflows/`](.github/workflows/):
Expand Down
Loading