Index: src/views/pages/post-edit.ejs
===================================================================
--- src/views/pages/post-edit.ejs	(revision 2165b6d3d57ceaa8b5d1f70491868482149470cf)
+++ src/views/pages/post-edit.ejs	(revision 7bc636b391c66ac399c33e54f7173a022c6a3cbd)
@@ -9,100 +9,29 @@
 // and styling changed.
 const _hasCover = !!post.cover_image_url;
-// In hub mode, save/create/cancel must carry the /user/<slug>/ prefix, otherwise
-// the request falls back to the primary site (siteUrlBase empty) and the post
-// renders headerless (bareChrome) after saving. In solo mode _base is empty.
-const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
 %>
 <div class="container post-edit-page">
-  <h1><%= isNew ? t('pedit.title_new') : t('pedit.title_edit') %></h1>
-
-  <form method="post" action="<%= _base %><%= isNew ? '/posts/create' : '/posts/' + post.slug + '/save' %>" class="post-edit-form">
-
-    <%# ── TYPE (segmented) + type-specific inputs ────────────────
-        Post type lives at the TOP (above the body), and the selected type
-        reveals its own input: Audio → inline upload, Video → embed URL,
-        Foto → cover/insert hint. %>
-    <% var ptype = post.type || 'post'; %>
-    <% var TYPE_ICONS = { post: '📝', foto: '📷', video: '🎬', album: '💿', playlist: '📃', mixtape: '📼', audio: '🎵' }; %>
-    <%# Audio is geen keuze meer (shaer-cyg): alles wat muziek is landt op Album
-        of Playlist. De knop blijft alleen staan zolang deze post nog audio IS,
-        zodat een oude post niet stilzwijgend van type verandert bij het openen. %>
-    <% var TYPES = (typeof keuzeTypes !== 'undefined' && keuzeTypes.length)
-                     ? keuzeTypes.slice() : ['post','foto','video','album','playlist','mixtape']; %>
-    <% if (ptype === 'audio' && TYPES.indexOf('audio') < 0) TYPES.push('audio'); %>
-    <section class="pe-card pe-type-card">
-      <div class="pe-section-title"><%= t('pedit.s_type') %></div>
-      <input type="hidden" name="type" id="pe-type-input" value="<%= ptype %>">
-      <div class="pe-typeseg" role="radiogroup" aria-label="<%= t('pedit.s_type') %>">
-        <% TYPES.forEach(function (tt) { %>
-          <button type="button" class="pe-typeseg-btn<%= ptype === tt ? ' is-active' : '' %>"
-                  data-type="<%= tt %>" role="radio" aria-checked="<%= ptype === tt ? 'true' : 'false' %>">
-            <span class="pe-typeseg-ic" aria-hidden="true"><%= TYPE_ICONS[tt] %></span>
-            <span><%= t('pedit.type_' + tt) %></span>
-          </button>
-        <% }); %>
-      </div>
-
-      <%# Muziek: inline upload (transcodes + drops [[track]] into the post).
-          Album, Playlist en Mixtape delen dit paneel -- het verschil zit in de
-          playlist die je insluit, niet in wat je hier uploadt. Audio staat er
-          alleen nog voor oude posts. %>
-      <div class="pe-type-panel" data-panel="album playlist mixtape audio" hidden>
-        <div class="pe-audio-up" id="pe-audio-drop" tabindex="0" role="button" aria-label="<%= t('pedit.audio_up_drop') %>">
-          <input type="file" id="pe-audio-file" accept="audio/*,.mp3,.m4a,.ogg,.opus,.flac,.wav" multiple hidden>
-          <span class="pe-audio-up-ic" aria-hidden="true">🎵</span>
-          <strong><%= t('pedit.audio_up_drop') %></strong>
-          <small><%= t('pedit.audio_up_hint') %></small>
-        </div>
-        <ul class="pe-audio-list" id="pe-audio-list"></ul>
-      </div>
-
-      <%# Video: paste a URL → embed chip %>
-      <div class="pe-type-panel" data-panel="video" hidden>
-        <label class="pe-field">
-          <span><%= t('pedit.video_up_title') %></span>
-          <div class="pe-video-row">
-            <input type="url" id="pe-video-url" inputmode="url" autocapitalize="none" spellcheck="false"
-                   placeholder="<%= t('pedit.video_up_ph') %>">
-            <button type="button" class="pe-btn pe-btn-secondary" id="pe-video-insert"><%= t('pedit.video_up_btn') %></button>
-          </div>
-        </label>
-      </div>
-
-      <%# Foto: light hint toward cover + insert-image %>
-      <div class="pe-type-panel" data-panel="foto" hidden>
-        <p class="pe-foto-hint">🖼 <%= t('pedit.foto_up_hint') %></p>
-      </div>
-    </section>
+  <h1><%= isNew ? 'Nieuwe post' : 'Post bewerken' %></h1>
+
+  <form method="post" action="<%= isNew ? '/posts/create' : '/posts/' + post.slug + '/save' %>" class="post-edit-form">
 
     <%# ── IDENTITY ─────────────────────────────────────────────── %>
     <section class="pe-card">
       <label class="pe-field">
-        <span><%= t('pedit.f_title') %></span>
-        <input type="text" name="title" value="<%= post.title %>" required>
+        <span>Titel</span>
+        <input type="text" name="title" value="<%= post.title %>" required autofocus>
       </label>
       <div class="pe-row pe-row-2">
         <label class="pe-field">
-          <span><%= t('pedit.f_slug') %></span>
-          <input type="text" name="slug" value="<%= post.slug %>" placeholder="<%= t('pedit.slug_placeholder') %>">
+          <span>Slug (URL)</span>
+          <input type="text" name="slug" value="<%= post.slug %>" placeholder="auto van titel als leeg">
         </label>
         <label class="pe-field">
-          <span><%= t('pedit.f_tags') %> <small><%= t('pedit.tags_hint') %></small></span>
+          <span>Tags <small>(komma-gescheiden)</small></span>
           <input type="text" name="tags" value="<%= Array.isArray(post.tags) ? post.tags.join(', ') : '' %>">
         </label>
       </div>
       <label class="pe-field">
-        <span><%= t('pedit.f_excerpt') %></span>
+        <span>Excerpt <small>(preview tekst)</small></span>
         <textarea name="excerpt" rows="2"><%= post.excerpt %></textarea>
-        <small class="pe-hint" style="opacity:.7"><%- t('pedit.excerpt_hint') %></small>
-      </label>
-      <% var _postLang = (typeof post.language !== 'undefined' && post.language) ? post.language : (typeof lang !== 'undefined' ? lang : 'en'); %>
-      <label class="pe-field">
-        <span><%= t('pedit.f_language') %> <small><%= t('pedit.language_hint') %></small></span>
-        <select name="language" id="pe-language" style="max-width:220px">
-          <% [['en','English'],['nl','Nederlands'],['de','Deutsch'],['fr','Français'],['es','Español'],['it','Italiano'],['pt','Português'],['pl','Polski'],['ru','Русский'],['uk','Українська'],['sv','Svenska'],['da','Dansk'],['nb','Norsk'],['fi','Suomi'],['tr','Türkçe'],['ja','日本語'],['zh','中文'],['ar','العربية']].forEach(function (l) { %>
-            <option value="<%= l[0] %>" <%= l[0] === _postLang ? 'selected' : '' %>><%= l[1] %></option>
-          <% }); %>
-        </select>
       </label>
     </section>
@@ -110,5 +39,5 @@
     <%# ── COVER ────────────────────────────────────────────────── %>
     <section class="pe-card">
-      <div class="pe-section-title"><%= t('pedit.s_cover') %></div>
+      <div class="pe-section-title">Cover</div>
       <div class="pe-cover-row">
         <div class="pe-cover-thumb" id="cover-preview-wrap" <%= _hasCover ? '' : 'data-empty' %>>
@@ -118,21 +47,12 @@
         <div class="pe-cover-actions">
           <label class="pe-field">
-            <span><%= t('pedit.f_cover_url') %></span>
-            <input type="text" name="cover_image_url" id="cover-url-field"
+            <span>Cover URL</span>
+            <input type="url" name="cover_image_url" id="cover-url-field"
                    value="<%= post.cover_image_url || '' %>"
-                   inputmode="url" autocapitalize="none" spellcheck="false"
-                   placeholder="<%= t('pedit.cover_url_placeholder') %>">
+                   placeholder="https://…  of upload met de knop">
           </label>
