diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml
index 0dcdf21..1c198d8 100644
--- a/.gitea/workflows/deploy.yaml
+++ b/.gitea/workflows/deploy.yaml
@@ -14,10 +14,10 @@ jobs:
run: |
if [ "${{ gitea.ref_name }}" = "production" ]; then
echo "TARGET_DIR=/var/www/node/cdrdpyj" >> $GITHUB_ENV
- echo "PM2_ENV=production" >> $GITHUB_ENV
+ echo "APP_NAME=cdrdpyj-live" >> $GITHUB_ENV
else
- echo "TARGET_DIR=/var/www/node/dev.cdrdjyp" >> $GITHUB_ENV
- echo "PM2_ENV=staging" >> $GITHUB_ENV
+ echo "TARGET_DIR=/var/www/node/dev.cdrdpyj" >> $GITHUB_ENV
+ echo "APP_NAME=cdrdpyj" >> $GITHUB_ENV
fi
- uses: pnpm/action-setup@v4
@@ -53,7 +53,7 @@ jobs:
npm install -g pnpm@11 2>&1
cd ${{ env.TARGET_DIR }}
pnpm install --prod 2>&1
- sed -i "s|TURNSTILE_SITE_KEY: \"\"|TURNSTILE_SITE_KEY: \"${{ secrets.TURNSTILE_SITE_KEY }}\"|" ecosystem.config.cjs
- sed -i "s|TURNSTILE_SECRET_KEY: \"\"|TURNSTILE_SECRET_KEY: \"${{ secrets.TURNSTILE_SECRET_KEY }}\"|" ecosystem.config.cjs
- pm2 reload ecosystem.config.cjs --env ${{ env.PM2_ENV }} --update-env || \
- pm2 start ecosystem.config.cjs --env ${{ env.PM2_ENV }} --update-env
+ sed -i "s|TURNSTILE_SITE_KEY: \"\"|TURNSTILE_SITE_KEY: \"${{ secrets.TURNSTILE_SITE_KEY }}\"|g" ecosystem.config.cjs
+ sed -i "s|TURNSTILE_SECRET_KEY: \"\"|TURNSTILE_SECRET_KEY: \"${{ secrets.TURNSTILE_SECRET_KEY }}\"|g" ecosystem.config.cjs
+ pm2 reload ecosystem.config.cjs --only ${{ env.APP_NAME }} --update-env || \
+ pm2 start ecosystem.config.cjs --only ${{ env.APP_NAME }} --update-env
diff --git a/.opencode/skills/dynamic-form/SKILL.md b/.opencode/skills/dynamic-form/SKILL.md
new file mode 100644
index 0000000..d46dc71
--- /dev/null
+++ b/.opencode/skills/dynamic-form/SKILL.md
@@ -0,0 +1,186 @@
+---
+name: dynamic-form
+description: Use when working with the volunteer registration dynamic multi-step form. Relevant files: DynamicForm.vue, formulario-inscripcion.json, [section]/index.astro, i18n system, reglamento HTML.
+---
+
+# Dynamic Multi-Step Volunteer Registration Form
+
+## Stack
+
+- **Framework:** Astro (SSR, `output: "server"`, Node.js standalone)
+- **UI:** Vue 3 + Tailwind CSS v4 + daisyUI v5
+- **i18n:** Astro built-in i18n with `prefixDefaultLocale: true`, custom translator via `createTranslator(locale)`
+
+## Architecture
+
+### Form Config (JSON-driven)
+
+The form structure is defined in `public/forms/formulario-inscripcion.json`:
+
+```json
+{
+ "action": "/api/formulario/send",
+ "submit_label": "form.submit",
+ "steps": [
+ {
+ "label": "form.step1_reglamento",
+ "fields": [ ... ]
+ }
+ ]
+}
+```
+
+Each field supports:
+- `type`: `text`, `email`, `phone`, `date`, `textarea`, `radio`, `checkbox`, `select`, `autocomplete`
+- `required`: boolean (for checkbox, individual options can also have `required: true`)
+- `colspan`: `2` or `3` for grid layout when step has `column: 2` or `column: 3`
+- `readonly`: displays value from `formData` (shared across steps). Supports `valueFrom` to reference another field's key.
+- `showWhen`: `{ field: "other_key", value: "expected_value" }` — conditional display; for checkboxes checks array inclusion
+- `levels`: proficiency radios for each selected checkbox option (e.g., Básico/Intermedio/Avanzado)
+- `placeholder`: translation key for placeholder text
+- `source`: URL to JSON file for `select`/`autocomplete` options (e.g., `/forms/paises.json`)
+
+### Key Files
+
+| File | Purpose |
+|------|---------|
+| `src/components/forms/DynamicForm.vue` | Core form component (751 lines) |
+| `public/forms/formulario-inscripcion.json` | Form structure config (8 steps) |
+| `public/forms/paises.json` | Country list for `select`/`autocomplete` |
+| `src/pages/[locale]/[section]/index.astro` | Routes news, editorial, AND formulario |
+| `src/i18n/index.ts` | Translator, `routeTranslations`, `I18nKey` type |
+| `src/i18n/es.json` | Spanish translations (all form keys) |
+| `public/reglamento/es.html` | Reglamento content (section-based HTML) |
+
+### Field Types
+
+| Type | Rendering | Behavior |
+|------|-----------|----------|
+| `text` | `` inside daisyUI `