Creating Offers on angebote.lenne.tech
This skill enables Claude Code to create, optimize, and manage business offers on the lenne.tech Offers platform via MCP tools.
Gotchas
- Content block
order values must be ascending without gaps — Gaps in the sequence (e.g., 1, 3, 5) cause rendering glitches on the offers frontend. When deleting a block, re-normalize remaining orders; when inserting, pick the next consecutive integer. The API does not validate this — the bug only surfaces client-side.
global-ref block type is NOT listed in the standard MCP tool catalog — It's created automatically by the /offers:create workflow when a block is promoted to the offers repository. Users attempting to use it directly via create_offer will get a schema error. The workflow guards this via the @lenne.tech git email check.
- OAuth session expires silently across sessions — The
offers-api and offers-api-demo MCP OAuth cookies are tied to the current Claude session and tracked per-server. Resuming an earlier offers session (via --resume) often hits a 401 on the first MCP call without a clear error. Re-authenticate by running a trivial MCP tool first. The first call against offers-api-demo triggers its own OAuth flow even if offers-api is already authenticated.
git config user.email detection is fragile — The reusable-block detection uses this to gate the lenne.tech-only flow. It fails for developers with a non-@lenne.tech email configured locally (CI machines, temporary clones, rebased-from-fork setups). The step silently skips in those cases, which is the intended fail-safe.
- Template offers cannot be published — only duplicated — Offers with
isTemplate: true cannot be mark_sent. Attempting to publish a template silently returns the unchanged offer. To publish, first create_from_template to produce a regular offer, then send that one.
- Hardcoded colors in
custom-html break in the other color mode — A block styled with inline colors for a light page turns unreadable when the viewer flips the theme toggle: dark headings and dark body text end up on the dark page background. colorMode: 'light' does NOT prevent this — it only sets the initial preference, the toggle stays available. Every custom-html block must paint its own background on the outermost element whenever it sets text colors. See custom-html-guide.md → "Readability in both color modes".
cta.text is rendered as plain text, not HTML — Passing "<p>…</p>" prints the literal tags on the offer page. The block docs list it next to HTML-bearing fields, which invites the mistake. Pass a bare sentence. text blocks, custom-html and faq answers are unaffected.
- Embedded credentials in links (
https://user:pass@host) are blocked by Chrome — The navigation fails with ERR_FAILED, so a "one-click" demo link built that way is dead on arrival for most recipients. Link the plain URL and list the basic-auth credentials next to it so the browser prompt can be answered.
- Customer quotes must be verbatim and complete — Shortening a
testimonial or reference.quote to its "relevant" part, or silently fixing a typo in it, misrepresents a real person. Copying a quote out of an older offer is not safe either: it may already be truncated there. Pull the canonical wording from https://lenne.tech/kundenerfolge and diff it character by character — see best-practices.md → "Customer quotes are verbatim, always".
- File fields survive an update that omits them, so a new block inherits the old block at the same position —
update_offer replaces the block array, but OfferService.update() first runs mergeContentBlockFiles(), which pairs stored and incoming blocks by ${order}-${type} and then protects every file field: imageFileId, fileId, animationFileId, previewFileId, fileIds, members[].imageFileId, files[].fileId. Omitting the field or sending "" means keep what is stored — only an explicit null clears it. Because the pairing is positional, a brand-new reference inserted where another reference used to sit silently adopts that block's imageFileId, and the offer then shows the wrong screenshot under the new project name. Whenever a block changes identity, send a real file id or null, never "". See content-blocks.md → "File fields on update".
- A
reference block without an image renders an empty placeholder box — The renderer always reserves the image column and falls back to a grey box with an image icon, which reads as broken on a customer-facing page. Every reference needs a real imageFileId. When no product screenshot exists, a purpose-built diagram is a legitimate substitute; a screenshot of a different project is not.
When to Use This Skill
- User asks to create, edit, or optimize an offer/Angebot
- User references content blocks, pricing tables, or offer templates
- User mentions angebote.lenne.tech, demo-angebote.lenne.tech, or the offers platform
- Working inside the offers project repository
- User wants to generate sharing snippets or manage offer status
- User asks about offer analytics, views, downloads, or statistics
Skill Boundaries
| User Intent |
Correct Skill |
| Create/edit offers via MCP |
THIS SKILL |
| Develop the offers codebase (API/Frontend) |
lt-dev:generating-nest-servers / lt-dev:developing-lt-frontend |
| Deploy offers infrastructure |
devops |
Related Skills
Works closely with:
lt-dev:generating-nest-servers — For backend development on the offers API
lt-dev:developing-lt-frontend — For frontend development on the offers app
MCP Connection
All offer operations go through one of two MCP servers — the platform ships a production and a demo deployment:
| MCP Server |
URL |
When to use |
offers-api |
https://api.angebote.lenne.tech/mcp |
Default. Production — real customer-facing offers. |
offers-api-demo |
https://api.demo-angebote.lenne.tech/mcp |
Demo stage — sandbox for prospect demos. Use when the user mentions "demo", "Demo-Angebot", "demo-angebote", "Demo-Stage", or "Demo-Umgebung". |
Routing rule. If the user prompt mentions "demo" in an offers context, route ALL tool calls in that prompt to offers-api-demo. Otherwise — including for ambiguous prompts — default to offers-api (production). The UserPromptSubmit hook emits a one-line stage hint that names the correct server; honor that hint.
Both connections use OAuth 2.1 with automatic browser-based login. The OAuth session is per-MCP-server, so the first call against offers-api-demo triggers its own browser-auth flow even if offers-api is already authenticated.
When working inside the offers project repository (local development), the project-level .mcp.json overrides offers-api to http://localhost:3000/mcp so production-flavored tool calls hit your local API. offers-api-demo is unaffected — still points at the deployed demo stage — which is useful for testing demo-only flows from a local dev environment.
Available MCP Tools (identical on both servers):
add_html_embed — Upload a self-contained HTML file (base64) and create an html-embed content block in one atomic call (validates the HTML, ≤ 5 MB). For larger files prefer create_upload_ticket + HTTP upload
add_lottie_animation — Upload a Lottie JSON file and create a lottie content block in one atomic call (validates the JSON, rejects unsupported features, ≤ 2 MB)
add_offer_source — Add a source (text/link/file) to an offer
create_from_template — Create offer from template
create_knowledge — Create a knowledge base entry
create_offer — Create new offer (returns offer + access code). Accepts an optional theme: { enabled, light, dark } per-offer override and an optional colorMode: 'system' | 'light' | 'dark' (forces the offer page into light/dark; default system = browser preference)
create_upload_ticket — Create a single-use upload URL (valid 15 min) for uploading files via plain HTTP instead of base64 through MCP. purpose selects validation: html-embed (validated HTML, ≤ 5 MB), image (image/*, ≤ 10 MB), file (any, ≤ 25 MB). POST multipart form-data with field file to the returned uploadUrl; the response contains the GridFS file id for use as fileId in content blocks
delete_knowledge — Delete a knowledge base entry
delete_offer — Delete offer permanently
duplicate_offer — Clone offer with new slug + access code (theme is carried over)
generate_snippet — Generate sharing text with link + access code
get_default_theme — Read the app-wide default theme that the renderer applies to offers without their own theme
get_global — Get global block with versions
get_knowledge — Get a knowledge base entry with full content
get_offer — Get offer with all content blocks (globals auto-resolved). Returns the effective theme — i.e. the per-offer override when enabled, otherwise the settings default merged in transparently
get_offer_analytics — Get offer analytics (views, downloads, scroll depth, dwell time)
get_offer_context — Get full AI context (knowledge + globals + optional offer/sources)
get_offer_sources — Get all sources for an offer
list_globals — List reusable global content blocks
list_knowledge — List knowledge base entries
list_offers — List offers (with optional status filter)
list_templates — List template offers
mark_draft — Reset to draft (sent → draft)
mark_sent — Mark offer as sent (draft → sent)
remove_offer_source — Remove a source from an offer
set_default_theme — Configure the app-wide default theme (light/dark hex palettes). Admin-only on the underlying SettingsService
update_knowledge — Update a knowledge base entry
update_lottie_animation — Replace the Lottie JSON of an existing block (keeps the block ID + position; resets first-frame snapshot)
update_offer — Update offer fields and content blocks. Accepts an optional theme to set/clear the per-offer palette and an optional colorMode ('system'/'light'/'dark')
upload_knowledge_file — Upload file to knowledge entry (base64)
upload_offer_source_file — Upload file as offer source (base64)
Reference Files
${CLAUDE_SKILL_DIR}/reference/content-blocks.md — All 18 block types with schemas (incl. lottie, html-embed) and upload-ticket usage
${CLAUDE_SKILL_DIR}/reference/offer-model.md — Offer model, status lifecycle, per-offer theme and colorMode fields
${CLAUDE_SKILL_DIR}/reference/knowledge-base.md — Knowledge base schema and categories
${CLAUDE_SKILL_DIR}/reference/custom-html-guide.md — HTML + Tailwind + NuxtUI guide (incl. WYSIWYG editor)
${CLAUDE_SKILL_DIR}/reference/theming.md — Per-offer theme override, app-wide default theme, MCP & UI workflows
${CLAUDE_SKILL_DIR}/reference/best-practices.md — Content structure and examples
Core Workflow
Creating an Offer
- Load context —
get_offer_context → Company knowledge + global blocks
- Gather requirements — Title, customer, content, approach
- Additional materials? — Ask if there are briefing docs/notes
- Store sources — Via
add_offer_source / upload_offer_source_file
- Choose approach — From template or new
- Build content blocks — Based on knowledge + sources + briefing
- Create offer —
create_offer
- Review and refine —
get_offer / update_offer
- Share —
mark_sent → generate_snippet
Optimizing an Offer
- Fetch the offer —
get_offer with the offer ID
- Analyze content — Check structure, completeness, persuasiveness
- Suggest improvements — Better block order, missing sections, text quality
- Apply changes —
update_offer with improved content blocks
Reusable Global Blocks (lenne.tech Developers Only)
This feature is exclusively for lenne.tech developers. Before evaluating reusable blocks, silently run git config user.email and check if the result contains @lenne.tech. If not, skip this entire section without any mention — the user must not see any hint of this feature.
Detection Logic
command -v git >/dev/null 2>&1 && git config user.email 2>/dev/null | grep -q '@lenne.tech'
- git not installed → skip silently
- git installed but no
@lenne.tech email → skip silently
- git installed and
@lenne.tech email → proceed with reusable block evaluation
When Detected as lenne.tech Developer
During offer creation or optimization, evaluate whether any content block could be useful across multiple offers (e.g., company introduction, standard team section, recurring FAQ, legal disclaimers, standard CTA). If so:
- Ask the user: "Dieser Inhaltsblock könnte auch in zukünftigen Angeboten wiederverwendet werden. Soll er als wiederverwendbarer Block im Repository angelegt werden?"
- If yes, locate or clone the offers repository and create the global block there
- Verify availability via
list_globals MCP tool
- Automatically replace the original inline block in the current offer with a
global-ref block pointing to the new global ID via update_offer
Repository Access
The offers platform repository is hosted at: https://gitlab.lenne.tech/intern/offers
Workflow:
- Check whether the repo is already cloned on this machine. Checkout paths differ per developer, so search instead of assuming a workspace layout:
find "$HOME" -maxdepth 5 -type d -name offers -not -path '*/node_modules/*' 2>/dev/null, then confirm the hit via its git remote get-url origin.
- If not found, ask the user if they want to clone it:
git clone https://gitlab.lenne.tech/intern/offers
- Use
lt-dev:generating-nest-servers / lt-dev:developing-lt-frontend skills for codebase changes
When to Suggest a Global Block
- Content that is not customer-specific (company info, team, legal, processes)
- Blocks that have been manually duplicated across offers
- Standardized sections like "Über uns", "Unser Prozess", "AGB-Hinweis"
- Recurring FAQ items that apply to most offers
Analyzing Offer Performance
Use get_offer_analytics to check how an offer performs. In Claude Desktop, an interactive dashboard with KPI cards, charts, and download stats renders directly in the chat.
Analytics Workflow
- Check performance —
get_offer_analytics with the offer ID
- Interpret metrics — Views, scroll depth, dwell time, PDF/attachment downloads
- Suggest improvements — Low scroll depth → restructure content; no downloads → better CTA placement
- Apply changes —
update_offer with optimized blocks
Key Metrics
| Metric |
Interpretation |
totalViews |
How many times the offer was opened |
avgScrollDepth |
How far customers scroll (< 50% = content needs restructuring) |
avgTimeOnPage |
Engagement level (< 30s = not reading, > 5min = very engaged) |
pdfDownloadCount |
PDF saves (high = serious interest) |
attachmentDownloads |
Per-file download tracking |
timeToFirstViewHours |
Response time after sharing |
Content Guidelines
- Language: Always German. Ask the user whether to use "du" (informal) or "Sie" (formal) for addressing the customer. Default is siezen (formal). Avoid direct address where possible.
- Structure: Start with greeting/intro, then main content, end with CTA
- Block order: text → image/video → pricing-table → testimonial/reference → cta
- Pricing: Always use
pricing-table block for prices, not inline text
- File references: Reference existing
fileId values, or upload new files via create_upload_ticket + HTTP POST (recommended), add_html_embed / add_lottie_animation (small files, base64).
Pre-Submission Checklist
1---2name: creating-offers3description: Creates and edits business offers on the lenne.tech Offers platform (angebote.lenne.tech) and its demo deployment (demo-angebote.lenne.tech). Knows all 18 content block types, offer lifecycle (draft/sent/viewed/template), custom HTML with Tailwind CSS and NuxtUI components (via rich-component block), HTML embeds for click-dummies, per-offer themes and color mode, and file uploads via single-use upload tickets. Activates when working with offers, content blocks, or the Offers API. Uses MCP tools (offers-api for production, offers-api-demo for demo) for all CRUD operations.4---56# Creating Offers on angebote.lenne.tech78This skill enables Claude Code to create, optimize, and manage business offers on the lenne.tech Offers platform via MCP tools.910## Gotchas1112- **Content block `order` values must be ascending without gaps** — Gaps in the sequence (e.g., `1, 3, 5`) cause rendering glitches on the offers frontend. When deleting a block, re-normalize remaining orders; when inserting, pick the next consecutive integer. The API does not validate this — the bug only surfaces client-side.13- **`global-ref` block type is NOT listed in the standard MCP tool catalog** — It's created automatically by the `/offers:create` workflow when a block is promoted to the offers repository. Users attempting to use it directly via `create_offer` will get a schema error. The workflow guards this via the `@lenne.tech` git email check.14- **OAuth session expires silently across sessions** — The `offers-api` and `offers-api-demo` MCP OAuth cookies are tied to the current Claude session and tracked per-server. Resuming an earlier offers session (via `--resume`) often hits a 401 on the first MCP call without a clear error. Re-authenticate by running a trivial MCP tool first. The first call against `offers-api-demo` triggers its own OAuth flow even if `offers-api` is already authenticated.15- **`git config user.email` detection is fragile** — The reusable-block detection uses this to gate the lenne.tech-only flow. It fails for developers with a non-`@lenne.tech` email configured locally (CI machines, temporary clones, rebased-from-fork setups). The step silently skips in those cases, which is the intended fail-safe.16- **Template offers cannot be published — only duplicated** — Offers with `isTemplate: true` cannot be `mark_sent`. Attempting to publish a template silently returns the unchanged offer. To publish, first `create_from_template` to produce a regular offer, then send that one.17- **Hardcoded colors in `custom-html` break in the other color mode** — A block styled with inline colors for a light page turns unreadable when the viewer flips the theme toggle: dark headings and dark body text end up on the dark page background. `colorMode: 'light'` does NOT prevent this — it only sets the initial preference, the toggle stays available. Every `custom-html` block must paint its own background on the outermost element whenever it sets text colors. See [`custom-html-guide.md`](./reference/custom-html-guide.md) → "Readability in both color modes".18- **`cta.text` is rendered as plain text, not HTML** — Passing `"<p>…</p>"` prints the literal tags on the offer page. The block docs list it next to HTML-bearing fields, which invites the mistake. Pass a bare sentence. `text` blocks, `custom-html` and `faq` answers are unaffected.19- **Embedded credentials in links (`https://user:pass@host`) are blocked by Chrome** — The navigation fails with `ERR_FAILED`, so a "one-click" demo link built that way is dead on arrival for most recipients. Link the plain URL and list the basic-auth credentials next to it so the browser prompt can be answered.20- **Customer quotes must be verbatim and complete** — Shortening a `testimonial` or `reference.quote` to its "relevant" part, or silently fixing a typo in it, misrepresents a real person. Copying a quote out of an older offer is not safe either: it may already be truncated there. Pull the canonical wording from https://lenne.tech/kundenerfolge and diff it character by character — see [`best-practices.md`](./reference/best-practices.md) → "Customer quotes are verbatim, always".21- **File fields survive an update that omits them, so a new block inherits the old block at the same position** — `update_offer` replaces the block array, but `OfferService.update()` first runs `mergeContentBlockFiles()`, which pairs stored and incoming blocks by `${order}-${type}` and then protects every file field: `imageFileId`, `fileId`, `animationFileId`, `previewFileId`, `fileIds`, `members[].imageFileId`, `files[].fileId`. Omitting the field or sending `""` means *keep what is stored* — only an explicit `null` clears it. Because the pairing is positional, a brand-new `reference` inserted where another `reference` used to sit silently adopts that block's `imageFileId`, and the offer then shows the wrong screenshot under the new project name. Whenever a block changes identity, send a real file id or `null`, never `""`. See [`content-blocks.md`](./reference/content-blocks.md) → "File fields on update".22- **A `reference` block without an image renders an empty placeholder box** — The renderer always reserves the image column and falls back to a grey box with an image icon, which reads as broken on a customer-facing page. Every `reference` needs a real `imageFileId`. When no product screenshot exists, a purpose-built diagram is a legitimate substitute; a screenshot of a *different* project is not.2324## When to Use This Skill2526- User asks to create, edit, or optimize an offer/Angebot27- User references content blocks, pricing tables, or offer templates28- User mentions angebote.lenne.tech, demo-angebote.lenne.tech, or the offers platform29- Working inside the offers project repository30- User wants to generate sharing snippets or manage offer status31- User asks about offer analytics, views, downloads, or statistics3233## Skill Boundaries3435| User Intent | Correct Skill |36|------------|---------------|37| Create/edit offers via MCP | **THIS SKILL** |38| Develop the offers codebase (API/Frontend) | `lt-dev:generating-nest-servers` / `lt-dev:developing-lt-frontend` |39| Deploy offers infrastructure | `devops` |4041## Related Skills4243**Works closely with:**44- `lt-dev:generating-nest-servers` — For backend development on the offers API45- `lt-dev:developing-lt-frontend` — For frontend development on the offers app4647## MCP Connection4849All offer operations go through one of two MCP servers — the platform ships a production and a demo deployment:5051| MCP Server | URL | When to use |52|---|---|---|53| `offers-api` | `https://api.angebote.lenne.tech/mcp` | **Default.** Production — real customer-facing offers. |54| `offers-api-demo` | `https://api.demo-angebote.lenne.tech/mcp` | Demo stage — sandbox for prospect demos. Use when the user mentions "demo", "Demo-Angebot", "demo-angebote", "Demo-Stage", or "Demo-Umgebung". |5556**Routing rule.** If the user prompt mentions "demo" in an offers context, route ALL tool calls in that prompt to `offers-api-demo`. Otherwise — including for ambiguous prompts — default to `offers-api` (production). The `UserPromptSubmit` hook emits a one-line stage hint that names the correct server; honor that hint.5758Both connections use OAuth 2.1 with automatic browser-based login. The OAuth session is per-MCP-server, so the first call against `offers-api-demo` triggers its own browser-auth flow even if `offers-api` is already authenticated.5960When working inside the offers project repository (local development), the project-level `.mcp.json` overrides `offers-api` to `http://localhost:3000/mcp` so production-flavored tool calls hit your local API. `offers-api-demo` is unaffected — still points at the deployed demo stage — which is useful for testing demo-only flows from a local dev environment.6162**Available MCP Tools (identical on both servers):**63- `add_html_embed` — Upload a self-contained HTML file (base64) and create an `html-embed` content block in one atomic call (validates the HTML, ≤ 5 MB). For larger files prefer `create_upload_ticket` + HTTP upload64- `add_lottie_animation` — Upload a Lottie JSON file and create a `lottie` content block in one atomic call (validates the JSON, rejects unsupported features, ≤ 2 MB)65- `add_offer_source` — Add a source (text/link/file) to an offer66- `create_from_template` — Create offer from template67- `create_knowledge` — Create a knowledge base entry68- `create_offer` — Create new offer (returns offer + access code). Accepts an optional `theme: { enabled, light, dark }` per-offer override and an optional `colorMode: 'system' | 'light' | 'dark'` (forces the offer page into light/dark; default `system` = browser preference)69- `create_upload_ticket` — Create a single-use upload URL (valid 15 min) for uploading files via plain HTTP instead of base64 through MCP. `purpose` selects validation: `html-embed` (validated HTML, ≤ 5 MB), `image` (`image/*`, ≤ 10 MB), `file` (any, ≤ 25 MB). POST multipart form-data with field `file` to the returned `uploadUrl`; the response contains the GridFS file `id` for use as `fileId` in content blocks70- `delete_knowledge` — Delete a knowledge base entry71- `delete_offer` — Delete offer permanently72- `duplicate_offer` — Clone offer with new slug + access code (theme is carried over)73- `generate_snippet` — Generate sharing text with link + access code74- `get_default_theme` — Read the app-wide default theme that the renderer applies to offers without their own theme75- `get_global` — Get global block with versions76- `get_knowledge` — Get a knowledge base entry with full content77- `get_offer` — Get offer with all content blocks (globals auto-resolved). Returns the **effective theme** — i.e. the per-offer override when enabled, otherwise the settings default merged in transparently78- `get_offer_analytics` — Get offer analytics (views, downloads, scroll depth, dwell time)79- `get_offer_context` — Get full AI context (knowledge + globals + optional offer/sources)80- `get_offer_sources` — Get all sources for an offer81- `list_globals` — List reusable global content blocks82- `list_knowledge` — List knowledge base entries83- `list_offers` — List offers (with optional status filter)84- `list_templates` — List template offers85- `mark_draft` — Reset to draft (sent → draft)86- `mark_sent` — Mark offer as sent (draft → sent)87- `remove_offer_source` — Remove a source from an offer88- `set_default_theme` — Configure the app-wide default theme (light/dark hex palettes). Admin-only on the underlying SettingsService89- `update_knowledge` — Update a knowledge base entry90- `update_lottie_animation` — Replace the Lottie JSON of an existing block (keeps the block ID + position; resets first-frame snapshot)91- `update_offer` — Update offer fields and content blocks. Accepts an optional `theme` to set/clear the per-offer palette and an optional `colorMode` ('system'/'light'/'dark')92- `upload_knowledge_file` — Upload file to knowledge entry (base64)93- `upload_offer_source_file` — Upload file as offer source (base64)9495## Reference Files9697- `${CLAUDE_SKILL_DIR}/reference/content-blocks.md` — All 18 block types with schemas (incl. `lottie`, `html-embed`) and upload-ticket usage98- `${CLAUDE_SKILL_DIR}/reference/offer-model.md` — Offer model, status lifecycle, per-offer theme and colorMode fields99- `${CLAUDE_SKILL_DIR}/reference/knowledge-base.md` — Knowledge base schema and categories100- `${CLAUDE_SKILL_DIR}/reference/custom-html-guide.md` — HTML + Tailwind + NuxtUI guide (incl. WYSIWYG editor)101- `${CLAUDE_SKILL_DIR}/reference/theming.md` — Per-offer theme override, app-wide default theme, MCP & UI workflows102- `${CLAUDE_SKILL_DIR}/reference/best-practices.md` — Content structure and examples103104## Core Workflow105106### Creating an Offer1071081. **Load context** — `get_offer_context` → Company knowledge + global blocks1092. **Gather requirements** — Title, customer, content, approach1103. **Additional materials?** — Ask if there are briefing docs/notes1114. **Store sources** — Via `add_offer_source` / `upload_offer_source_file`1125. **Choose approach** — From template or new1136. **Build content blocks** — Based on knowledge + sources + briefing1147. **Create offer** — `create_offer`1158. **Review and refine** — `get_offer` / `update_offer`1169. **Share** — `mark_sent` → `generate_snippet`117118### Optimizing an Offer1191201. **Fetch the offer** — `get_offer` with the offer ID1212. **Analyze content** — Check structure, completeness, persuasiveness1223. **Suggest improvements** — Better block order, missing sections, text quality1234. **Apply changes** — `update_offer` with improved content blocks124125## Reusable Global Blocks (lenne.tech Developers Only)126127This feature is **exclusively for lenne.tech developers**. Before evaluating reusable blocks, silently run `git config user.email` and check if the result contains `@lenne.tech`. If not, **skip this entire section without any mention** — the user must not see any hint of this feature.128129### Detection Logic130131```bash132command -v git >/dev/null 2>&1 && git config user.email 2>/dev/null | grep -q '@lenne.tech'133```134135- git not installed → skip silently136- git installed but no `@lenne.tech` email → skip silently137- git installed and `@lenne.tech` email → proceed with reusable block evaluation138139### When Detected as lenne.tech Developer140141During offer creation or optimization, evaluate whether any content block could be useful across multiple offers (e.g., company introduction, standard team section, recurring FAQ, legal disclaimers, standard CTA). If so:1421431. **Ask the user**: "Dieser Inhaltsblock könnte auch in zukünftigen Angeboten wiederverwendet werden. Soll er als wiederverwendbarer Block im Repository angelegt werden?"1442. **If yes**, locate or clone the offers repository and create the global block there1453. Verify availability via `list_globals` MCP tool1464. **Automatically replace** the original inline block in the current offer with a `global-ref` block pointing to the new global ID via `update_offer`147148### Repository Access149150The offers platform repository is hosted at: `https://gitlab.lenne.tech/intern/offers`151152**Workflow:**1531. Check whether the repo is already cloned on this machine. Checkout paths differ per developer, so search instead of assuming a workspace layout: `find "$HOME" -maxdepth 5 -type d -name offers -not -path '*/node_modules/*' 2>/dev/null`, then confirm the hit via its `git remote get-url origin`.1542. If not found, ask the user if they want to clone it: `git clone https://gitlab.lenne.tech/intern/offers`1553. Use `lt-dev:generating-nest-servers` / `lt-dev:developing-lt-frontend` skills for codebase changes156157### When to Suggest a Global Block158159- Content that is **not customer-specific** (company info, team, legal, processes)160- Blocks that have been **manually duplicated** across offers161- Standardized sections like "Über uns", "Unser Prozess", "AGB-Hinweis"162- Recurring FAQ items that apply to most offers163164## Analyzing Offer Performance165166Use `get_offer_analytics` to check how an offer performs. In Claude Desktop, an interactive dashboard with KPI cards, charts, and download stats renders directly in the chat.167168### Analytics Workflow1691701. **Check performance** — `get_offer_analytics` with the offer ID1712. **Interpret metrics** — Views, scroll depth, dwell time, PDF/attachment downloads1723. **Suggest improvements** — Low scroll depth → restructure content; no downloads → better CTA placement1734. **Apply changes** — `update_offer` with optimized blocks174175### Key Metrics176177| Metric | Interpretation |178|--------|---------------|179| `totalViews` | How many times the offer was opened |180| `avgScrollDepth` | How far customers scroll (< 50% = content needs restructuring) |181| `avgTimeOnPage` | Engagement level (< 30s = not reading, > 5min = very engaged) |182| `pdfDownloadCount` | PDF saves (high = serious interest) |183| `attachmentDownloads` | Per-file download tracking |184| `timeToFirstViewHours` | Response time after sharing |185186## Content Guidelines187188- **Language**: Always German. Ask the user whether to use "du" (informal) or "Sie" (formal) for addressing the customer. Default is **siezen** (formal). Avoid direct address where possible.189- **Structure**: Start with greeting/intro, then main content, end with CTA190- **Block order**: text → image/video → pricing-table → testimonial/reference → cta191- **Pricing**: Always use `pricing-table` block for prices, not inline text192- **File references**: Reference existing `fileId` values, or upload new files via `create_upload_ticket` + HTTP POST (recommended), `add_html_embed` / `add_lottie_animation` (small files, base64).193194## Pre-Submission Checklist195196- [ ] Offer has a meaningful title197- [ ] Content blocks are properly ordered (ascending `order` from 0)198- [ ] All blocks have `visible: true` unless intentionally hidden199- [ ] Customer name/company is set if known200- [ ] `validUntil` date is set if offer has an expiration201- [ ] No duplicate block titles202- [ ] CTA block included at the end