-          <label class="pe-field">
-            <span><%= t('pedit.f_cover_alt') %></span>
-            <input type="text" name="cover_alt" id="cover-alt-field" maxlength="1500"
-                   value="<%= post.cover_alt || '' %>"
-                   placeholder="<%= t('pedit.cover_alt_placeholder') %>">
-          </label>
-          <%# Muted loop MP4 for an animated cover (auto-made from an animated WebP). Hidden — set by the uploader. %>
-          <input type="hidden" name="cover_video_url" id="cover-video-field" value="<%= post.cover_video_url || '' %>">
           <div class="pe-cover-upload">
             <button type="button" class="pe-btn pe-btn-secondary" id="cover-upload-trigger">
-              📷 <%= t('pedit.cover_upload_btn') %>
+              📷 Upload nieuwe cover
             </button>
             <input type="file" id="cover-upload-field" accept="image/jpeg,image/png,image/webp,image/gif" hidden>
@@ -146,59 +66,51 @@
     <section class="pe-card pe-card-content">
       <div class="pe-section-title">
-        <%= t('pedit.s_content') %>
-        <small class="pe-content-hint"><%= t('pedit.content_hint') %></small>
+        Content
+        <small>Sleep een afbeelding om in te voegen · selecteer tekst om op te maken</small>
       </div>
       <div class="pe-editor-frame">
         <div class="pe-toolbar" id="pe-toolbar" role="toolbar" aria-label="Format">
-          <button type="button" id="pe-fs-done" class="pe-fs-done" title="<%= t('pedit.tb_done_title') %>">✓ <%= t('pedit.tb_done') %></button>
-          <button type="button" data-cmd="bold" title="<%= t('pedit.tb_bold_title') %>" aria-label="<%= t('pedit.tb_bold') %>">
+          <button type="button" data-cmd="bold" title="Vet (Ctrl+B)" aria-label="Vet">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4h8a4 4 0 0 1 0 8H6z"/><path d="M6 12h9a4 4 0 0 1 0 8H6z"/></svg>
           </button>
-          <button type="button" data-cmd="italic" title="<%= t('pedit.tb_italic_title') %>" aria-label="<%= t('pedit.tb_italic') %>">
+          <button type="button" data-cmd="italic" title="Cursief (Ctrl+I)" aria-label="Cursief">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg>
           </button>
-          <button type="button" data-cmd="underline" title="<%= t('pedit.tb_underline') %>" aria-label="<%= t('pedit.tb_underline') %>">
+          <button type="button" data-cmd="underline" title="Onderstreept" aria-label="Onderstreept">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4v6a6 6 0 0 0 12 0V4"/><line x1="4" y1="20" x2="20" y2="20"/></svg>
           </button>
           <span class="pe-toolbar-sep" aria-hidden="true"></span>
-          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="<%= t('pedit.tb_h2') %>">H2</button>
-          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="<%= t('pedit.tb_h3') %>">H3</button>
-          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p"  title="<%= t('pedit.tb_p') %>">¶</button>
+          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h2" title="Kop">H2</button>
+          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="h3" title="Subkop">H3</button>
+          <button type="button" class="pe-tb-text" data-cmd="formatBlock" data-arg="p"  title="Paragraaf">¶</button>
           <span class="pe-toolbar-sep" aria-hidden="true"></span>
-          <button type="button" data-cmd="insertUnorderedList" title="<%= t('pedit.tb_ul') %>" aria-label="<%= t('pedit.tb_ul') %>">
+          <button type="button" data-cmd="insertUnorderedList" title="Lijst" aria-label="Lijst">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="9" y1="6" x2="20" y2="6"/><line x1="9" y1="12" x2="20" y2="12"/><line x1="9" y1="18" x2="20" y2="18"/><circle cx="4.5" cy="6" r="1.2"/><circle cx="4.5" cy="12" r="1.2"/><circle cx="4.5" cy="18" r="1.2"/></svg>
           </button>
-          <button type="button" data-cmd="insertOrderedList" title="<%= t('pedit.tb_ol') %>" aria-label="<%= t('pedit.tb_ol') %>">
+          <button type="button" data-cmd="insertOrderedList" title="Genummerde lijst" aria-label="Genummerde lijst">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg>
           </button>
-          <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="<%= t('pedit.tb_quote') %>" aria-label="<%= t('pedit.tb_quote') %>">
+          <button type="button" data-cmd="formatBlock" data-arg="blockquote" title="Quote" aria-label="Quote">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2H4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h2.5C6 17.5 4 19 3 19v2z"/><path d="M14 21c3 0 7-1 7-8V5c0-1.25-.75-2-2-2h-4c-1.25 0-2 .75-2 2v6c0 1.25.75 2 2 2h2.5c-.5 4.5-2.5 6-3.5 6v2z"/></svg>
           </button>
-          <button type="button" data-cmd="link-prompt" title="<%= t('pedit.tb_link_title') %>" aria-label="<%= t('pedit.tb_link') %>">
+          <button type="button" data-cmd="link-prompt" title="Link (Ctrl+K)" aria-label="Link">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
           </button>
-          <button type="button" data-cmd="code-wrap" title="<%= t('pedit.tb_code_title') %>" aria-label="<%= t('pedit.tb_code') %>">
+          <button type="button" data-cmd="code-wrap" title="Code (inline)" aria-label="Code">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
           </button>
           <span class="pe-toolbar-sep" aria-hidden="true"></span>
-          <button type="button" id="insert-image-btn" title="<%= t('pedit.tb_image_title') %>" aria-label="<%= t('pedit.tb_image') %>">
+          <button type="button" id="insert-image-btn" title="Afbeelding invoegen" aria-label="Afbeelding">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg>
           </button>
-          <button type="button" id="insert-track-btn" title="<%= t('pedit.tb_track_title') %>" aria-label="<%= t('pedit.tb_track') %>">
+          <button type="button" id="insert-track-btn" title="Track invoegen" aria-label="Track">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
           </button>
-          <button type="button" id="insert-playlist-btn" title="<%= t('pedit.tb_playlist_title') %>" aria-label="<%= t('pedit.tb_playlist') %>">
+          <button type="button" id="insert-playlist-btn" title="Playlist invoegen" aria-label="Playlist">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="15" y2="18"/><polygon points="3 5 3 13 9 9"/></svg>
           </button>
-          <button type="button" id="insert-embed-btn" title="<%= t('pedit.tb_embed_title') %>" aria-label="<%= t('pedit.tb_embed') %>">
-            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><polygon points="10 9 15.5 12 10 15"/></svg>
-          </button>
           <span class="pe-toolbar-spacer"></span>
-          <button type="button" data-cmd="removeFormat" title="<%= t('pedit.tb_clear') %>" aria-label="<%= t('pedit.tb_clear') %>">
+          <button type="button" data-cmd="removeFormat" title="Wis opmaak" aria-label="Wis opmaak">
             <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M5 5l14 14" stroke-linecap="round"/></svg>
-          </button>
-          <button type="button" id="pe-fullscreen-btn" title="<%= t('pedit.tb_fullscreen') %>" aria-label="<%= t('pedit.tb_fullscreen') %>" aria-pressed="false">
-            <svg class="fs-icon-expand" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
-            <svg class="fs-icon-compress" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="4 14 10 14 10 20"/><polyline points="20 10 14 10 14 4"/><line x1="14" y1="10" x2="21" y2="3"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
           </button>
         </div>
@@ -209,15 +121,10 @@
              role="textbox"
              aria-multiline="true"
-             aria-label="<%= t('pedit.editor_aria') %>"></div>
-
-        <%# Sticky "tap to edit" hint (only visible on touch + non-fullscreen
-            via CSS). Purely visual (pointer-events:none); tapping the field opens fullscreen. %>
-        <div class="pe-edit-hint" aria-hidden="true">✎ <%= t('pedit.tap_to_edit') %></div>
+             aria-label="Content"
+             data-placeholder="Begin met schrijven…"></div>
 
         <%# Hidden field is what actually submits to the server. The visible
             contenteditable's serialized HTML is copied here on submit. %>
-        <%# value = de BESTAANDE inhoud: laadt de module niet (oude cache, js uit),
-    dan slaat het formulier de post ongewijzigd op in plaats van hem te wissen. %>
-        <input type="hidden" name="content" id="content-hidden" value="<%= post.content || '' %>">
+        <input type="hidden" name="content" id="content-hidden" value="">
 
         <%# Initial content is injected as a JSON string in a script tag so we
@@ -228,5 +135,5 @@
         <div class="pe-editor-status">
           <small id="content-upload-status" class="pe-status"></small>
-          <span class="pe-char-count"><span id="char-count">0</span> <%= t('pedit.chars') %></span>
+          <span class="pe-char-count"><span id="char-count">0</span> tekens</span>
         </div>
 
@@ -237,130 +144,38 @@
     <%# ── META ─────────────────────────────────────────────────── %>
     <section class="pe-card">
