---
name: template-create
version: 2.3.0
description: Create custom digital identity and CV templates for talent.de — single-file HTML with Handlebars bindings, embedded config, and auto-validation.
homepage: https://www.talent.de
env_vars:
  TALENT_ACCESS_ID:
    required: true
    sensitive: true
    description: "Required for template upload. Also used as HMAC secret for callback verification."
metadata: {"openclaw":{"emoji":"🎨","category":"career","author":"talent.de","requires_access_id":true}}
---

# talent.de Template Creation Skill

> You are building a custom template for digital identities and CVs on talent.de. Your template is a single HTML file with Handlebars bindings. You upload it via API, it gets auto-validated, and goes live instantly if it passes all checks.

**You MUST have an Access-ID.** Register via `POST /api/agent/register` (see [Access System](https://www.talent.de/skills/shared/access.md)) or use one provided by your operator.

**You will be listed as the template author** (via your `access_id`).

---

## Quick Start — Follow These Steps First

### Step A: Copy the starter template

ALWAYS start from the starter template in Step 1 below. Do NOT write a template from scratch. The starter template demonstrates every required pattern — branding, nested profile fields, collection loops, translations, print styles, and reduced motion.

If you skip the starter template, you WILL hit validation errors.

### Step B: Use ONLY these helpers — do NOT invent your own

The renderer provides built-in helpers. If you need functionality, check this list first. Using a non-existent helper (like `repeat`, `render`, or `icon`) will cause a `handlebars_unknown_variable` validation error.

**Date formatting:**
- `{{formatDate this.startDate}}` → "01/2024"
- `{{formatDate this.startDate "long"}}` → "January 2024"
- `{{formatDate this.startDate "year"}}` → "2024"

**Skill visualization:**
- `{{getStarRating this.level}}` → "★★★★☆"
- `{{getProgressWidth this.level}}` → "85%" (for CSS width)
- `{{getLevelDots this.level}}` → 4 (active dots out of 5)
- `{{getRemainingDots this.level}}` → 1 (inactive dots)
- Pre-computed fields: `{{this.levelText}}` ("Expert"), `{{this.levelPercentage}}` (0-100)

**Logic & comparison:**
- `{{#if (gt this.level 3)}}...{{/if}}`
- `{{#if (eq this.platform "LINKEDIN")}}...{{/if}}`
- `{{#if (and profile.email profile.phone)}}...{{/if}}`

**Text:**
- `{{uppercase this.name}}`, `{{lowercase this.name}}`, `{{capitalize this.name}}`
- `{{truncate this.description 100}}`
- `{{initials "John Doe"}}` → "JD"

**Math & arrays:**
- `{{add @index 1}}` → 1-based index
- `{{multiply this.level 20}}` → percentage from 1-5 scale
- `{{length hardSkills}}` → item count
- `{{#each (limit hardSkills 5)}}` → first 5 only
- `{{#times 5}}...{{/times}}` → repeat N times (has @index)

### Step C: Use ONLY these data bindings

All profile fields are nested: `{{profile.firstName}}`, NOT `{{firstName}}`.

**Profile:** `{{profile.firstName}}` `{{profile.lastName}}` `{{profile.title}}` `{{profile.email}}` `{{profile.phone}}` `{{profile.city}}` `{{profile.country}}` `{{profile.summary}}` `{{profile.website}}`

**Collections and their key fields:**

| Loop with `{{#each ...}}` | Key fields inside loop (use `{{this.fieldName}}`) |
|---|---|
| `experience` | `jobTitle`, `company`, `startDate`, `endDate`, `isCurrent`, `description`, `achievements` |
| `education` | `degree`, `fieldOfStudy`, `institution`, `startDate`, `endDate`, `isCurrent` |
| `hardSkills` | `name`, `level` (1-5), `levelText`, `levelPercentage` |
| `softSkills` | `name`, `levelText`, `levelPercentage` |
| `toolSkills` | `name`, `levelText`, `levelPercentage` |
| `languages` | `name`, `level` (CEFR code), `levelText`, `isNative` |
| `projects` | `name`, `description`, `url`, `technologies` |
| `certificates` | `name`, `issuer`, `issueDate` |
| `hobbies` | `name`, `description` |
| `profile.socialLinks` | `platform`, `url`, `username` |

**Translations** — use these for section headings and labels:
- Sections: `{{t.sections.experience}}`, `{{t.sections.education}}`, `{{t.sections.skills}}`, `{{t.sections.projects}}`, `{{t.sections.languages}}`, `{{t.sections.certificates}}`, `{{t.sections.about}}`
- Fields: `{{t.fields.present}}`, `{{t.fields.email}}`, `{{t.fields.phone}}`, `{{t.fields.location}}`
- Actions: `{{t.actions.getInTouch}}`, `{{t.actions.download}}`, `{{t.actions.print}}`

**Colors & branding:**
- `{{primaryColor}}`, `{{accentColor}}`, `{{neutralColor}}`, `{{fontFamily}}`
- Branding (REQUIRED): `{{{talentLogoSvg}}}` (triple braces!) + `<a href="{{brandUrl}}">powered by talent.de</a>`

### Step D: Now read the Critical Rules below, then build your template following Steps 1-6

---

## Critical Rules — Read These First

You MUST follow every rule below. The validator will reject your template if any rule is violated.

- **Access-ID required** — You cannot upload without one. Zero open access.
- **Branding is mandatory** — You MUST include `{{{talentLogoSvg}}}` (triple braces for raw HTML!) OR a visible "powered by talent.de" text with a link to `{{brandUrl}}`. This is a hard validation rule, not a suggestion.
- **All code inline** — Do NOT use external scripts, stylesheets, fonts, or images. Everything MUST be in the single HTML file. _Exception: 3D patterns (ThreeDSimulationPattern) may use same-origin `/vendor/` imports via Import-Map._
- **No network access** — `fetch()`, `XMLHttpRequest`, `WebSocket`, `navigator.sendBeacon` are all blocked. _Exception: 3D patterns may load `.glb`/`.hdr` assets from same-origin `/vendor/` paths only — external URLs (`http://`, `https://`) are forbidden._
- **No browser storage** — `localStorage`, `sessionStorage`, `indexedDB`, `document.cookie` are all blocked.
- **No popups or navigation** — `window.open()`, `location.href`, `location.replace()` are blocked.
- **Profile fields are NESTED** — Use `{{profile.firstName}}`, NOT `{{firstName}}`. Profile data lives under the `profile` object.
- **Size limits** — Total 500KB, CSS 100KB, JS 100KB. Aim for under 200KB.
- **Readable code** — No minified or obfuscated code. UTF-8 encoding required.
- **Reduced motion** — You MUST include `@media (prefers-reduced-motion: reduce)` for accessibility.

### Common Mistakes — Do NOT Make These

- Do NOT use `{{firstName}}` — use `{{profile.firstName}}`. Profile fields are nested.
- Do NOT use `<script src="...">` — only inline `<script>` allowed (except vendor Three.js).
- Do NOT use `@import` in CSS — include all CSS directly in `<style>` tags.
- Do NOT use `{{talentLogoSvg}}` (double braces) — use `{{{talentLogoSvg}}}` (triple braces) or the SVG gets HTML-escaped.
- Do NOT use `{{this.current}}` — the field name is `{{this.isCurrent}}`.
- Do NOT use `{{#each certifications}}` — the collection is `{{#each certificates}}`.
- Do NOT use `{{t.common.present}}` or `{{t.labels.present}}` — use `{{t.fields.present}}`.
- Do NOT use `{{t.labels.email}}` — use `{{t.fields.email}}`. All field labels are under `t.fields.*`.
- Do NOT use `{{backgroundColor}}` without defining it in embedded config — only `{{primaryColor}}`, `{{accentColor}}`, `{{neutralColor}}` come from CV data. Custom colors go in your config `variables`.
- Do NOT use `{{#each socialLinks}}` — use `{{#each profile.socialLinks}}`. Social links are nested under profile.
- Do NOT minify or obfuscate code — readable UTF-8 required.
- Do NOT use large Base64 images — use inline SVG instead. Base64 blocks >1000 chars are flagged.
- Do NOT forget a container element — your template MUST have at least one `<div>`, `<section>`, or `<main>`.
- Do NOT use relative vendor paths like `../vendor/` — use absolute: `/vendor/three@0.182.0/`.
- Do NOT forget `@media (prefers-reduced-motion: reduce)` — required for accessibility.

---

## Build Your Template — Step by Step

### Step 1: Start from this example

Copy the template below as your starting point. It already includes correct structure, branding, data bindings, print styles, and reduced-motion support.

```html
<!DOCTYPE html>
<html lang="{{language}}">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{profile.firstName}} {{profile.lastName}}</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: {{fontFamily}}, system-ui, sans-serif; color: #333; }
    .container { max-width: 800px; margin: 0 auto; padding: 2rem; }
    header { text-align: center; padding: 3rem 0; border-bottom: 3px solid {{primaryColor}}; }
    h1 { font-size: 2.5rem; color: {{primaryColor}}; }
    h2 { color: {{primaryColor}}; margin: 2rem 0 1rem; font-size: 1.3rem; text-transform: uppercase; letter-spacing: 0.1em; }
    .job { margin-bottom: 1.5rem; }
    .job h3 { font-size: 1.1rem; }
    .job .meta { color: #666; font-size: 0.9rem; }
    .skills { display: flex; flex-wrap: wrap; gap: 0.5rem; }
    .skill { background: {{primaryColor}}22; color: {{primaryColor}}; padding: 0.3rem 0.8rem; border-radius: 1rem; font-size: 0.85rem; }
    .skill-bar { height: 6px; background: #eee; border-radius: 3px; overflow: hidden; }
    .skill-bar__fill { height: 100%; background: {{primaryColor}}; border-radius: 3px; }
    footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #eee; text-align: center; font-size: 0.8rem; opacity: 0.7; }
    @media print { body { font-size: 11pt; } header { padding: 1rem 0; } }
    @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
  </style>
</head>
<body>
  <div class="container">
    <header>
      <h1>{{profile.firstName}} {{profile.lastName}}</h1>
      <p>{{profile.title}}</p>
      {{#if profile.city}}<p>{{profile.city}}{{#if profile.country}}, {{profile.country}}{{/if}}</p>{{/if}}
    </header>

    {{#if profile.summary}}
    <section>
      <h2>{{t.sections.about}}</h2>
      <p>{{profile.summary}}</p>
    </section>
    {{/if}}

    {{#if experience}}
    <section>
      <h2>{{t.sections.experience}}</h2>
      {{#each experience}}
      <div class="job">
        <h3>{{this.jobTitle}} — {{this.company}}</h3>
        <p class="meta">{{this.startDate}} — {{#if this.isCurrent}}{{t.fields.present}}{{else}}{{this.endDate}}{{/if}}</p>
        {{#if this.description}}<p>{{this.description}}</p>{{/if}}
      </div>
      {{/each}}
    </section>
    {{/if}}

    {{#if hardSkills}}
    <section>
      <h2>{{t.sections.skills}}</h2>
      {{#each hardSkills}}
      <div style="margin-bottom: 0.5rem;">
        <div style="display: flex; justify-content: space-between; font-size: 0.9rem;">
          <span>{{this.name}}</span>
          <span>{{this.levelText}}</span>
        </div>
        <div class="skill-bar">
          <div class="skill-bar__fill" style="width: {{this.levelPercentage}}%"></div>
        </div>
      </div>
      {{/each}}
    </section>
    {{/if}}

    {{#if education}}
    <section>
      <h2>{{t.sections.education}}</h2>
      {{#each education}}
      <div class="job">
        <h3>{{this.degree}}{{#if this.fieldOfStudy}} — {{this.fieldOfStudy}}{{/if}}</h3>
        <p class="meta">{{this.institution}} | {{this.startDate}} — {{#if this.isCurrent}}{{t.fields.present}}{{else}}{{this.endDate}}{{/if}}</p>
      </div>
      {{/each}}
    </section>
    {{/if}}

    <footer class="talent-branding">
      {{{talentLogoSvg}}}
      <a href="{{brandUrl}}">powered by talent.de</a>
    </footer>
  </div>
</body>
</html>
```

### Step 2: Customize structure and content

Use the **Data Bindings Reference** below to add sections to your template. The available collections are:
- `experience`, `education`, `hardSkills`, `softSkills`, `toolSkills`, `languages`, `projects`, `certificates`, `hobbies`

Wrap each section in `{{#if collection}}...{{/if}}` so it only renders when data exists.

### Step 3: Style your template

- Use `{{primaryColor}}`, `{{accentColor}}`, `{{neutralColor}}` from CV settings for colors
- Define custom colors in your embedded config `variables` block (see Template Architecture below)
- See the **Design Guidance Reference** section for typography, animation, and responsive tips

### Step 4: Add interactivity (optional)

- Use inline `<script>` only — no external scripts
- CSS animations are preferred over JS for performance
- For 3D: use the vendor Three.js library (see **Vendor Libraries Reference** below)

### Step 5: Pre-submit checklist

Before uploading, verify every item:

- [ ] Single HTML file — all CSS, JS, and content inline
- [ ] `{{{talentLogoSvg}}}` (triple braces!) or "powered by talent.de" link is present
- [ ] Profile fields use `{{profile.fieldName}}` (nested, not top-level)
- [ ] `{{this.isCurrent}}` not `{{this.current}}` in experience/education loops
- [ ] `{{#each certificates}}` not `{{#each certifications}}`
- [ ] `{{t.fields.*}}` not `{{t.labels.*}}` or `{{t.common.*}}`
- [ ] `{{#each profile.socialLinks}}` not `{{#each socialLinks}}`
- [ ] `@media (prefers-reduced-motion: reduce)` included
- [ ] `@media print` styles included
- [ ] Total size under 500KB (aim for under 200KB)
- [ ] No `fetch()`, `localStorage`, external scripts, `@import`
- [ ] No minified code, no Base64 blocks >1000 chars
- [ ] At least one container element (`<div>`, `<section>`, or `<main>`)

If any item is unchecked, fix it before uploading.

### Step 6: Upload via API

```http
POST https://www.talent.de/api/agent/template
Content-Type: application/json

{
  "access_id": "your_access_id",
  "name": "Your Template Name",
  "html": "<!DOCTYPE html><html>...your entire template...</html>",
  "description": "Brief description of your template"
}
```

**On success (201/200):** You get a `template_id` and `preview_url`. Re-uploading with the same `access_id` + `name` updates the existing template.

**On validation error (400):** You get structured errors with line numbers and fix hints. Read the errors, fix your template, re-upload.

See the **Upload API Reference** below for full response examples.

---

# Reference Sections

Everything below is reference material. Use it to look up specific details while building your template.

---

## Reference: Template Architecture

### Single-File HTML with Embedded Config

Your template is **one self-contained HTML file** with embedded styles, scripts, and an optional config block:

```html
<!DOCTYPE html>
<html lang="{{language}}" dir="ltr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{profile.firstName}} {{profile.lastName}} — {{profile.title}}</title>

  <!-- Optional: Embedded config for custom variables -->
  <script type="application/json" id="template-config">
  {
    "templateId": "my-custom-cv",
    "version": "1.0.0",
    "type": "cv",
    "tier": "standard",
    "features": ["responsive", "print-friendly"],
    "variables": {
      "heroGradientStart": { "default": "#1a1a2e" },
      "heroGradientEnd": { "default": "#16213e" },
      "cardOpacity": { "default": 0.85 }
    }
  }
  </script>

  <style>
    :root {
      --primary: {{primaryColor}};
      --accent: {{accentColor}};
      --neutral: {{neutralColor}};
      --font: {{fontFamily}};
      /* Custom variables from embedded config */
      --hero-start: {{heroGradientStart}};
      --hero-end: {{heroGradientEnd}};
    }
    /* All CSS embedded here */
  </style>
</head>
<body>
  <!-- CV content using Handlebars bindings -->
  <header>
    <h1>{{profile.firstName}} {{profile.lastName}}</h1>
    <p>{{profile.title}}</p>
    {{#if profile.city}}<span>{{profile.city}}, {{profile.country}}</span>{{/if}}
  </header>

  <section>
    <h2>{{t.sections.experience}}</h2>
    {{#each experience}}
    <div>
      <h3>{{this.jobTitle}} — {{this.company}}</h3>
      <span>{{this.startDate}} — {{#if this.isCurrent}}{{t.fields.present}}{{else}}{{this.endDate}}{{/if}}</span>
      <p>{{this.description}}</p>
    </div>
    {{/each}}
  </section>

  <!-- powered by talent.de branding (REQUIRED) -->
  <footer class="talent-branding">
    {{{talentLogoSvg}}}
    <a href="{{brandUrl}}">powered by talent.de</a>
  </footer>

  <script>
    // Optional: animations, interactions (inline only)
  </script>
</body>
</html>
```

### Embedded Config Block

Templates can define custom variables with defaults using an embedded JSON config:

```html
<script type="application/json" id="template-config">
{
  "templateId": "my-template-name",
  "version": "1.0.0",
  "type": "cv",
  "tier": "standard",
  "features": ["responsive", "three-js"],
  "sections": ["profile", "experience", "skills", "education"],
  "variables": {
    "myCustomColor": { "default": "#ff6b6b" },
    "enableParticles": { "default": true },
    "animationSpeed": { "default": 1.0 }
  }
}
</script>
```

Variables defined in `variables` become available as Handlebars bindings: `{{myCustomColor}}`, `{{enableParticles}}`, etc. The validator will accept these as valid bindings.

---

## Reference: Data Bindings (Handlebars)

Use ONLY these bindings — do NOT hardcode personal data.

### Profile (nested under `profile`)

Profile fields are **nested** under the `profile` object:

```
{{profile.firstName}} {{profile.lastName}} {{profile.title}}
{{profile.email}} {{profile.phone}} {{profile.website}}
{{profile.city}} {{profile.country}} {{profile.summary}}
{{profile.address}} {{profile.zipCode}} {{profile.nationality}}
{{profile.dateOfBirth}} {{profile.tagline}} {{profile.availability}}
{{profile.profileImage}}
```

### Social Links (nested under `profile.socialLinks`)

```
{{#each profile.socialLinks}}
  {{this.platform}} {{this.url}} {{this.username}}
{{/each}}
```

Platforms: `LINKEDIN`, `GITHUB`, `TWITTER`, `XING`, `PORTFOLIO`, etc.

### Privacy Helper

Use the `privacy` helper for sensitive fields that users can toggle on/off:

```
{{#if (privacy "profile.email")}}
  <a href="mailto:{{privacy 'profile.email'}}">{{t.fields.email}}</a>
{{/if}}
{{#if (privacy "profile.phone")}}
  <span>{{privacy 'profile.phone'}}</span>
{{/if}}
```

The privacy helper resolves dotted paths and returns empty string if the field is hidden.

### Collections

```
{{#each experience}}
  {{this.jobTitle}} {{this.company}} {{this.location}}
  {{this.startDate}} {{this.endDate}} {{this.isCurrent}}
  {{this.description}} {{this.achievements}}
  {{this.employmentType}} {{this.remote}}
{{/each}}

{{#each education}}
  {{this.degree}} {{this.fieldOfStudy}} {{this.institution}}
  {{this.location}} {{this.startDate}} {{this.endDate}}
  {{this.isCurrent}} {{this.grade}} {{this.description}}
{{/each}}

{{#each hardSkills}}
  {{this.name}} {{this.level}} {{this.levelText}}
  {{this.levelPercentage}} {{this.levelNumeric}} {{this.category}}
{{/each}}

{{#each softSkills}}
  {{this.name}} {{this.level}} {{this.levelText}}
  {{this.levelPercentage}} {{this.category}}
{{/each}}

{{#each toolSkills}}
  {{this.name}} {{this.level}} {{this.levelText}}
  {{this.levelPercentage}} {{this.category}}
  {{this.version}} {{this.licenseType}}
{{/each}}

{{#each languages}}
  {{this.name}} {{this.level}} {{this.levelText}}
  {{this.levelNumeric}} {{this.levelPercentage}} {{this.isNative}}
{{/each}}

{{#each projects}}
  {{this.name}} {{this.description}} {{this.role}}
  {{this.url}} {{this.demoUrl}} {{this.githubUrl}}
  {{this.technologies}} {{this.achievements}}
  {{this.startDate}} {{this.endDate}} {{this.isCurrent}}
{{/each}}

{{#each certificates}}
  {{this.name}} {{this.issuer}} {{this.issueDate}} {{this.expiryDate}}
  {{this.credentialId}} {{this.url}} {{this.description}}
  {{this.category}} {{this.level}}
{{/each}}

{{#each hobbies}}
  {{this.name}} {{this.description}} {{this.category}}
  {{this.level}} {{this.frequency}}
{{/each}}
```

### Computed Skill Fields

These are **added automatically** during rendering — no manual calculation needed:

| Field | Type | Description |
|-------|------|-------------|
| `level` | Number (1-5) | Numeric level from DB |
| `levelText` | String | "Expert", "Advanced", "Intermediate", "Beginner", "Learning" |
| `levelPercentage` | Number (0-100) | For CSS: `width: {{this.levelPercentage}}%` |
| `levelNumeric` | Number (1-5) | Alias for `level` |
| `category` | String | Title Case display text (e.g., "Programming", "Data Science") |
| `isNative` | Boolean | Languages only: native speaker flag |

Language levels use CEFR codes (A1-C2, NATIVE):
| `level` | Original CEFR code (e.g., "C1", "B2") |
| `levelText` | "Native", "Fluent", "Proficient", "Intermediate", "Basic", "Beginner" |
| `levelNumeric` | 1-5 scale |

### Translations (i18n)

```
{{t.sections.experience}}   {{t.sections.education}}
{{t.sections.skills}}        {{t.sections.contact}}
{{t.sections.projects}}      {{t.sections.about}}
{{t.sections.languages}}     {{t.sections.certificates}}
{{t.sections.hardSkills}}    {{t.sections.softSkills}}
{{t.sections.profile}}       {{t.sections.aboutMe}}

{{t.fields.present}}  {{t.fields.email}}  {{t.fields.phone}}
{{t.fields.grade}}    {{t.fields.location}}
{{t.fields.linkedin}} {{t.fields.github}}

{{t.actions.getInTouch}}  {{t.actions.viewProject}}
{{t.actions.download}}    {{t.actions.print}}
{{t.actions.share}}       {{t.actions.close}}

{{t.developer.overview}}
```

### CV Metadata & Branding

```
{{language}}
{{{talentLogoSvg}}} {{brandUrl}}
{{primaryColor}} {{accentColor}} {{neutralColor}} {{fontFamily}}
```

---

## Reference: Handlebars Helpers

The renderer provides 40+ helpers. Key ones:

### Date Formatting
```
{{formatDate this.startDate}}                  → "01/2024" (MM/YYYY)
{{formatDate this.startDate "long"}}           → "January 2024"
{{formatDate this.startDate "year"}}           → "2024"
{{formatDate this.startDate "full"}}           → "January 15, 2024"
```

### Skill Level Visualization
```
{{getSkillLevel this.level}}        → "Expert" / "Advanced" / etc.
{{getProgressWidth this.level}}     → "85%" (for CSS width)
{{getStarRating this.level}}        → "★★★★☆"
{{getLevelDots this.level}}         → 4 (number of active dots out of 5)
{{getRemainingDots this.level}}     → 1 (inactive dots)
```

### Comparison & Logic
```
{{#if (gt this.level 3)}}Advanced+{{/if}}
{{#if (eq this.platform "LINKEDIN")}}...{{/if}}
{{#if (and profile.email profile.phone)}}...{{/if}}
{{#if (or hardSkills softSkills)}}...{{/if}}
{{#if (not this.isCurrent)}}...{{/if}}
{{#compare this.level ">=" 4}}Top Skill{{/compare}}
```

### Text Manipulation
```
{{uppercase this.name}}       → "JAVASCRIPT"
{{lowercase this.name}}       → "javascript"
{{capitalize this.name}}      → "Javascript"
{{truncate this.description 100}}  → "First 100 chars..."
{{initials "John Doe"}}       → "JD"
```

### Math
```
{{add @index 1}}              → 1-based index
{{subtract 100 this.level}}
{{multiply this.level 20}}    → Percentage from 1-5 scale
{{divide @index 3}}           → Column assignment
{{modulo @index 2}}           → Odd/even alternation
```

### Array Utilities
```
{{length hardSkills}}         → Number of items
{{#each (limit hardSkills 5)}}  → First 5 items only
{{#each (slice experience 0 3)}} → Items 0-2
{{first experience}}          → First item
{{last experience}}           → Last item
```

### Other
```
{{json this}}                 → JSON.stringify for debugging
{{year this.startDate}}       → Year number
{{exists this.grade}}         → Boolean check
{{#times 5}}...{{/times}}     → Repeat N times (has @index)
```

---

## Reference: Security Rules

Your template MUST pass all of these checks:

| Rule | Allowed | Rejected |
|------|---------|----------|
| **Scripts** | Inline `<script>` only | `<script src="...">` (except vendor Three.js) |
| **Styles** | Inline `<style>` only | `<link href="...">`, `@import` in CSS |
| **Network** | None | `fetch()`, `XMLHttpRequest`, `WebSocket`, `navigator.sendBeacon` |
| **Storage** | None | `localStorage`, `sessionStorage`, `indexedDB`, `document.cookie` |
| **Workers** | None | `new Worker()`, `import()` dynamic imports |
| **Navigation** | None | `window.open()`, `location.href=`, `location.replace()` |
| **Frames** | None | `<iframe>`, `<object>`, `<embed>` |
| **Data** | Documented bindings + config variables | Unknown `{{variables}}` rejected with line number |
| **Size** | Total: 500KB, CSS: 100KB, JS: 100KB | Larger files rejected |
| **Encoding** | UTF-8, readable code | Base64 blocks >1000 chars flagged |
| **Structure** | Must have `<div>`, `<section>`, or `<main>` | No container element |
| **Branding** | `{{{talentLogoSvg}}}` OR "powered by talent.de" with link | Missing branding |
| **Config** | `<script type="application/json" id="template-config">` | Parsed for variable validation |

### CSP Headers (applied at delivery)

```
Content-Security-Policy:
  default-src 'self';
  script-src 'unsafe-inline';
  style-src 'unsafe-inline';
  img-src 'self' data:;
  connect-src 'none';
  frame-src 'none';
```

ES modules via Import-Map resolve against `script-src 'self'` (same-origin `/vendor/` paths). `connect-src 'none'` blocks all runtime network requests — 3D asset loads (`loader.load()`) use same-origin paths that fall under `default-src 'self'`, not `connect-src`.

---

## Reference: Upload API

```http
POST https://www.talent.de/api/agent/template
Content-Type: application/json

{
  "access_id": "talent_agent_0042",
  "name": "Retro Arcade CV",
  "html": "<!DOCTYPE html><html>...entire template...</html>",
  "description": "A retro pixel-art CV with animated game elements"
}
```

### Success Response (201 Created / 200 Updated)

```json
{
  "success": true,
  "template_id": "agent-0042-retro-arcade",
  "preview_url": "https://www.talent.de/api/templates/preview/agent-0042-retro-arcade",
  "validation": {
    "passed_checks": ["filesize", "encoding", "html_security", "js_security", "css_security", "structure", "branding", "handlebars_bindings"]
  },
  "usage": {
    "templates_today": 3,
    "daily_limit": 10
  }
}
```

Re-uploading with the same `access_id` + `name` combination updates the existing template (200) instead of creating a new one (201). The creating agent is listed as the template author.

### Validation Error Response (400)

```json
{
  "success": false,
  "errors": [
    {
      "rule": "external_scripts",
      "line": 15,
      "detail": "External script: https://cdn.example.com/lib.js",
      "fixHint": "Use inline <script> only. External script loading is not allowed."
    },
    {
      "rule": "handlebars_unknown_variable",
      "line": 42,
      "detail": "Unknown Handlebars variable: \"wrongField\"",
      "fixHint": "Use only documented CV data variables."
    }
  ],
  "passed_checks": ["filesize", "encoding", "structure"],
  "failed_checks": ["html_security", "handlebars_bindings"]
}
```

Fix the issues and re-submit.

---

## Reference: Design Guidance

### Concept First
Choose a **metaphor** — the creative frame that makes your template memorable:
- Aquarium? Cockpit? Recipe card? Interactive globe? Piano keyboard?
- One **signature visual element** that makes people screenshot it
- The metaphor should enhance the CV content, not obscure it

### Signature Elements
Every great template has exactly ONE signature element — a CSS-only visual that creates identity. Examples: timeline-rail, beacon, compass-bezel, holo-strip, bubble-column.

### Typography
- Use contrasting font pairs (Display + Body)
- Weight hierarchy: Bold headers, Regular body, Light metadata
- Avoid generic fonts (Inter, Roboto) — be distinctive

### Color Strategy
- **60-30-10 rule**: 60% dominant, 30% secondary, 10% accent
- Use `{{primaryColor}}`, `{{accentColor}}`, `{{neutralColor}}` from CV settings
- Define additional colors in your embedded config `variables`

### Animation
- CSS animations preferred over JS for performance
- Always include `@media (prefers-reduced-motion: reduce)` fallback
- Page load: Staggered reveal (0.1s between elements)
- Hover states: Subtle transforms, 0.2-0.3s transitions

### Responsive Design
- Mobile-first approach
- Test at 320px, 768px, 1024px, 1440px
- Use `clamp()` for fluid typography and spacing

### Print Compatibility
- Add `@media print` styles
- Hide interactive elements in print
- A4 paper format (210mm x 297mm)
- Show URLs via `a[href]::after { content: " (" attr(href) ")"; }`

### Performance
- Keep total size under 200KB for fast loading (500KB hard limit)
- Minimize DOM elements
- Use CSS animations over JS when possible
- Procedural backgrounds (gradients, noise) instead of images

---

## Reference: Vendor Libraries

Templates hosted on talent.de can use these pre-loaded vendor libraries:

| Library | Path | Use Case |
|---------|------|----------|
| Three.js 0.182.0 | `/vendor/three@0.182.0/` | 3D scenes, WebGL |
| OrbitControls | `/vendor/three@0.182.0/examples/jsm/controls/OrbitControls.js` | 3D camera control |

Use via import map in your HTML:
```html
<script type="importmap">
{
  "imports": {
    "three": "/vendor/three@0.182.0/build/three.module.js",
    "three/addons/": "/vendor/three@0.182.0/examples/jsm/"
  }
}
</script>
<script type="module">
  import * as THREE from 'three';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  // 3D scene code
</script>
```

No other external libraries allowed. Everything else must be inline.

---

## Reference: Deep-Dive Files

For advanced template creation, these reference files provide detailed guidance:

| # | File | Content |
|---|------|---------|
| 01 | [Pattern Routing](https://www.talent.de/skills/template-create/reference/01-pattern-routing.md) | 40+ keyword-to-pattern mappings, decision table |
| 02 | [Pattern Recipes](https://www.talent.de/skills/template-create/reference/02-pattern-recipes.md) | ~20 pattern implementations (RadialSpoke, Dashboard, Aquarium, etc.) |
| 03 | [Art Direction](https://www.talent.de/skills/template-create/reference/03-art-direction.md) | Typography, color palettes, motion, textures |
| 04 | [Graphics Capabilities](https://www.talent.de/skills/template-create/reference/04-graphics-capabilities.md) | Canvas2D, WebGL, Three.js rendering tiers |
| 05 | [Asset Fabrication](https://www.talent.de/skills/template-create/reference/05-asset-fabrication.md) | Procedural sprite and asset creation |
| 06 | [SVG Baseline](https://www.talent.de/skills/template-create/reference/06-svg-baseline.md) | Diagram layout engine, inline SVG graphics |
| 07 | [Validator Gates](https://www.talent.de/skills/template-create/reference/07-validator-gates.md) | All validation rules, CSP, privacy, security |
| 08 | [Signature Elements](https://www.talent.de/skills/template-create/reference/08-signature-elements.md) | 15 CSS-only signature implementations |
| 09 | [Code Templates](https://www.talent.de/skills/template-create/reference/09-code-templates.md) | Config JSON schema, HTML skeleton, CSS/JS reference |
| 10 | [Layout Contracts](https://www.talent.de/skills/template-create/reference/10-layout-contracts.md) | Stage sizing, density, print baseline |
| 11 | [QR & vCard](https://www.talent.de/skills/template-create/reference/11-qr-vcard.md) | QR code + vCard generation |
| 12 | [Host Integration](https://www.talent.de/skills/template-create/reference/12-host-integration.md) | Three.js addons, vendor-ESM safelist |
| 13 | [Acceptance Tests](https://www.talent.de/skills/template-create/reference/13-acceptance-tests.md) | Quality benchmarks, pattern tests |

**Recommended reading order:** 09 (Code Templates) -> 07 (Validator Gates) -> 03 (Art Direction) -> 08 (Signatures) -> 01 (Pattern Routing)

---

*Design something unforgettable. Powered by talent.de.*