-      <div class="pe-section-title"><%= t('pedit.s_publication') %></div>
-      <label class="pe-field">
-        <span><%= t('pedit.f_status') %></span>
-        <% var _st = isNew ? 'published' : (post.status || 'draft'); %>
-        <select name="status">
-          <option value="published" <%= _st === 'published' ? 'selected' : '' %>><%= t('pedit.status_published') %></option>
-          <option value="draft"     <%= _st === 'draft'     ? 'selected' : '' %>><%= t('pedit.status_draft') %></option>
-          <option value="archived"  <%= _st === 'archived'  ? 'selected' : '' %>><%= t('pedit.status_archived') %></option>
-        </select>
-      </label>
+      <div class="pe-section-title">Publicatie</div>
+      <div class="pe-row pe-row-2">
+        <label class="pe-field">
+          <span>Status</span>
+          <select name="status">
+            <option value="draft"     <%= post.status === 'draft'     ? 'selected' : '' %>>Draft</option>
+            <option value="published" <%= post.status === 'published' ? 'selected' : '' %>>Published</option>
+            <option value="archived"  <%= post.status === 'archived'  ? 'selected' : '' %>>Archived</option>
+          </select>
+        </label>
+        <label class="pe-field">
+          <span>Type</span>
+          <select name="type">
+            <% var ptype = post.type || 'post'; %>
+            <option value="post"  <%= ptype === 'post'  ? 'selected' : '' %>>Post</option>
+            <option value="foto"  <%= ptype === 'foto'  ? 'selected' : '' %>>Foto</option>
+            <option value="video" <%= ptype === 'video' ? 'selected' : '' %>>Video</option>
+            <option value="audio" <%= ptype === 'audio' ? 'selected' : '' %>>Audio</option>
+          </select>
+        </label>
+      </div>
       <div class="pe-checkboxes">
-        <div class="pe-pin">
-          <label class="pe-checkbox">
-            <input type="checkbox" id="pin-toggle" <%= (Number(post.pinned) > 0) ? 'checked' : '' %>>
-            <span>📌 <%= t('pedit.pin_label') %></span>
-          </label>
-          <input type="hidden" name="pinned" id="pin-rank" value="<%= post.pinned || 0 %>">
-          <div class="pe-pin-pos" id="pin-pos" <%= (Number(post.pinned) > 0) ? '' : 'hidden' %>>
-            <span class="pe-pin-steps">
-              <button type="button" class="pe-pin-btn" id="pin-up" aria-label="<%= t('pedit.pin_up') %>">▲</button>
-              <button type="button" class="pe-pin-btn" id="pin-down" aria-label="<%= t('pedit.pin_down') %>">▼</button>
-            </span>
-            <span class="pe-pin-label" id="pin-label"></span>
-          </div>
-        </div>
+        <label class="pe-pinned-rank">
+          <span>📌 Pinned rank</span>
+          <input type="number" name="pinned" min="0" step="1"
+                 inputmode="numeric" pattern="[0-9]*"
+                 value="<%= post.pinned || 0 %>"
+                 title="0 = niet pinned. 1 = bovenaan, 2 = daaronder, etc.">
+          <small>0 = niet pinned · 1 = bovenaan · 2 daaronder · …</small>
+        </label>
         <label class="pe-checkbox">
           <input type="checkbox" name="noindex" value="1" <%= post.noindex ? 'checked' : '' %>>
-          <span>🚫 <%= t('pedit.noindex_label') %></span>
+          <span>🚫 noindex (verberg voor zoekmachines)</span>
         </label>
-        <label class="pe-checkbox">
-          <input type="checkbox" name="nsfw" value="1" id="pe-nsfw" <%= post.nsfw ? 'checked' : '' %>>
-          <span>🔞 <%= t('pedit.nsfw_label') %></span>
-        </label>
-        <input type="text" name="content_warning" id="pe-cw" value="<%= post.content_warning || '' %>" maxlength="200"
-               placeholder="<%= t('pedit.nsfw_cw_ph') %>"
-               style="width:100%;box-sizing:border-box;margin:6px 0 2px;font-size:13px;padding:7px 9px">
-        <% var _fediOpen = (typeof fediOpenAudio !== 'undefined' && fediOpenAudio); %>
-        <label class="pe-checkbox">
-          <%# One-way: once opened, the file has federated — re-gating would be false security, so the box locks. %>
-          <input type="checkbox" name="fedi_open_audio" value="1" id="pe-fedi-audio" <%= _fediOpen ? 'checked disabled' : '' %>>
-          <span>🌐 <%= t('pedit.fedi_audio_label') %></span>
-        </label>
-        <% if (_fediOpen) { %>
-          <p style="font-size:12px;opacity:.7;margin:2px 0 0 26px"><%= t('pedit.fedi_audio_locked') %></p>
-        <% } else { %>
-          <p id="pe-fedi-audio-warn" style="font-size:12px;color:#c0392b;margin:2px 0 0 26px" hidden>⚠️ <%= t('pedit.fedi_audio_oneway') %></p>
-          <%# De scripts van deze pagina staan in assets/js/mod/post-edit.js; de gegevens via partials/page-data.ejs (shaer-bqr). %>
-        <% } %>
-        
-        <% // Poll (federates as an AS2 Question). Free feature. A poll with votes is frozen.
-           var _poll = null; try { _poll = post.poll_json ? JSON.parse(post.poll_json) : null; } catch (e) { _poll = null; }
-           var _pollLocked = (typeof pollLocked !== 'undefined' && pollLocked);
-           var _pollOpts = (_poll && Array.isArray(_poll.options) && _poll.options.length) ? _poll.options : [{ name: '' }, { name: '' }]; %>
-        <label class="pe-checkbox" style="margin-top:8px">
-          <input type="checkbox" name="poll_enabled" value="1" id="pe-poll-toggle" <%= _poll ? 'checked' : '' %> <%= _pollLocked ? 'disabled' : '' %>>
-          <span>📊 <%= t('pedit.poll_label') %></span>
-        </label>
-        <style>
-          .pe-poll { margin-top: 6px; }
-          .pe-poll-locked { font-size: 12px; opacity: .7; margin: 0 0 6px; }
-          .pe-poll-row { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
-          .pe-poll-opt { flex: 1; min-width: 0; box-sizing: border-box; font-size: 13px; padding: 7px 9px; border-radius: 7px; border: 1px solid var(--rule, rgba(128,128,128,.35)); background: transparent; color: inherit; }
-          .pe-poll-opt:focus { outline: none; border-color: var(--accent); }
-          .pe-poll-del { flex: 0 0 auto; width: 30px; height: 32px; display: inline-flex; align-items: center; justify-content: center; font-size: 18px; line-height: 1; border: 1px solid var(--rule, rgba(128,128,128,.35)); border-radius: 7px; background: transparent; color: var(--ink-muted, #999); cursor: pointer; transition: color .12s, border-color .12s; }
-          .pe-poll-del:hover { border-color: #c0392b; color: #c0392b; }
-          .pe-poll-del[hidden] { display: none; }
-          .pe-poll-add { display: block; width: fit-content; font-size: 12.5px; padding: 6px 12px; border-radius: 7px; border: 1px dashed var(--rule, rgba(128,128,128,.45)); background: transparent; color: inherit; cursor: pointer; margin: 0 0 10px; transition: color .12s, border-color .12s; }
-          .pe-poll-add:hover { border-color: var(--accent); color: var(--accent); }
-          .pe-poll-add:disabled { opacity: .4; cursor: default; border-style: dashed; }
-          .pe-poll-durlabel { display: block; font-size: 12.5px; opacity: .8; margin: 8px 0 4px; }
-          .pe-poll-dur { padding: 7px 9px; border-radius: 7px; border: 1px solid var(--rule, rgba(128,128,128,.35)); background: transparent; color: inherit; font-size: 13px; }
-        </style>
-        <%# NB: keep the condition INSIDE the quoted attribute — escaped output (equals-tag)
-            turns a whole emitted style attribute into entity soup (fields showed always). %>
-        <div id="pe-poll-fields" class="pe-poll" style="<%= _poll ? '' : 'display:none' %>">
-          <% if (_pollLocked) { %><p class="pe-poll-locked"><%= t('pedit.poll_locked') %></p><% } %>
-          <div id="pe-poll-opts" data-ph="<%= t('pedit.poll_option_ph') %>" data-del="<%= t('pedit.poll_remove') %>">
-            <% _pollOpts.forEach(function (o) { %>
-              <div class="pe-poll-row">
-                <input type="text" name="poll_option" class="pe-poll-opt" maxlength="100" value="<%= (o && o.name) || '' %>" placeholder="<%= t('pedit.poll_option_ph') %>" <%= _pollLocked ? 'disabled' : '' %>>
-                <% if (!_pollLocked) { %><button type="button" class="pe-poll-del" aria-label="<%= t('pedit.poll_remove') %>" title="<%= t('pedit.poll_remove') %>">&times;</button><% } %>
-              </div>
-            <% }); %>
-          </div>
-          <% if (!_pollLocked) { %><button type="button" id="pe-poll-add" class="pe-poll-add">+ <%= t('pedit.poll_add') %></button><% } %>
-          <label class="pe-checkbox">
-            <input type="checkbox" name="poll_multiple" value="1" <%= (_poll && _poll.multiple) ? 'checked' : '' %> <%= _pollLocked ? 'disabled' : '' %>>
-            <span><%= t('pedit.poll_multiple') %></span>
-          </label>
-          <label class="pe-poll-durlabel"><%= t('pedit.poll_duration') %></label>
-          <select name="poll_duration" class="pe-poll-dur" <%= _pollLocked ? 'disabled' : '' %>>
-            <% [['300','5m'],['1800','30m'],['3600','1h'],['21600','6h'],['43200','12h'],['86400','1d'],['259200','3d'],['604800','7d']].forEach(function (d) { %>
-              <option value="<%= d[0] %>" <%= d[0] === '86400' ? 'selected' : '' %>><%= t('pedit.poll_dur_' + d[1]) %></option>
-            <% }); %>
-          </select>
-        </div>
-        
-        <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
-          <label class="pe-checkbox">
-            <input type="checkbox" name="fan_only" value="1" <%= post.fan_only ? 'checked' : '' %>>
-            <span>🔒 <%= t('pedit.fan_only_label') %></span>
-          </label>
-          <label class="pe-checkbox" style="margin-top:8px">
-            <input type="checkbox" name="paid" value="1" id="pe-paid" <%= post.paid ? 'checked' : '' %>>
-            <span>💶 Betaalde post (supporters ontgrendelen met Patreon)</span>
-          </label>
-          <div id="pe-paid-price" style="margin:6px 0 0 26px;<%= post.paid ? '' : 'display:none' %>">
-            <label style="font-size:.85rem;opacity:.8">Vereist steunbedrag (euro, leeg = standaard)
-              <input type="text" name="paid_min_eur" inputmode="decimal" style="width:100px"
-                     value="<%= post.paid_min_cents ? (post.paid_min_cents/100).toFixed(2) : '' %>">
-            </label>
-          </div>
-          
-          <% var _scheduled = !!(post.publish_at && (post.status === 'scheduled' || Date.parse(String(post.publish_at).replace(' ', 'T')) > Date.now())); %>
-          <label class="pe-checkbox" style="margin-top:8px">
-            <input type="checkbox" name="schedule_enabled" value="1" id="pe-sched-toggle" <%= _scheduled ? 'checked' : '' %>>
-            <span>📅 <%= t('pedit.schedule_label') %></span>
-          </label>
-          <div id="pe-sched-fields" style="margin-top:6px;<%= _scheduled ? '' : 'display:none' %>">
-            <label style="display:block;font-size:12.5px;opacity:.8;margin-bottom:4px"><%= t('pedit.publish_at_label') %></label>
-            <input type="datetime-local" id="pe-publish-at" name="publish_at" <%= _scheduled ? '' : 'disabled' %> data-iso="<%= post.publish_at || '' %>" value="" style="padding:8px 10px;border-radius:8px;border:1px solid var(--rule,rgba(128,128,128,.4));background:transparent;color:inherit">
-            <% if (post.status === 'scheduled' && post.publish_at) { %><div style="font-size:12px;opacity:.7;margin-top:4px"><span id="pe-sched-when" data-iso="<%= post.publish_at %>" data-label="<%= t('pedit.scheduled_prefix') %>">⏳ <%= t('pedit.scheduled_for', { d: post.publish_at }) %></span></div><% } %>
-            <div style="font-size:11.5px;opacity:.65;margin-top:4px"><%= t('pedit.schedule_hint') %></div>
-          </div>
-          
-        <% } %>
       </div>
     </section>
@@ -368,10 +183,10 @@
     <%# ── ACTIONS (sticky at bottom) ──────────────────────────── %>
     <div class="pe-actions">
-      <a href="<%= _base %><%= isNew ? '/' : '/' + post.slug %>" class="pe-btn"><%= t('pedit.cancel') %></a>
+      <a href="<%= isNew ? '/' : '/' + post.slug %>" class="pe-btn">Annuleren</a>
       <div class="pe-actions-spacer"></div>
       <% if (!isNew && post.status !== 'published') { %>
-        <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 <%= t('pedit.publish') %></button>
+        <button type="submit" name="action" value="publish" class="pe-btn pe-btn-success">📤 Publish</button>
       <% } %>
-      <button type="submit" class="pe-btn pe-btn-primary">💾 <%= t('pedit.save') %></button>
+      <button type="submit" class="pe-btn pe-btn-primary">💾 Opslaan</button>
     </div>
   </form>
@@ -526,86 +341,4 @@
 }
 
-/* ─── Post type: segmented control + type-aware panels ──────────── */
-.pe-typeseg {
-  display: grid;
-  grid-template-columns: repeat(4, 1fr);
-  gap: 0.4rem;
-}
-.pe-typeseg-btn {
-  display: flex; flex-direction: column; align-items: center; gap: 0.25rem;
-  padding: 0.7rem 0.4rem;
-  border: 1px solid var(--rule);
-  border-radius: 9px;
-  background: var(--paper-2);
-  color: var(--ink-soft);
-  font-size: 0.85rem; font-weight: 600;
-  cursor: pointer;
-  transition: border-color 120ms, background 120ms, color 120ms;
-}
-.pe-typeseg-btn:hover { border-color: var(--accent); color: var(--ink); }
-.pe-typeseg-btn .pe-typeseg-ic { font-size: 1.3rem; line-height: 1; }
-.pe-typeseg-btn.is-active {
-  border-color: var(--accent);
-  background: color-mix(in srgb, var(--accent) 14%, var(--paper-2));
-  color: var(--ink);
-}
-.pe-typeseg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
-@media (max-width: 480px) {
-  .pe-typeseg-btn { font-size: 0.78rem; padding: 0.6rem 0.25rem; }
-  .pe-typeseg-btn .pe-typeseg-ic { font-size: 1.15rem; }
-}
-
-.pe-type-panel { margin-top: 0.2rem; }
-
-/* Audio drop/upload zone */
-.pe-audio-up {
-  display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
-  text-align: center;
-  padding: 1.4rem 1rem;
-  border: 2px dashed var(--rule);
-  border-radius: 10px;
-  background: var(--paper-2);
-  color: var(--ink-soft);
-  cursor: pointer;
-  transition: border-color 120ms, background 120ms;
-}
-.pe-audio-up:hover,
-.pe-audio-up:focus-visible { border-color: var(--accent); outline: none; }
-.pe-audio-up.is-drag {
-  border-color: var(--accent);
-  background: color-mix(in srgb, var(--accent) 12%, var(--paper-2));
-}
-.pe-audio-up .pe-audio-up-ic { font-size: 1.7rem; line-height: 1; }
-.pe-audio-up strong { color: var(--ink); font-size: 0.95rem; }
-.pe-audio-up small { font-size: 0.78rem; color: var(--ink-muted, var(--ink-soft)); max-width: 42ch; }
-
-.pe-audio-list { list-style: none; margin: 0.7rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
-.pe-audio-item {
-  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
-  padding: 0.5rem 0.7rem;
-  border: 1px solid var(--rule);
-  border-radius: 7px;
-  background: var(--paper-2);
-  font-size: 0.85rem;
-}
-.pe-audio-item-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.pe-audio-item-state { flex: none; font-size: 0.8rem; color: var(--ink-soft); }
-.pe-audio-item.is-done { border-color: color-mix(in srgb, var(--accent) 50%, var(--rule)); }
-.pe-audio-item.is-done .pe-audio-item-state { color: var(--accent); }
-.pe-audio-item.is-fail .pe-audio-item-state { color: #d9534f; }
-
-/* Video URL row */
-.pe-video-row { display: flex; gap: 0.5rem; }
-.pe-video-row input { flex: 1 1 auto; min-width: 0; }
-.pe-video-row input {
-  box-sizing: border-box; padding: 0.6rem 0.75rem;
-  border: 1px solid var(--rule); border-radius: 6px;
-  background: var(--paper-2); color: var(--ink); font-size: 0.95rem;
-}
-.pe-video-row input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
-.pe-video-row .pe-btn { flex: none; }
-
-.pe-foto-hint { margin: 0; font-size: 0.88rem; color: var(--ink-soft); }
-
 /* ─── Editor frame (WYSIWYG: top toolbar, contenteditable body, status bar) ─── */
 .pe-editor-frame {
@@ -633,6 +366,4 @@
   width: 32px; height: 32px;
   padding: 0;
-  touch-action: manipulation; /* snappy taps on mobile, no double-tap zoom */
-  -webkit-user-select: none; user-select: none;
   background: transparent;
   border: 1px solid transparent;
@@ -645,21 +376,14 @@
   -webkit-tap-highlight-color: transparent;
 }
-/* Hover only on real hover-capable devices — on touch :hover otherwise "sticks"
-   after a tap, making the active/inactive state unreadable. */
-@media (hover: hover) {
-  .pe-toolbar button:hover {
-    background: var(--paper-2);
-    color: var(--accent);
-  }
+.pe-toolbar button:hover {
+  background: var(--paper-2);
+  color: var(--accent);
 }
 .pe-toolbar button:active { transform: scale(.94); }
-/* Active formatting = unmistakably filled with the accent colour. On mobile
-   it's immediately clear when e.g. bold is ON (tap again = off). */
 .pe-toolbar button.is-active {
-  background: var(--accent);
-  color: #fff;
-  border-color: var(--accent);
-}
-.pe-toolbar button.is-active svg { color: #fff; }
+  background: color-mix(in srgb, var(--accent) 14%, var(--paper-2));
+  color: var(--accent);
+  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
+}
 .pe-toolbar button svg { width: 16px; height: 16px; }
 .pe-toolbar button.pe-tb-text { font-family: var(--font-display, serif); font-weight: 700; }
@@ -671,42 +395,4 @@
 }
 .pe-toolbar-spacer { flex: 1; }
-
-/* ─── Full-screen writing mode ─── */
-.fs-icon-compress { display: none; }
-.pe-editor-frame.pe-fs .fs-icon-expand { display: none; }
-.pe-editor-frame.pe-fs .fs-icon-compress { display: inline; }
-.pe-editor-frame.pe-fs {
-  position: fixed; inset: 0; z-index: 1000;
-  margin: 0; border-top: 0;
-  height: 100dvh;
-  background: var(--paper);
-  overflow: hidden;   /* only the text field scrolls, not the frame itself */
-}
-/* In fullscreen the writing field fills the remaining space and scrolls itself
-   (max-height: none overrides the mobile box limit below). */
-.pe-editor-frame.pe-fs .pe-editor {
-  flex: 1 1 auto; min-height: 0; height: auto; max-height: none;
-}
-/* iOS: in fullscreen the toolbar sits at the very top → push it below the
-   camera / Dynamic Island with the top safe-area inset. */
-.pe-editor-frame.pe-fs .pe-toolbar { padding-top: calc(.4rem + env(safe-area-inset-top, 0px)); }
-/* NB: deliberately NO overflow:hidden on html/body in fullscreen — that could get
-   stuck (e.g. leaving fullscreen via navigation) and would block scrolling on the
-   whole page. The fullscreen frame (position:fixed, inset:0) already covers the page,
-   and on touch scrollbars are hidden → no double bar needed. */
-
-/* "Done" button: only visible in fullscreen (left side of toolbar), clear
-   accent-pill instead of a square icon. */
-/* Hide rule more specific than ".pe-toolbar button" (otherwise that wins and the
-   Done button also shows inline as a small button). Only shown in fullscreen → large. */
-.pe-toolbar button.pe-fs-done { display: none; }
-.pe-editor-frame.pe-fs .pe-fs-done {
-  display: inline-flex; align-items: center; gap: .35rem;
-  width: auto !important; height: auto !important; min-height: 40px;
-  padding: .55rem 1.3rem !important; margin-right: .5rem;
-  background: var(--accent); color: #fff;
-  font-weight: 700; font-size: 1.05rem; border-radius: 999px;
-}
-.pe-editor-frame.pe-fs .pe-fs-done:hover { background: var(--accent); color: #fff; }
 
 /* Editor body — looks like prose, behaves like a textarea */
@@ -726,40 +412,14 @@
 }
 .pe-editor:focus { outline: none; }
-/* Inline (non-fullscreen) the writing field simply grows with the content — no
-   internal scroll-box (scroll-within-scroll is confusing). On mobile/tablet typing
-   always goes fullscreen (see JS), where the field fills the page and is the sole
-   scroller. */
-.pe-editor-frame:not(.pe-fs) .pe-editor { overflow: visible; }
-
-/* Touch: the inline content field is not a text field but a tap target → fullscreen
-   editing. A "✎ Tap to edit" pill sticks to the bottom of the container,
-   so the hint is always visible without sitting in the middle of the text. */
-.pe-editor.pe-tap-to-edit { cursor: pointer; }
-.pe-edit-hint { display: none; }
-@media (pointer: coarse) {
-  .pe-editor-frame:not(.pe-fs) .pe-edit-hint {
-    display: block;
-    position: sticky;
-    bottom: 4.5rem;   /* above the sticky Save/Cancel bar */
-    width: max-content;
-    max-width: calc(100% - 2rem);
-    margin: .4rem auto;
-    background: var(--accent); color: #fff;
-    font-size: .9rem; font-weight: 700; padding: .5rem 1.1rem; border-radius: 999px;
-    box-shadow: 0 4px 14px rgba(0,0,0,.35);
-    pointer-events: none; text-align: center; white-space: nowrap;
-  }
-  /* Not relevant on touch (drag/select belongs to inline editing on desktop). */
-  .pe-content-hint { display: none; }
-  /* Inline (non-fullscreen) on touch: keep it simple — you don't edit here anyway,
-     so no formatting toolbar and no border. Just the content preview + the
-     "tap to edit" pill. The toolbar only appears in fullscreen. */
-  .pe-editor-frame:not(.pe-fs) .pe-toolbar { display: none; }
-  .pe-editor-frame:not(.pe-fs) { border-top: 0; }
-  .pe-editor-frame:not(.pe-fs) .pe-editor { min-height: 8rem; }
-}
 .pe-editor.is-dragover {
   outline: 2px dashed var(--accent);
   outline-offset: -10px;
+}
+.pe-editor[data-placeholder]:empty::before,
+.pe-editor[data-placeholder]:has(br:only-child)::before {
+  content: attr(data-placeholder);
+  color: var(--ink-muted, #999);
+  pointer-events: none;
+  opacity: .55;
 }
 /* Inline prose styles inside the editor — match the public post styling so
@@ -1118,19 +778,29 @@
 }
 
-/* Pin: checkbox + ▲▼-stepper with description (instead of a raw rank number). */
-.pe-pin { display: flex; flex-direction: column; gap: 0.45rem; }
-.pe-pin-pos { display: flex; align-items: center; gap: 0.55rem; padding-left: 1.65rem; }
-.pe-pin-pos[hidden] { display: none; }
-.pe-pin-steps { display: inline-flex; border: 1px solid var(--rule); border-radius: 6px; overflow: hidden; }
-.pe-pin-btn {
-  width: 30px; height: 28px; padding: 0;
-  border: none; background: var(--paper-2); color: var(--ink);
-  cursor: pointer; font-size: 0.68rem; line-height: 1;
-  display: inline-flex; align-items: center; justify-content: center;
-}
-.pe-pin-btn + .pe-pin-btn { border-left: 1px solid var(--rule); }
-.pe-pin-btn:hover { background: color-mix(in srgb, var(--accent) 14%, var(--paper-2)); color: var(--accent); }
-.pe-pin-btn:disabled { opacity: 0.35; cursor: default; }
-.pe-pin-label { font-size: 0.85rem; color: var(--accent); font-weight: 600; }
+/* Pinned rank: replaces the old boolean checkbox with a number input.
+   Stack the label / input / hint vertically — small footprint. */
+.pe-pinned-rank {
+  display: flex; flex-direction: column; gap: 0.25rem;
+  font-size: 0.95rem;
+  color: var(--ink);
+}
+.pe-pinned-rank input[type="number"] {
+  width: 80px;
+  padding: 0.4rem 0.6rem; min-height: 36px;
+  border: 1px solid var(--rule); border-radius: 6px;
+  background: var(--paper-2); color: var(--ink);
+  font-family: var(--font-ui, system-ui), sans-serif;
+  font-size: 0.95rem;
+  font-variant-numeric: tabular-nums;
+  -webkit-appearance: none; appearance: textfield;
+}
+.pe-pinned-rank input[type="number"]:focus {
+  outline: 2px solid var(--accent); outline-offset: -1px;
+  border-color: var(--accent);
+}
+.pe-pinned-rank small {
+  font-size: 0.78rem;
+  color: var(--ink-muted, var(--ink-soft));
+}
 
 /* ─── Sticky action footer ──────────────────────────────────────── */
@@ -1141,5 +811,5 @@
   gap: 0.5rem;
   padding: 0.85rem 1rem;
-  margin: 0.5rem -1rem 0;   /* extend to viewport edges on a narrow container */
+  margin: 0.5rem -1rem 0;   /* extend to viewport edges on tight container */
   background: color-mix(in srgb, var(--paper) 94%, transparent);
   -webkit-backdrop-filter: blur(8px);
@@ -1150,48 +820,507 @@
 .pe-actions-spacer { flex: 1; }
 
-/* The editor is a focus screen: hide the mobile site tab bar (Home/Search/…)
-   so two bars don't stack at the bottom (Save bar + tab bar). The
-   Save/Cancel bar then becomes the only bottom bar → plain bottom:0,
-   no tab offset needed. On desktop the tab bar is already hidden. */
-body:has(.post-edit-page) .bottom-tab { display: none; }
-/* Audio player (if playing) no longer lifted 56px for the now-hidden
-   tab bar, otherwise it would float above the action bar. */
-body:has(.post-edit-page) .audio-player { bottom: 0; }
-
-/* ── Image editor (rotate / crop / mirror) ── */
-.imed-backdrop {
-  position: fixed; inset: 0; z-index: 9999;
-  display: flex; align-items: center; justify-content: center;
-  background: rgba(0,0,0,.62); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
-  padding: 14px;
-}
-.imed-modal {
-  display: flex; flex-direction: column; gap: 12px;
-  width: 100%; max-width: 640px; max-height: 92vh;
-  background: var(--paper, #fff); color: var(--ink, #111);
-  border: 1px solid var(--rule, rgba(128,128,128,.3)); border-radius: 14px;
-  padding: 14px; box-sizing: border-box;
-}
-.imed-stage {
-  height: 58vh; flex: 0 0 auto;
-  background: var(--paper-2, rgba(128,128,128,.08)); border-radius: 10px; overflow: hidden;
-}
-.imed-stage img { display: block; max-width: 100%; }
-/* Cropper's container must fill the full stage height — otherwise it collapses
-   in a flex column without an explicit height → empty edit window (no image). */
-.imed-stage .cropper-container { width: 100% !important; height: 100% !important; }
-.imed-tools {
-  display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;
-}
-.imed-tools button {
-  width: 42px; height: 42px; font-size: 18px; line-height: 1;
-  border: 1px solid var(--rule, rgba(128,128,128,.4)); border-radius: 10px;
-  background: var(--paper-2, transparent); color: inherit; cursor: pointer;
-}
-.imed-tools button:hover { border-color: var(--accent); }
-.imed-actions { display: flex; gap: 8px; justify-content: flex-end; }
+/* On a body that already has a fixed bottom-tab nav (mobile), nudge the
+   sticky footer up so it doesn't sit under it. The bottom-tab is ~64px tall. */
+body.has-bottom-tab .pe-actions {
+  bottom: 64px;
+}
 </style>
 
-
+<script>
+(function() {
+
+  // ── Cover upload ────────────────────────────────────────────────
+  const coverField   = document.getElementById('cover-upload-field');
+  const coverTrigger = document.getElementById('cover-upload-trigger');
+  const coverUrl     = document.getElementById('cover-url-field');
+  const coverStatus  = document.getElementById('cover-upload-status');
+  const coverWrap    = document.getElementById('cover-preview-wrap');
+  const coverImg     = document.getElementById('cover-preview-img');
+
+  async function uploadImage(file) {
+    const fd = new FormData();
+    fd.append('image', file);
+    const res = await fetch('/posts/upload-image', { method: 'POST', body: fd });
+    if (!res.ok) {
+      const j = await res.json().catch(() => ({}));
+      throw new Error(j.error || ('Upload failed (' + res.status + ')'));
+    }
+    return await res.json();   // {url, size, mime}
+  }
+
+  function showCoverPreview(url) {
+    if (!coverWrap || !coverImg) return;
+    if (url) {
+      coverImg.src = url;
+      coverImg.hidden = false;
+      coverWrap.removeAttribute('data-empty');
+      const emptyIcon = coverWrap.querySelector('.pe-cover-empty');
+      if (emptyIcon) emptyIcon.remove();
+    } else {
+      coverImg.hidden = true;
+      coverImg.src = '';
+      coverWrap.setAttribute('data-empty', '');
+      if (!coverWrap.querySelector('.pe-cover-empty')) {
+        const span = document.createElement('span');
+        span.className = 'pe-cover-empty';
+        span.textContent = '🖼';
+        coverWrap.appendChild(span);
+      }
+    }
+  }
+
+  if (coverTrigger && coverField) {
+    coverTrigger.addEventListener('click', () => coverField.click());
+  }
+  if (coverField) {
+    coverField.addEventListener('change', async () => {
+      if (!coverField.files[0]) return;
+      coverStatus.classList.remove('is-error');
+      coverStatus.textContent = 'Uploaden…';
+      try {
+        const j = await uploadImage(coverField.files[0]);
+        coverUrl.value = j.url;
+        showCoverPreview(j.url);
+        coverStatus.textContent = 'Geüpload ✓';
+        setTimeout(() => { coverStatus.textContent = ''; }, 2000);
+      } catch (e) {
+        coverStatus.classList.add('is-error');
+        coverStatus.textContent = 'Mislukt: ' + e.message;
+      }
+    });
+  }
+  // Live-update preview when user pastes a URL manually
+  if (coverUrl) {
+    coverUrl.addEventListener('input', () => {
+      const v = coverUrl.value.trim();
+      if (v) showCoverPreview(v); else showCoverPreview('');
+    });
+  }
+
+  // ── WYSIWYG editor (P58) ────────────────────────────────────────
+  // Architecture:
+  //   - Visible <div contenteditable> (`#content-editor`) is what the user
+  //     types in; it shows real HTML (formatted, not raw markup).
+  //   - Hidden <input name="content"> (`#content-hidden`) is what submits.
+  //     On submit we serialize the editor's HTML into it, with shortcode
+  //     chips reduced back to their [[track:UUID]]/[[album:Name]]/[[playlist:slug]] text.
+  //   - Initial content comes from a <script type="application/json"> tag
+  //     to avoid HTML-escape-into-DOM issues; we set innerHTML once on load
+  //     and walk text nodes to render shortcode tokens as chips.
+  const contentField  = document.getElementById('content-upload-field');
+  const contentBtn    = document.getElementById('insert-image-btn');
+  const contentStatus = document.getElementById('content-upload-status');
+  const editor        = document.getElementById('content-editor');
+  const hiddenField   = document.getElementById('content-hidden');
+  const charCountEl   = document.getElementById('char-count');
+  const initialEl     = document.getElementById('initial-content');
+  const toolbar       = document.getElementById('pe-toolbar');
+  const form          = editor && editor.closest('form');
+
+  if (!editor) return;
+
+  // ── Shortcode chip rendering / serialization ────────────────────
+  // Pattern matches [[track:UUID]] / [[album:any text]] / [[playlist:slug]]
+  // — but we DON'T want to chipify text the user is mid-typing inside an
+  // HTML attribute; since chipify only walks text nodes (never attribute
+  // values) that's already safe.
+  const SC_RE = /\[\[(track|album|playlist):([^\]]+)\]\]/g;
+
+  const SC_ICONS = {
+    track:    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="6 4 20 12 6 20 6 4"/></svg>',
+    album:    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>',
+    playlist: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="15" y2="18"/><polygon points="3 5 3 13 9 9"/></svg>',
+  };
+
+  function chipLabel(kind, value) {
+    if (kind === 'track') {
+      // UUIDs are noisy — show a 6-char prefix for visual hint
+      const v = String(value || '');
+      return 'Track ' + (v.length > 8 ? v.slice(0, 6) + '…' : v);
+    }
+    if (kind === 'album')    return 'Album: ' + value;
+    if (kind === 'playlist') return 'Playlist: ' + value;
+    return value;
+  }
+
+  function makeChip(kind, value) {
+    const span = document.createElement('span');
+    span.className = 'sc-chip';
+    span.contentEditable = 'false';
+    span.setAttribute('data-sc', kind + ':' + value);
+    span.innerHTML =
+      '<span class="sc-chip-icon" aria-hidden="true">' + (SC_ICONS[kind] || '') + '</span>' +
+      '<span class="sc-chip-label"></span>';
+    span.querySelector('.sc-chip-label').textContent = chipLabel(kind, value);
+    return span;
+  }
+
+  // Walk text nodes inside `root` and replace [[type:value]] tokens with chips.
+  function chipifyShortcodes(root) {
+    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
+    const targets = [];
+    while (walker.nextNode()) {
+      const n = walker.currentNode;
+      // Skip text inside existing chips (their .sc-chip-label is set via .textContent so the [[...]] text never appears)
+      if (n.parentElement && n.parentElement.closest('.sc-chip')) continue;
+      if (SC_RE.test(n.nodeValue)) targets.push(n);
+      SC_RE.lastIndex = 0;
+    }
+    for (const node of targets) {
+      const txt = node.nodeValue;
+      const frag = document.createDocumentFragment();
+      let last = 0;
+      let m;
+      SC_RE.lastIndex = 0;
+      while ((m = SC_RE.exec(txt)) !== null) {
+        if (m.index > last) frag.appendChild(document.createTextNode(txt.slice(last, m.index)));
+        frag.appendChild(makeChip(m[1], m[2].trim()));
+        last = m.index + m[0].length;
+      }
+      if (last < txt.length) frag.appendChild(document.createTextNode(txt.slice(last)));
+      node.parentNode.replaceChild(frag, node);
+    }
+  }
+
+  // Inverse of chipify: clone the editor, replace every chip with its text.
+  function serializeChips(rootClone) {
+    const chips = rootClone.querySelectorAll('.sc-chip[data-sc]');
+    for (const c of chips) {
+      const txt = '[[' + c.getAttribute('data-sc') + ']]';
+      c.replaceWith(document.createTextNode(txt));
+    }
+  }
+
+  // ── Boot: load initial content as HTML, then render shortcodes as chips
+  try {
+    const initial = JSON.parse(initialEl.textContent || '""');
+    editor.innerHTML = initial || '';
+    chipifyShortcodes(editor);
+  } catch (e) {
+    console.error('[editor] could not parse initial content', e);
+    editor.innerHTML = '';
+  }
+
+  // ── Char counter
+  function updateCharCount() {
+    const text = (editor.innerText || '').replace(/\s+/g, ' ').trim();
+    if (charCountEl) charCountEl.textContent = String(text.length);
+  }
+  updateCharCount();
+  editor.addEventListener('input', updateCharCount);
+
+  // ── Toolbar wiring
+  function execCmd(cmd, arg) {
+    editor.focus();
+    document.execCommand(cmd, false, arg);
+    updateToolbarState();
+    updateCharCount();
+  }
+  function wrapCode() {
+    const sel = window.getSelection();
+    if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
+    const range = sel.getRangeAt(0);
+    const code = document.createElement('code');
+    code.textContent = sel.toString();
+    range.deleteContents();
+    range.insertNode(code);
+    // Move caret after the new node
+    range.setStartAfter(code);
+    range.collapse(true);
+    sel.removeAllRanges();
+    sel.addRange(range);
+    editor.focus();
+  }
+  function linkPrompt() {
+    const url = window.prompt('Link URL (https://… of /pad)');
+    if (!url) return;
+    execCmd('createLink', url);
+  }
+
+  if (toolbar) {
+    toolbar.addEventListener('click', (e) => {
+      const btn = e.target.closest('button[data-cmd]');
+      if (!btn) return;
+      e.preventDefault();
+      const cmd = btn.dataset.cmd;
+      const arg = btn.dataset.arg || null;
+      if (cmd === 'link-prompt') linkPrompt();
+      else if (cmd === 'code-wrap') wrapCode();
+      else execCmd(cmd, arg);
+    });
+  }
+
+  // Reflect bold/italic/list state on the toolbar buttons
+  function updateToolbarState() {
+    if (!toolbar) return;
+    const cmds = ['bold', 'italic', 'underline', 'insertUnorderedList', 'insertOrderedList'];
+    for (const cmd of cmds) {
+      const btn = toolbar.querySelector('button[data-cmd="' + cmd + '"]');
+      if (!btn) continue;
+      try { btn.classList.toggle('is-active', document.queryCommandState(cmd)); } catch(_) {}
+    }
+  }
+  document.addEventListener('selectionchange', () => {
+    if (document.activeElement === editor) updateToolbarState();
+  });
+
+  // Keyboard shortcuts: Ctrl/Cmd + B/I/U/K
+  editor.addEventListener('keydown', (e) => {
+    const mod = e.ctrlKey || e.metaKey;
+    if (!mod) return;
+    const k = e.key.toLowerCase();
+    if (k === 'b') { e.preventDefault(); execCmd('bold'); }
+    else if (k === 'i') { e.preventDefault(); execCmd('italic'); }
+    else if (k === 'u') { e.preventDefault(); execCmd('underline'); }
+    else if (k === 'k') { e.preventDefault(); linkPrompt(); }
+  });
+
+  // Paste: keep it simple — strip formatting unless user wants it. Default
+  // execCommand 'paste' includes Word/Google-Docs garbage. We accept inline
+  // styles from clipboard only when shift is held — otherwise plain text.
+  editor.addEventListener('paste', (e) => {
+    if (e.shiftKey) return; // user wants formatted paste
+    const text = (e.clipboardData || window.clipboardData).getData('text/plain');
+    if (text == null) return;
+    e.preventDefault();
+    document.execCommand('insertText', false, text);
+  });
+
+  // ── Image upload (button + drag-drop into the editor)
+  async function uploadAndInsertImage(file) {
+    contentStatus.classList.remove('is-error');
+    contentStatus.textContent = 'Uploaden…';
+    try {
+      const j = await uploadImage(file);
+      const img = '<img src="' + j.url + '" alt="">';
+      editor.focus();
+      document.execCommand('insertHTML', false, img);
+      contentStatus.textContent = 'Ingevoegd ✓';
+      setTimeout(() => { contentStatus.textContent = ''; }, 2000);
+      updateCharCount();
+    } catch (e) {
+      contentStatus.classList.add('is-error');
+      contentStatus.textContent = 'Mislukt: ' + e.message;
+    }
+  }
+
+  if (contentBtn && contentField) {
+    contentBtn.addEventListener('click', () => contentField.click());
+    contentField.addEventListener('change', () => {
+      if (contentField.files[0]) uploadAndInsertImage(contentField.files[0]);
+      contentField.value = '';
+    });
+
+    editor.addEventListener('dragover', (e) => {
+      if (e.dataTransfer && e.dataTransfer.types.includes('Files')) {
+        e.preventDefault();
+        editor.classList.add('is-dragover');
+      }
+    });
+    editor.addEventListener('dragleave', () => editor.classList.remove('is-dragover'));
+    editor.addEventListener('drop', async (e) => {
+      editor.classList.remove('is-dragover');
+      const files = e.dataTransfer && e.dataTransfer.files;
+      if (!files || !files.length) return;
+      e.preventDefault();
+      for (const f of files) {
+        if (f.type.startsWith('image/')) await uploadAndInsertImage(f);
+      }
+    });
+  }
+
+  // ── Insert chip helpers (track / playlist)
+  function insertChip(kind, value) {
+    editor.focus();
+    const chip = makeChip(kind, value);
+    // Insert at caret using the Selection API (execCommand insertNode)
+    const sel = window.getSelection();
+    if (sel && sel.rangeCount > 0) {
+      const range = sel.getRangeAt(0);
+      range.deleteContents();
+      range.insertNode(chip);
+      // Insert a trailing space so the user can keep typing after the chip
+      const space = document.createTextNode('\u00A0');
+      chip.after(space);
+      range.setStartAfter(space);
+      range.collapse(true);
+      sel.removeAllRanges();
+      sel.addRange(range);
+    } else {
+      editor.appendChild(chip);
+      editor.appendChild(document.createTextNode('\u00A0'));
+    }
+    updateCharCount();
+  }
+
+  // ── Track insert: opens the track-picker modal (P59)
+  const trackBtn = document.getElementById('insert-track-btn');
+  const trackPicker = document.getElementById('track-picker');
+  if (trackBtn && trackPicker) {
+    const tpList   = document.getElementById('tp-list');
+    const tpEmpty  = document.getElementById('tp-empty');
+    const tpSearch = document.getElementById('tp-search');
+    let tpCache = null;       // cached tracks list (fetched once per page load)
+    let tpLastFocus = null;   // element to restore focus to on close
+
+    const SVG_NOTE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 17V5l12-2v12"/><circle cx="6" cy="17" r="3"/><circle cx="18" cy="15" r="3"/></svg>';
+
+    function fmtDur(sec) {
+      sec = Math.max(0, Math.floor(sec || 0));
+      const m = Math.floor(sec / 60), s = sec % 60;
+      return m + ':' + String(s).padStart(2, '0');
+    }
+    function escAttr(s) {
+      return String(s == null ? '' : s).replace(/[&<>"']/g, c => ({
+        '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'
+      }[c]));
+    }
+
+    function renderList(filter) {
+      if (!Array.isArray(tpCache)) return;
+      const q = (filter || '').trim().toLowerCase();
+      const filtered = q
+        ? tpCache.filter(t =>
+            (t.title  || '').toLowerCase().includes(q) ||
+            (t.artist || '').toLowerCase().includes(q))
+        : tpCache;
+
+      if (!filtered.length) {
+        tpList.innerHTML = '';
+        tpEmpty.textContent = q ? 'Geen tracks gevonden voor "' + q + '"' : 'Nog geen tracks. Upload via Beheer → Audio.';
+        tpList.appendChild(tpEmpty);
+        return;
+      }
+
+      tpList.innerHTML = filtered.map(t => {
+        const cov = t.cover
+          ? '<span class="tp-cover" style="background-image:url(\'' + escAttr(t.cover) + '\')"></span>'
+          : '<span class="tp-cover tp-cover-empty">' + SVG_NOTE + '</span>';
+        const dis = t.playable ? '' : ' aria-disabled="true"';
+        const sub = t.artist ? '<span class="tp-row-artist">' + escAttr(t.artist) + '</span>' : '';
+        return (
+          '<button type="button" class="tp-row" role="option" data-track-id="' + escAttr(t.id) + '"' + dis + '>' +
+            cov +
+            '<span class="tp-meta">' +
+              '<span class="tp-row-title">' + escAttr(t.title) + '</span>' +
+              sub +
+            '</span>' +
+            '<span class="tp-duration">' + fmtDur(t.duration) + '</span>' +
+          '</button>'
+        );
+      }).join('');
+    }
+
+    async function loadTracks() {
+      if (Array.isArray(tpCache)) return tpCache;
+      tpEmpty.textContent = 'Tracks laden…';
+      try {
+        const r = await fetch('/admin/playlists/api/tracks', { credentials: 'same-origin' });
+        const j = await r.json();
+        tpCache = (j && j.ok && Array.isArray(j.tracks)) ? j.tracks : [];
+      } catch (e) {
+        tpCache = [];
+        tpEmpty.textContent = 'Kon tracks niet laden: ' + e.message;
+      }
+      return tpCache;
+    }
+
+    function openPicker() {
+      tpLastFocus = document.activeElement;
+      trackPicker.hidden = false;
+      trackPicker.setAttribute('aria-hidden', 'false');
+      document.body.classList.add('tp-locked');
+      tpSearch.value = '';
+      renderList('');
+      // Defer focus so the open animation doesn't get jumped
+      setTimeout(() => tpSearch.focus(), 30);
+    }
+    function closePicker() {
+      trackPicker.hidden = true;
+      trackPicker.setAttribute('aria-hidden', 'true');
+      document.body.classList.remove('tp-locked');
+      if (tpLastFocus && typeof tpLastFocus.focus === 'function') {
+        try { tpLastFocus.focus(); } catch(_) {}
+      }
+    }
+
+    trackBtn.addEventListener('click', async () => {
+      openPicker();
+      await loadTracks();
+      renderList(tpSearch.value);
+    });
+
+    // Close: backdrop click, [data-tp-close], or Escape
+    trackPicker.addEventListener('click', (e) => {
+      if (e.target.closest('[data-tp-close]')) {
+        closePicker();
+        return;
+      }
+      const row = e.target.closest('.tp-row[data-track-id]');
+      if (row) {
+        if (row.getAttribute('aria-disabled') === 'true') return;
+        const id = row.dataset.trackId;
+        if (id) {
+          insertChip('track', id);
+          closePicker();
+        }
+      }
+    });
+    document.addEventListener('keydown', (e) => {
+      if (!trackPicker.hidden && e.key === 'Escape') {
+        e.preventDefault();
+        closePicker();
+      }
+    });
+
+    // Live filter
+    tpSearch.addEventListener('input', () => renderList(tpSearch.value));
+  }
+
+  // ── Playlist insert (open existing or create new via modal)
+  const playlistBtn = document.getElementById('insert-playlist-btn');
+  if (playlistBtn) {
+    playlistBtn.addEventListener('click', async () => {
+      if (typeof window.openPlaylistEditor !== 'function') {
+        alert('Playlist editor niet geladen');
+        return;
+      }
+      try {
+        const r = await fetch('/admin/playlists/api/list', { credentials: 'same-origin' });
+        const j = await r.json();
+        if (j.ok && Array.isArray(j.playlists) && j.playlists.length > 0) {
+          const choice = prompt(
+            'Bestaande playlists:\n\n' +
+            j.playlists.map((p, i) => `${i + 1}. ${p.title} (${p.track_count} tracks)`).join('\n') +
+            '\n\nKies een nummer om in te voegen, leeg = nieuwe maken:'
+          );
+          if (choice && /^\d+$/.test(choice.trim())) {
+            const idx = parseInt(choice.trim(), 10) - 1;
+            if (idx >= 0 && idx < j.playlists.length) {
+              insertChip('playlist', j.playlists[idx].id);
+              return;
+            }
+          }
+          if (choice === null) return;
+        }
+      } catch (_) { /* fall through to create */ }
+
+      window.openPlaylistEditor({
+        mode: 'create',
+        onSaved: ({ id }) => insertChip('playlist', id),
+      });
+    });
+  }
+
+  // ── Submit: serialize editor contents into the hidden field
+  if (form && hiddenField) {
+    form.addEventListener('submit', () => {
+      const clone = editor.cloneNode(true);
+      serializeChips(clone);
+      hiddenField.value = clone.innerHTML;
+    });
+  }
+})();
+</script>
 
 <%# ── Track picker modal (P59). Mobile-first: full-screen sheet on small
@@ -1202,6 +1331,6 @@
   <div class="tp-sheet" role="dialog" aria-modal="true" aria-labelledby="tp-title">
     <header class="tp-header">
-      <h2 id="tp-title" class="tp-h2"><%= t('pedit.tp_title') %></h2>
-      <button type="button" class="tp-close" data-tp-close aria-label="<%= t('pedit.tp_close') %>">
+      <h2 id="tp-title" class="tp-h2">Track invoegen</h2>
+      <button type="button" class="tp-close" data-tp-close aria-label="Sluiten">
         <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
       </button>
@@ -1211,8 +1340,8 @@
         <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
       </span>
-      <input type="search" class="tp-search" id="tp-search" placeholder="<%= t('pedit.tp_search_placeholder') %>" autocomplete="off" inputmode="search">
+      <input type="search" class="tp-search" id="tp-search" placeholder="Zoek op titel of artiest…" autocomplete="off" inputmode="search">
     </div>
-    <div class="tp-list" id="tp-list" role="listbox" aria-label="<%= t('pedit.tp_list_aria') %>">
-      <div class="tp-empty" id="tp-empty"><%= t('pedit.js_tracks_loading') %></div>
+    <div class="tp-list" id="tp-list" role="listbox" aria-label="Tracks">
+      <div class="tp-empty" id="tp-empty">Tracks laden…</div>
     </div>
   </div>
@@ -1225,3 +1354,2 @@
   <%- include('../partials/playlist-editor', { csrfToken: (typeof csrfToken !== 'undefined' ? csrfToken : '') }) %>
 <% } %>
-<%- include('../partials/page-data', { pageData: { _timezone: (typeof timezone !== 'undefined' ? timezone : ''), apply: t('imed.apply'), audio_up_busy: t('pedit.audio_up_busy'), audio_up_done: t('pedit.audio_up_done'), audio_up_fail: t('pedit.audio_up_fail'), cancel: t('imed.cancel'), chip_album: t('pedit.chip_album'), chip_playlist: t('pedit.chip_playlist'), chip_track: t('pedit.chip_track'), flip_h: t('imed.flip_h'), flip_v: t('imed.flip_v'), js_embed_invalid: t('pedit.js_embed_invalid'), js_embed_prompt: t('pedit.js_embed_prompt'), js_failed: t('pedit.js_failed'), js_inserted: t('pedit.js_inserted'), js_link_prompt: t('pedit.js_link_prompt'), js_no_tracks_found: t('pedit.js_no_tracks_found'), js_no_tracks_yet: t('pedit.js_no_tracks_yet'), js_playlist_choose: t('pedit.js_playlist_choose'), js_playlist_editor_missing: t('pedit.js_playlist_editor_missing'), js_playlist_existing: t('pedit.js_playlist_existing'), js_tracks_load_fail: t('pedit.js_tracks_load_fail'), js_tracks_loading: t('pedit.js_tracks_loading'), js_uploaded: t('pedit.js_uploaded'), js_uploading: t('pedit.js_uploading'), pin_nth_suffix: t('pedit.pin_nth_suffix'), pin_top: t('pedit.pin_top'), reset: t('imed.reset'), rotate_left: t('imed.rotate_left'), rotate_right: t('imed.rotate_right'), tb_done: t('pedit.tb_done'), tb_fullscreen: t('pedit.tb_fullscreen'), title: t('imed.title'), zoom_in: t('imed.zoom_in'), zoom_out: t('imed.zoom_out') } }) %>
