<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Page Builder — Pure HTML</title>
<link rel="stylesheet" href="../../../dist/builder.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=block">
<style>
body { margin: 0; font-family: system-ui, sans-serif; display: grid; grid-template-rows: 48px 1fr; height: 100vh; }
.my-header { display: flex; align-items: center; gap: 8px; padding: 0 16px; background: #f8f9fa; border-bottom: 1px solid #e5e7eb; }
.my-header__title { margin-right: auto; font-weight: 600; font-size: 14px; }
.my-header__btn { padding: 6px 12px; background: #fff; border: 1px solid #d1d5db; border-radius: 4px; font: inherit; cursor: pointer; }
.my-header__btn:hover { background: #f3f4f6; }
.my-builder-host { display: grid; grid-template-columns: 220px 1fr 280px; min-height: 0; }
#MyWidgets, #MySettings { background: #f8f9fa; padding: 12px; overflow: auto; }
#MyCanvas { overflow: auto; }
</style>
</head>
<body>
<header class="my-header">
<span class="my-header__title">My builder · pure HTML</span>
<button type="button" id="myGetHtml" class="my-header__btn">Get HTML</button>
<button type="button" id="myGetJson" class="my-header__btn">Get JSON</button>
</header>
<div class="my-builder-host">
<div id="MyWidgets"></div>
<div id="MyCanvas"></div>
<div id="MySettings"></div>
</div>
<!-- ════════════════════════════════════════════════════════════════════
FOUR NAMED INGREDIENTS for builder.load(), in order.
The PHP version (snippet.php) computes these four with one call to
ThemeRegistry::resolveBundle('default', 'master/sample/email/Minimal').
This pure-HTML sibling inlines them as literals so the file opens
directly via file:// — no PHP, no server, no fetch.
• THEME_JSON — the page's element tree (PageElement → blocks → …)
• THEME_TEMPLATES — 39 EJS template strings (Page, Block, Cell, …)
• THEME_CONFIG_DATA — fonts, colors, sizes, theme-level options
• MEDIA_URL — base URL for image src resolution
To regenerate after editing themes/default/* :
php scripts/demo/dump-bundle.php default master/sample/email/Minimal \
--media-url=../../../themes/default --split
then paste the output below.
BUYERS: copy this <script> + the next two scripts into your own page.
Every variable is right here, top-level, named — no indirection, no
server-side composition. Re-run dump-bundle.php whenever the theme
changes (or wire your own backend that emits the same four values).
REVIEWERS: open this file directly via file:// — no PHP, no server.
════════════════════════════════════════════════════════════════════ -->
<script>
window.THEME_JSON = {"theme":"default","name":"PageElement","template":"Page","formats":{"background_color":"#F4F4F4","padding_top":0,"padding_right":0,"padding_bottom":0,"padding_left":0},"page_title":null,"blocks":[{"name":"BlockElement","template":"Block","formats":{"padding_top":50,"padding_bottom":20},"elements":[{"name":"LinkElement","template":"Link","formats":{"text_color":"#000000","text_align":"center","text_decoration":"underline"},"text":"View this email in your browser","url":"{{WEB_VIEW_URL}}","target":"","rel":"","title":"","download":"","ariaLabel":""}]},{"name":"BlockElement","template":"Block","formats":{"padding_top":20,"padding_bottom":20},"elements":[{"name":"ImageElement","template":"Image","formats":{"width":130,"align":"center","border_radius":0},"src":"master/assets/image/email/logo.png","alt":"","url":"","target":"","rel":"","title":"","download":"","effect":{"grayscale":null,"sepia":null,"invert":null,"blur":null,"brightness":null,"contrast":null,"saturate":null,"hueRotate":null,"opacity":null},"crop":{"enabled":false,"ratioX":3,"ratioY":2,"zoom":1,"posX":50,"posY":50}}]},{"name":"BlockElement","template":"Block","formats":[],"elements":[{"name":"HeadingElement","template":"Heading","formats":{"text_align":"center","padding_bottom":20},"type":"h1","text":"It's time to design your email"},{"name":"PElement","template":"P","formats":{"text_align":"center"},"text":"You can define the layout of your email and give your content a place to live by adding, rearranging, and deleting content blocks."}]},{"name":"BlockElement","template":"Block","formats":[],"elements":[{"name":"ImageElement","template":"Image","formats":{"width":"100%","align":"center","border_radius":0},"src":"master/assets/image/email/banner_wide.png","alt":"","url":"","target":"","rel":"","title":"","download":"","effect":{"grayscale":null,"sepia":null,"invert":null,"blur":null,"brightness":null,"contrast":null,"saturate":null,"hueRotate":null,"opacity":null},"crop":{"enabled":false,"ratioX":3,"ratioY":2,"zoom":1,"posX":50,"posY":50}}]},{"name":"BlockElement","template":"Block","formats":[],"elements":[{"name":"ButtonElement","template":"Button","formats":{"align":"center","font_family":"inherit","font_weight":"400","font_size":18,"text_color":"#FFFFFF","link_color":"#ffffff","text_align":"center","line_height":"1.5","text_direction":"ltr","background_color":"#000000","background_position":"center","background_size":"100%","background_repeat":"no-repeat","padding_top":14,"padding_right":24,"padding_bottom":14,"padding_left":24,"border_top_style":"solid","border_top_width":0,"border_top_color":"#0d6efd","border_right_style":"solid","border_right_width":0,"border_right_color":"#0d6efd","border_bottom_style":"solid","border_bottom_width":0,"border_bottom_color":"#0d6efd","border_left_width":0,"border_left_style":"solid","border_left_color":"#0d6efd","border_radius":0},"text":"Register Now","url":"#","target":"","rel":"","title":"","download":"","ariaLabel":"","preset_id":"solid-ink","size_key":"lg","shadow_key":"none","hover_effect":"none","icon":null,"icon_position":"none"}]},{"name":"BlockElement","template":"Block","formats":[],"elements":[{"name":"DividerElement","template":"Divider","formats":{"background_color":"#333333","height":2,"divider_style":"solid","padding_top":10,"padding_bottom":10}}]},{"name":"BlockElement","template":"Block","formats":[],"elements":[{"name":"SocialIconsElement","template":"SocialIcons","formats":{"background_position":"center","background_size":"100%","background_repeat":"no-repeat"},"items":[{"link":"#","image_url":"master/assets/image/icons/facebook.png","label":"Facebook"},{"link":"#","image_url":"master/assets/image/icons/linkedin.png","label":"LinkedIn"},{"link":"#","image_url":"master/assets/image/icons/youtube.png","label":"YouTube"},{"link":"#","image_url":"master/assets/image/icons/twitter.png","label":"X"}],"size":35,"gap":30,"align":"center"}]},{"name":"BlockElement","template":"Block","formats":{"padding_top":20,"padding_bottom":20},"elements":[{"name":"ImageElement","template":"Image","formats":{"width":130,"align":"center","border_radius":0},"src":"master/assets/image/email/logo.png","alt":"","url":"","target":"","rel":"","title":"","download":"","effect":{"grayscale":null,"sepia":null,"invert":null,"blur":null,"brightness":null,"contrast":null,"saturate":null,"hueRotate":null,"opacity":null},"crop":{"enabled":false,"ratioX":3,"ratioY":2,"zoom":1,"posX":50,"posY":50}}]},{"name":"BlockElement","template":"Block","formats":{"padding_top":10,"padding_bottom":10},"elements":[{"name":"PElement","template":"P","formats":{"font_size":13,"text_align":"center"},"text":"Copyright (C) {{CURRENT_YEAR}}. All rights reserved."}]},{"name":"BlockElement","template":"Block","formats":[],"elements":[{"name":"PElement","template":"P","formats":{"font_size":13,"text_align":"center"},"text":"If you no longer want to receive these emails, you can <a href=\"{{UNSUBSCRIBE_URL}}\">unsubscribe</a>"}]}],"block_gap":0,"container_width":"medium","block_padding_bottom":20,"block_padding_top":20,"block_padding_right":20,"block_padding_left":20,"default_block_background_color":"#FFFFFF"};
window.THEME_TEMPLATES = {"Page":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title><%= page_title %></title>\n\n <style>\n html {\n margin: 0;\n }\n body {\n padding: 0;\n margin: 0;\n font-family: <%= (typeof formats !== 'undefined' && formats && formats.font_family) ? formats.font_family : 'Arial, Helvetica, sans-serif' %>;\n line-height: 1.4;\n }\n p, h1, h2, h3, h4, h5, h6, label, ul {\n margin: 0;\n }\n a {\n color: inherit;\n }\n [builder-element=\"PageElement\"] {\n min-height: 100vh;\n display: <%= (typeof formats !== 'undefined' && formats && formats.display) ? formats.display : ((typeof display !== 'undefined' && display) ? display : 'block') %>;\n flex-direction: <%= (typeof formats !== 'undefined' && formats && formats.flex_direction) ? formats.flex_direction : ((typeof flex_direction !== 'undefined' && flex_direction) ? flex_direction : '') %>;\n justify-content: <%= (typeof formats !== 'undefined' && formats && formats.justify_content) ? formats.justify_content : ((typeof justify_content !== 'undefined' && justify_content) ? justify_content : '') %>;\n align-items: <%= (typeof formats !== 'undefined' && formats && (formats.align_items || formats.align)) ? (formats.align_items || formats.align) : ((typeof align_items !== 'undefined' && align_items) ? align_items : '') %>;\n }\n\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n width: 100%;\n margin-left: auto;\n margin-right: auto;\n }\n\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"]:not(:last-child) {\n margin-bottom: <%= (typeof block_gap !== 'undefined') ? block_gap : 0 %>px;\n }\n\n <%\n var containerWidth = (typeof container_width !== 'undefined' && container_width) ? container_width : 'auto';\n %>\n\n <% if (containerWidth === 'narrow') { %>\n @media (min-width: 800px) {\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n max-width: 600px;\n }\n }\n <% } else if (containerWidth === 'medium') { %>\n @media (min-width: 800px) {\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n max-width: 800px;\n }\n }\n <% } else if (containerWidth === 'wide') { %>\n @media (min-width: 900px) {\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n max-width: 1024px;\n }\n }\n <% } else if (containerWidth === 'xl') { %>\n @media (min-width: 1200px) {\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n max-width: 1200px;\n }\n }\n <% } else if (containerWidth === 'full') { %>\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n max-width: none;\n width: 100%;\n }\n <% } %>\n\n <% if (typeof default_block_background_color !== 'undefined' && default_block_background_color) { %>\n /* default_block_background_color targets the INNER styled div (`> :first-child`),\n NOT the outer `<div builder-element=\"BlockElement\">` wrapper. The outer is a\n hover/selection anchor with no painting role; the inner is where the formatter\n writes per-block bg + border + radius + padding. Painting the default bg on\n the outer (legacy behavior, retired 2026-05-05) caused outer's flat-edge\n rectangle to visually mask any per-block `border_radius`. Targeting the inner\n lets default bg compose with per-block radius — they paint the same div,\n formatter's `!important` inline style still wins when set. */\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] > :first-child {\n background-color: <%= default_block_background_color %>;\n }\n <% } %>\n\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] > :first-child {\n padding-top: <%= (typeof block_padding_top !== 'undefined') ? block_padding_top : 0 %>px;\n padding-right: <%= (typeof block_padding_right !== 'undefined') ? block_padding_right : 0 %>px;\n padding-bottom: <%= (typeof block_padding_bottom !== 'undefined') ? block_padding_bottom : 0 %>px;\n padding-left: <%= (typeof block_padding_left !== 'undefined') ? block_padding_left : 0 %>px;\n }\n </style>\n</head>\n<body>\n <%- page %>\n</body>\n</html>","Form":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title><%= page_title %></title>\n\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css\">\n\n <style>\n html {\n margin: 0;\n }\n body {\n padding: 0;\n margin: 0;\n font-family: <%= (typeof formats !== 'undefined' && formats && formats.font_family) ? formats.font_family : 'Arial, Helvetica, sans-serif' %>;\n line-height: 1.4;\n }\n p, h1, h2, h3, h4, h5, h6, label, ul {\n margin: 0;\n }\n [builder-element=\"PageElement\"] {\n min-height: 100vh;\n display: <%= (typeof formats !== 'undefined' && formats && formats.display) ? formats.display : ((typeof display !== 'undefined' && display) ? display : 'block') %>;\n flex-direction: <%= (typeof formats !== 'undefined' && formats && formats.flex_direction) ? formats.flex_direction : ((typeof flex_direction !== 'undefined' && flex_direction) ? flex_direction : '') %>;\n justify-content: <%= (typeof formats !== 'undefined' && formats && formats.justify_content) ? formats.justify_content : ((typeof justify_content !== 'undefined' && justify_content) ? justify_content : '') %>;\n align-items: <%= (typeof formats !== 'undefined' && formats && (formats.align_items || formats.align)) ? (formats.align_items || formats.align) : ((typeof align_items !== 'undefined' && align_items) ? align_items : '') %>;\n }\n\n <% if (typeof default_block_background_color !== 'undefined' && default_block_background_color) { %>\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] {\n background-color: <%= default_block_background_color %>;\n }\n <% } %>\n\n [builder-element=\"PageElement\"] > [builder-element=\"BlockElement\"] > :first-child {\n padding-top: <%= (typeof block_padding_top !== 'undefined') ? block_padding_top : 0 %>px;\n padding-right: <%= (typeof block_padding_right !== 'undefined') ? block_padding_right : 0 %>px;\n padding-bottom: <%= (typeof block_padding_bottom !== 'undefined') ? block_padding_bottom : 0 %>px;\n padding-left: <%= (typeof block_padding_left !== 'undefined') ? block_padding_left : 0 %>px;\n }\n </style>\n</head>\n<body>\n <%- page %>\n</body>\n</html>","Block":"<div style=\"<%- formatter.toStyleStringAll() %>\">\n <%- elements %>\n</div>","Grid":"<div style=\"<%- formatter.toStyleStringAll() %> display: flex; width: 100%; gap: <%= cell_gap %>px; align-items: <%= align_items || 'stretch' %>;\">\n <% cells.forEach(function(cell) { %>\n <%- cell %>\n <% }); %>\n</div>\n","Cell":"<div style=\"<%\n var width = formatter.getFormat('width', 'auto');\n var gap = (typeof cell_gap === 'number' && cell_gap > 0) ? cell_gap : 0;\n var count = (typeof cell_count === 'number' && cell_count > 0) ? cell_count : 1;\n var flexStyle = '';\n if (width === 'auto' || width === null || width === '') {\n // Natural content-based sizing — no flex grow/shrink.\n flexStyle = '';\n } else if (width === 'fill') {\n // 2026-04-18 explicit \"fill remaining space\" primitive. The canonical\n // way to say \"this cell grows to absorb whatever space the auto/fixed\n // siblings don't use\". Renders as grow-share 1 with zero basis so the\n // flex algorithm distributes remaining space evenly among all `fill`\n // cells (typical use = exactly one `fill` per row). Replaces the\n // implicit grow-share semantic of bare numbers that used to live in\n // mixed `[\"auto\", 50]` grids — those are now `[\"auto\", \"fill\"]`.\n flexStyle = 'flex: 1 1 0; min-width: 0;';\n } else {\n // `width` has two valid shapes — units decide intent:\n //\n // 1. CSS percentage (\"50%\", \"33.33%\"):\n // author wants THIS cell to occupy that share of the row's CSS\n // width. Emit as `flex-basis` clamped to 0/0 so the browser honors\n // the value exactly. When the grid also has a `cell_gap`, subtract\n // this cell's proportional share of gap from the basis — otherwise\n // two 50% cells + gap overflow the row (2×50% + gap > 100%). Gap\n // share = total_gap / count = (count - 1) × gap / count.\n // Cell sizes may still sum to < 100% — that's the author's call\n // (e.g. [15%, 70%] leaves a 15% slack on purpose).\n //\n // 2. Other CSS length with unit (\"200px\", \"15em\", \"30vw\"):\n // pass through as flex-basis unchanged — absolute units are\n // independent of container width so no gap compensation is possible\n // without overflowing.\n //\n // 3. Bare number (50, 100, 33.33): author wants a GROW-SHARE\n // ratio. Grid cells distribute horizontal space in proportion:\n // [50, 50] → 50/50 split, [30, 70] → 30/70, [1, 2] → 1:2.\n // Totals don't need to sum to anything specific and the flex\n // algorithm already accounts for gap automatically.\n //\n // Anything else (unparseable) falls back to equal share so we don't\n // render a zero-width cell.\n //\n // `min-width: 0` lets content shrink below its intrinsic width so\n // long words don't force overflow.\n var str = String(width).trim();\n if (/%$/.test(str)) {\n var pct = parseFloat(str);\n if (!isNaN(pct) && pct > 0) {\n if (gap > 0 && count > 1) {\n var gapShare = (gap * (count - 1) / count);\n flexStyle = 'flex: 0 0 calc(' + pct + '% - ' + gapShare + 'px); min-width: 0;';\n } else {\n flexStyle = 'flex: 0 0 ' + pct + '%; min-width: 0;';\n }\n } else {\n flexStyle = 'flex: 1 1 0; min-width: 0;';\n }\n } else if (/(px|em|rem|vw|vh|ch|ex)$/i.test(str)) {\n flexStyle = 'flex: 0 0 ' + str + '; min-width: 0;';\n } else {\n var numWidth = parseFloat(str);\n if (!isNaN(numWidth) && numWidth > 0) {\n flexStyle = 'flex: ' + numWidth + ' 1 0; min-width: 0;';\n } else {\n flexStyle = 'flex: 1 1 0; min-width: 0;';\n }\n }\n }\n\n var height = formatter.getFormat('height');\n var heightStyle = '';\n if (height !== null && height !== '' && height !== undefined) {\n heightStyle = 'height: ' + height + (typeof height === 'number' ? 'px' : '') + ';';\n }\n\n var otherStyles = formatter.toStyleStringAll().replace(/width:\\s*[^;]+;?\\s*/gi, '').replace(/height:\\s*[^;]+;?\\s*/gi, '').trim();\n var finalStyle = (flexStyle + ' ' + heightStyle + ' ' + otherStyles).trim();\n%><%- finalStyle %>\">\n <% if (blocks && blocks.length) { blocks.forEach(function(block) { %>\n <%- block %>\n <% }); } %>\n</div>\n","P":"<p style=\"<%- formatter.toStyleStringAll() %>\" inline-edit=\"text\"><%- text %></p>\n","Link":"<a href=\"<%- url %>\"<% if (typeof target !== 'undefined' && target) { %> target=\"<%- target %>\"<% } %><% if (typeof rel !== 'undefined' && rel) { %> rel=\"<%- rel %>\"<% } %><% if (typeof title !== 'undefined' && title) { %> title=\"<%- title %>\"<% } %><% if (typeof download !== 'undefined' && download) { %> download=\"<%- download %>\"<% } %> style=\"display: block;<%- formatter.toStyleStringAll() %>\" inline-edit=\"text\"><%- text %></a>\n","H1":"<h1 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h1>","H2":"<h2 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h2>","H3":"<h3 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h3>","H4":"<h4 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h4>","H5":"<h5 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h5>","Heading":"<% if (type === 'h1') { %>\n <h1 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h1>\n<% } else if (type === 'h2') { %>\n <h2 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h2>\n<% } else if (type === 'h3') { %>\n <h3 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h3>\n<% } else if (type === 'h4') { %>\n <h4 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h4>\n<% } else if (type === 'h5') { %>\n <h5 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h5>\n<% } else if (type === 'h6') { %>\n <h6 inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></h6>\n<% } else { %>\n <p inline-edit=\"text\" style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></p>\n<% } %>\n","Image":"<div style=\"text-align: <%- formatter.getFormat(\"align\", \"center\") %>;\">\n <% if (url) { %><a href=\"<%- url %>\"<% if (target) { %> target=\"<%- target %>\"<% } %> style=\"text-decoration:none;display:inline-block;\"><% } %>\n <% if (crop && crop.enabled) { %>\n <%\n // Sanitize ratio integers (defensive — bad data should never reach here, but be safe)\n var rx = (typeof crop.ratioX === 'number' && crop.ratioX >= 1) ? Math.round(crop.ratioX) : 3;\n var ry = (typeof crop.ratioY === 'number' && crop.ratioY >= 1) ? Math.round(crop.ratioY) : 2;\n %>\n <div data-bjs-crop-frame=\"true\" style=\"display:inline-block;vertical-align:top;overflow:hidden;line-height:0;font-size:0;width:100%;aspect-ratio:<%= rx %>/<%= ry %>;\n <%- formatter.toStyleStringAll(['padding_top','padding_right','padding_bottom','padding_left','width','height','max_width','max_height','min_width','min_height','border_top_style','border_top_width','border_top_color','border_right_style','border_right_width','border_right_color','border_bottom_style','border_bottom_width','border_bottom_color','border_left_width','border_left_style','border_left_color','border_radius','box_shadow']) %>\n \">\n <img src=\"<%= src %>\" alt=\"<%= alt %>\"\n style=\"display:block;width:100%;height:100%;object-fit:cover;object-position:<%= crop.posX %>% <%= crop.posY %>%;transform:scale(<%= crop.zoom %>);transform-origin:<%= crop.posX %>% <%= crop.posY %>%;\n <% if (effect) { %>filter: grayscale(<%= effect.grayscale %>%) sepia(<%= effect.sepia %>%) invert(<%= effect.invert %>%) blur(<%= effect.blur %>px) brightness(<%= effect.brightness %>%) contrast(<%= effect.contrast %>%) saturate(<%= effect.saturate %>%) hue-rotate(<%= effect.hueRotate %>deg) opacity(<%= effect.opacity %>%);<% } %>\n \">\n </div>\n <% } else { %>\n <img src=\"<%= src %>\" alt=\"<%= alt %>\"\n style=\"display:inline-block;vertical-align:top;\n <%- formatter.toStyleStringAll(['padding_top','padding_right','padding_bottom','padding_left','width','height','max_width','max_height','min_width','min_height','border_top_style','border_top_width','border_top_color','border_right_style','border_right_width','border_right_color','border_bottom_style','border_bottom_width','border_bottom_color','border_left_width','border_left_style','border_left_color','border_radius','box_shadow']) %>\n <% if (effect) { %>filter: grayscale(<%= effect.grayscale %>%) sepia(<%= effect.sepia %>%) invert(<%= effect.invert %>%) blur(<%= effect.blur %>px) brightness(<%= effect.brightness %>%) contrast(<%= effect.contrast %>%) saturate(<%= effect.saturate %>%) hue-rotate(<%= effect.hueRotate %>deg) opacity(<%= effect.opacity %>%);<% } %>\n \">\n <% } %>\n <% if (url) { %></a><% } %>\n</div>\n","Label":"<label style=\"<%- formatter.toStyleStringAll() %>\"><%- text %></label>","Button":"<div style=\"text-align: <%- formatter.getFormat('align', 'center') %>;\">\n <a href=\"<%- url %>\"<% if (typeof target !== 'undefined' && target) { %> target=\"<%- target %>\"<% } %><% if (typeof rel !== 'undefined' && rel) { %> rel=\"<%- rel %>\"<% } %><% if (typeof title !== 'undefined' && title) { %> title=\"<%- title %>\"<% } %><% if (typeof download !== 'undefined' && download) { %> download=\"<%- download %>\"<% } %> style=\"<%- formatter.toStyleStringAll() %>box-sizing: border-box;\n <% if (formatter && formatter.getFormat && formatter.getFormat('width')) { %>\n display:block;\n <% if (formatter.getFormat('align') === 'center') { %> margin-left: auto; margin-right: auto; <% } %>\n <% } else { %>\n display:inline-block;\n <% } %>\n text-decoration: none;\"\n ><span inline-edit=\"text\"><%- text %></span></a>\n</div>\n","Alert":"<div style=\"<%- formatter.toStyleStringAll() %>\" inline-edit=\"text\"><%- text %></div>","RSS":"<div style=\"<%- formatter.toStyleStringAll() %>\">\n <% if (!url) { %>\n <!-- PLACEHOLDER STATE — Skeleton mirrors actual rendered layout -->\n <div style=\"padding: 16px; background: #f8f9fb; border: 2px dashed #d0d5dd; border-radius: 12px;\">\n <!-- Header -->\n <div style=\"display: flex; align-items: center; gap: 10px; margin-bottom: 14px;\">\n <svg style=\"width: 28px; height: 28px; flex-shrink: 0;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 40 40\" fill=\"none\">\n <rect width=\"40\" height=\"40\" rx=\"10\" fill=\"#FF6B35\" opacity=\"0.15\"/>\n <path d=\"M14 26a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm12 0c0-6.627-5.373-12-12-12\" stroke=\"#FF6B35\" stroke-width=\"2.5\" stroke-linecap=\"round\" fill=\"none\"/>\n <path d=\"M30 26c0-9.941-8.059-18-18-18\" stroke=\"#FF6B35\" stroke-width=\"2.5\" stroke-linecap=\"round\" fill=\"none\"/>\n </svg>\n <div>\n <div style=\"font-weight: 700; font-size: 15px; color: #344054;\"><%- typeof I18n !== 'undefined' ? I18n.t('rss.configure_feed') : 'RSS Feed' %></div>\n <div style=\"font-size: 12px; color: #98a2b3;\"><%- typeof I18n !== 'undefined' ? I18n.t('rss.configure_feed_desc') : 'Add an RSS URL in the settings panel to display your feed here.' %></div>\n </div>\n </div>\n\n <% if (style === 'modern') { %>\n <!-- ═══ MODERN SKELETON — matches card layout exactly ═══ -->\n\n <!-- Skeleton Card 1: image + title + desc + meta -->\n <div style=\"border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05); background: #fff;\">\n <!-- Image placeholder: 200px height like real render -->\n <svg width=\"100%\" height=\"200\" viewBox=\"0 0 600 200\" preserveAspectRatio=\"xMidYMid slice\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"600\" height=\"200\" fill=\"#eaecf0\"/>\n <g transform=\"translate(260,70)\">\n <rect x=\"0\" y=\"0\" width=\"80\" height=\"60\" rx=\"8\" fill=\"#d0d5dd\"/>\n <polygon points=\"20,48 32,28 44,48\" fill=\"#c0c5cc\"/>\n <polygon points=\"36,48 50,34 64,48\" fill=\"#bcc1c8\"/>\n <circle cx=\"54\" cy=\"24\" r=\"8\" fill=\"#c0c5cc\"/>\n </g>\n </svg>\n <div style=\"padding: 16px;\">\n <!-- Title skeleton: h3 18px font-weight 600 -->\n <div style=\"height: 16px; width: 78%; background: #d0d5dd; border-radius: 4px; margin-bottom: 10px;\"></div>\n <!-- Description skeleton: 14px two lines -->\n <div style=\"height: 11px; width: 96%; background: #eaecf0; border-radius: 3px; margin-bottom: 6px;\"></div>\n <div style=\"height: 11px; width: 72%; background: #eaecf0; border-radius: 3px; margin-bottom: 12px;\"></div>\n <!-- Date + author meta: 12px -->\n <div style=\"display: flex; align-items: center; gap: 10px;\">\n <div style=\"display: flex; align-items: center; gap: 4px;\">\n <svg style=\"width: 12px; height: 12px; fill: #d0d5dd;\" viewBox=\"0 -960 960 960\"><path d=\"M580-240q-42 0-71-29t-29-71q0-42 29-71t71-29q42 0 71 29t29 71q0 42-29 71t-71 29ZM200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Z\"/></svg>\n <div style=\"height: 8px; width: 90px; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n <div style=\"height: 8px; width: 60px; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n </div>\n\n <!-- Skeleton Card 2: no image, title + desc + meta -->\n <div style=\"border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05); background: #fff;\">\n <div style=\"padding: 16px;\">\n <div style=\"height: 16px; width: 62%; background: #d0d5dd; border-radius: 4px; margin-bottom: 10px;\"></div>\n <div style=\"height: 11px; width: 90%; background: #eaecf0; border-radius: 3px; margin-bottom: 6px;\"></div>\n <div style=\"height: 11px; width: 58%; background: #eaecf0; border-radius: 3px; margin-bottom: 12px;\"></div>\n <div style=\"display: flex; gap: 10px;\">\n <div style=\"height: 8px; width: 90px; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n </div>\n\n <!-- Skeleton Card 3: no image, shorter -->\n <div style=\"border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05); background: #fff;\">\n <div style=\"padding: 16px;\">\n <div style=\"height: 16px; width: 70%; background: #d0d5dd; border-radius: 4px; margin-bottom: 10px;\"></div>\n <div style=\"height: 11px; width: 84%; background: #eaecf0; border-radius: 3px; margin-bottom: 6px;\"></div>\n <div style=\"height: 11px; width: 45%; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n\n <% } else { %>\n <!-- ═══ CLASSIC SKELETON — matches list layout exactly ═══ -->\n\n <!-- Row 1: thumb 80x60 + title + desc + date -->\n <div style=\"padding: 14px 0; border-bottom: 1px solid #eee;\">\n <div style=\"display: flex; gap: 14px; align-items: flex-start;\">\n <!-- Thumbnail placeholder: 80x60 rounded like real -->\n <svg width=\"80\" height=\"60\" viewBox=\"0 0 80 60\" xmlns=\"http://www.w3.org/2000/svg\" style=\"flex-shrink: 0; border-radius: 4px; overflow: hidden;\">\n <rect width=\"80\" height=\"60\" rx=\"4\" fill=\"#eaecf0\"/>\n <g transform=\"translate(24,14)\">\n <rect x=\"0\" y=\"0\" width=\"32\" height=\"32\" rx=\"4\" fill=\"#d0d5dd\"/>\n <polygon points=\"8,26 14,16 20,26\" fill=\"#c0c5cc\"/>\n <circle cx=\"22\" cy=\"14\" r=\"4\" fill=\"#c0c5cc\"/>\n </g>\n </svg>\n <div style=\"flex: 1; min-width: 0;\">\n <div style=\"height: 13px; width: 75%; background: #d0d5dd; border-radius: 3px; margin-bottom: 7px;\"></div>\n <div style=\"height: 10px; width: 95%; background: #eaecf0; border-radius: 3px; margin-bottom: 5px;\"></div>\n <div style=\"height: 10px; width: 60%; background: #eaecf0; border-radius: 3px; margin-bottom: 6px;\"></div>\n <div style=\"height: 8px; width: 100px; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n </div>\n\n <!-- Row 2: thumb + title + desc + date -->\n <div style=\"padding: 14px 0; border-bottom: 1px solid #eee;\">\n <div style=\"display: flex; gap: 14px; align-items: flex-start;\">\n <svg width=\"80\" height=\"60\" viewBox=\"0 0 80 60\" xmlns=\"http://www.w3.org/2000/svg\" style=\"flex-shrink: 0; border-radius: 4px;\">\n <rect width=\"80\" height=\"60\" rx=\"4\" fill=\"#eaecf0\"/>\n <g transform=\"translate(24,14)\"><rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#d0d5dd\"/><polygon points=\"8,26 14,16 20,26\" fill=\"#c0c5cc\"/><circle cx=\"22\" cy=\"14\" r=\"4\" fill=\"#c0c5cc\"/></g>\n </svg>\n <div style=\"flex: 1; min-width: 0;\">\n <div style=\"height: 13px; width: 65%; background: #d0d5dd; border-radius: 3px; margin-bottom: 7px;\"></div>\n <div style=\"height: 10px; width: 88%; background: #eaecf0; border-radius: 3px; margin-bottom: 5px;\"></div>\n <div style=\"height: 10px; width: 50%; background: #eaecf0; border-radius: 3px; margin-bottom: 6px;\"></div>\n <div style=\"height: 8px; width: 80px; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n </div>\n\n <!-- Row 3: thumb + title + desc -->\n <div style=\"padding: 14px 0; border-bottom: 1px solid #eee;\">\n <div style=\"display: flex; gap: 14px; align-items: flex-start;\">\n <svg width=\"80\" height=\"60\" viewBox=\"0 0 80 60\" xmlns=\"http://www.w3.org/2000/svg\" style=\"flex-shrink: 0; border-radius: 4px;\">\n <rect width=\"80\" height=\"60\" rx=\"4\" fill=\"#eaecf0\"/><g transform=\"translate(24,14)\"><rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#d0d5dd\"/><polygon points=\"8,26 14,16 20,26\" fill=\"#c0c5cc\"/><circle cx=\"22\" cy=\"14\" r=\"4\" fill=\"#c0c5cc\"/></g>\n </svg>\n <div style=\"flex: 1; min-width: 0;\">\n <div style=\"height: 13px; width: 72%; background: #d0d5dd; border-radius: 3px; margin-bottom: 7px;\"></div>\n <div style=\"height: 10px; width: 80%; background: #eaecf0; border-radius: 3px; margin-bottom: 5px;\"></div>\n <div style=\"height: 10px; width: 42%; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n </div>\n\n <!-- Row 4: no thumb, title only -->\n <div style=\"padding: 14px 0;\">\n <div style=\"display: flex; gap: 14px; align-items: flex-start;\">\n <svg width=\"80\" height=\"60\" viewBox=\"0 0 80 60\" xmlns=\"http://www.w3.org/2000/svg\" style=\"flex-shrink: 0; border-radius: 4px;\">\n <rect width=\"80\" height=\"60\" rx=\"4\" fill=\"#eaecf0\"/><g transform=\"translate(24,14)\"><rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#d0d5dd\"/><polygon points=\"8,26 14,16 20,26\" fill=\"#c0c5cc\"/><circle cx=\"22\" cy=\"14\" r=\"4\" fill=\"#c0c5cc\"/></g>\n </svg>\n <div style=\"flex: 1; min-width: 0;\">\n <div style=\"height: 13px; width: 58%; background: #d0d5dd; border-radius: 3px; margin-bottom: 7px;\"></div>\n <div style=\"height: 10px; width: 75%; background: #eaecf0; border-radius: 3px; margin-bottom: 5px;\"></div>\n <div style=\"height: 8px; width: 90px; background: #eaecf0; border-radius: 3px;\"></div>\n </div>\n </div>\n </div>\n\n <% } %>\n </div>\n\n <% } else if (loading) { %>\n <!-- LOADING STATE -->\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px;\">\n <div style=\"width: 40px; height: 40px; border: 3px solid #e0e0e0; border-top-color: #335fa0; border-radius: 50%; animation: rss-spin 0.8s linear infinite;\"></div>\n <p style=\"margin: 12px 0 0 0; font-size: 14px; color: #999;\"><%- typeof I18n !== 'undefined' ? I18n.t('rss.loading') : 'Loading feed...' %></p>\n <style>@keyframes rss-spin { to { transform: rotate(360deg); } }</style>\n </div>\n\n <% } else if (error) { %>\n <!-- ERROR STATE -->\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; text-align: center;\">\n <svg style=\"height: 48px; width: 48px; fill: #dc3545;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\">\n <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z\"/>\n </svg>\n <p style=\"margin: 12px 0 0 0; font-size: 14px; color: #dc3545; font-weight: 500;\"><%- error %></p>\n <p style=\"margin: 4px 0 0 0; font-size: 13px; color: #999;\"><%- typeof I18n !== 'undefined' ? I18n.t('rss.network_error') : 'Please check the URL and try again.' %></p>\n </div>\n\n <% } else if (items && items.length > 0) { %>\n\n <% if (style === 'modern') { %>\n <!-- MODERN CARD LAYOUT -->\n <% items.forEach(function(item, index) { %>\n <div style=\"border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 16px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.05); background: #fff;\">\n <% if (show_image && item.image) { %>\n <div style=\"width: 100%; height: 200px; overflow: hidden;\">\n <img src=\"<%- item.image %>\" alt=\"\" style=\"width: 100%; height: 100%; object-fit: cover; display: block;\" />\n </div>\n <% } %>\n <div style=\"padding: 16px;\">\n <% if (show_title) { %>\n <% if (show_link && item.link) { %>\n <a href=\"<%- item.link %>\" target=\"_blank\" style=\"text-decoration: none; color: inherit;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 18px; font-weight: 600; line-height: 1.3;\"><%- item.title %></h3>\n </a>\n <% } else { %>\n <h3 style=\"margin: 0 0 8px 0; font-size: 18px; font-weight: 600; line-height: 1.3;\"><%- item.title %></h3>\n <% } %>\n <% } %>\n <% if (show_desc && item.description) { %>\n <p style=\"margin: 0 0 10px 0; font-size: 14px; color: #555; line-height: 1.5;\"><%- truncate(item.description, content_length) %></p>\n <% } %>\n <div style=\"display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 12px; color: #999;\">\n <% if (show_date && item.pubDate) { %>\n <span style=\"display: inline-flex; align-items: center; gap: 4px;\">\n <svg style=\"width: 14px; height: 14px; fill: currentColor;\" viewBox=\"0 -960 960 960\"><path d=\"M580-240q-42 0-71-29t-29-71q0-42 29-71t71-29q42 0 71 29t29 71q0 42-29 71t-71 29ZM200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Z\"/></svg>\n <%- item.pubDate %>\n </span>\n <% } %>\n <% if (show_author && item.author) { %>\n <span><%- typeof I18n !== 'undefined' ? I18n.t('rss.by') : 'by' %> <%- item.author %></span>\n <% } %>\n </div>\n <% if (show_link && item.link && !show_title) { %>\n <a href=\"<%- item.link %>\" target=\"_blank\" style=\"display: inline-block; margin-top: 8px; font-size: 13px; color: #335fa0; text-decoration: none; font-weight: 500;\"><%- typeof I18n !== 'undefined' ? I18n.t('rss.read_more') : 'Read more' %> →</a>\n <% } %>\n </div>\n </div>\n <% }); %>\n\n <% } else { %>\n <!-- CLASSIC LIST LAYOUT -->\n <% items.forEach(function(item, index) { %>\n <div style=\"padding: 14px 0; <%= index < items.length - 1 ? 'border-bottom: 1px solid #eee;' : '' %>\">\n <div style=\"display: flex; gap: 14px; align-items: flex-start;\">\n <% if (show_image && item.image) { %>\n <img src=\"<%- item.image %>\" alt=\"\" style=\"width: 80px; height: 60px; object-fit: cover; border-radius: 4px; flex-shrink: 0;\" />\n <% } %>\n <div style=\"flex: 1; min-width: 0;\">\n <% if (show_title) { %>\n <% if (show_link && item.link) { %>\n <a href=\"<%- item.link %>\" target=\"_blank\" style=\"font-weight: 600; font-size: 15px; color: #333; text-decoration: none; line-height: 1.3; display: block;\"><%- item.title %></a>\n <% } else { %>\n <div style=\"font-weight: 600; font-size: 15px; color: #333; line-height: 1.3;\"><%- item.title %></div>\n <% } %>\n <% } %>\n <% if (show_desc && item.description) { %>\n <p style=\"margin: 4px 0 0 0; font-size: 13px; color: #666; line-height: 1.4;\"><%- truncate(item.description, content_length) %></p>\n <% } %>\n <div style=\"margin-top: 4px; font-size: 12px; color: #999;\">\n <% if (show_date && item.pubDate) { %>\n <span><%- item.pubDate %></span>\n <% } %>\n <% if (show_author && item.author) { %>\n <% if (show_date && item.pubDate) { %> · <% } %>\n <span><%- item.author %></span>\n <% } %>\n </div>\n </div>\n </div>\n </div>\n <% }); %>\n <% } %>\n\n <% } else { %>\n <!-- EMPTY STATE -->\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 40px 20px; text-align: center;\">\n <svg style=\"height: 48px; width: 48px; fill: #999;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 -960 960 960\">\n <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm-40-160h80v-240h-80v240Zm40 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z\"/>\n </svg>\n <p style=\"margin: 12px 0 0 0; font-size: 14px; color: #999;\"><%- typeof I18n !== 'undefined' ? I18n.t('rss.no_items') : 'No items found in this feed.' %></p>\n </div>\n <% } %>\n</div>\n","Youtube":"<div style=\"display: flex; align-items: center; justify-content: <%- formatter.getFormat('align', 'center') %>;\">\n <% if (!youtubeID) { %>\n <%\n var placeholderStyle = formatter.toStyleStringAll([\n 'width','height',\n 'border_top_style','border_top_width','border_top_color',\n 'border_right_style','border_right_width','border_right_color',\n 'border_bottom_style','border_bottom_width','border_bottom_color',\n 'border_left_style','border_left_width','border_left_color',\n 'border_radius'\n ]);\n %>\n <div class=\"bjs-video-placeholder\" style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 2.25rem 1rem; text-align: center; box-sizing: border-box; max-width: 100%; min-width: 240px; min-height: 180px; <%- placeholderStyle %> border: 1px dashed #c7ccd4; background: #f6f7f9; color: #5a6270;\">\n <svg width=\"96\" height=\"72\" viewBox=\"0 0 96 72\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" style=\"flex: 0 0 auto;\">\n <rect x=\"1\" y=\"1\" width=\"94\" height=\"62\" rx=\"10\" fill=\"#ffffff\" stroke=\"#c7ccd4\" stroke-width=\"1.5\"/>\n <path d=\"M40 22 L62 32 L40 42 Z\" fill=\"#EA333D\" opacity=\"0.9\"/>\n <line x1=\"10\" y1=\"68\" x2=\"86\" y2=\"68\" stroke=\"#c7ccd4\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <div>\n <h4 style=\"margin: 0 0 4px; font-weight: 600; font-size: 15px; color: #1f2937;\" inline-edit=\"text\">No YouTube video yet</h4>\n <p style=\"margin: 0; font-size: 13px; line-height: 1.5;\" inline-edit=\"text\">Paste a YouTube URL (youtube.com/watch?v=… or youtu.be/…) in the right panel to embed it here.</p>\n </div>\n </div>\n <% } else { %>\n <iframe\n style=\"display: block; max-width: 100%; <%- formatter.toStyleStringAll(['width','height','border_top_style','border_top_width','border_top_color','border_right_style','border_right_width','border_right_color','border_bottom_style','border_bottom_width','border_bottom_color','border_left_style','border_left_width','border_left_color','border_radius']) %>\"\n src=\"https://www.youtube.com/embed/<%= youtubeID %>\"\n title=\"YouTube video player\"\n frameborder=\"0\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n referrerpolicy=\"strict-origin-when-cross-origin\"\n allowfullscreen></iframe>\n <% } %>\n</div>\n","Menu":"<%\nvar orientation = formatter.getFormat('orientation', 'horizontal');\nvar flexDirection = orientation === 'vertical' ? 'column' : 'row';\nvar separatorPaddingProp = orientation === 'vertical' ? 'padding-top' : 'padding-left';\nvar separatorPaddingProp2 = orientation === 'vertical' ? 'padding-bottom' : 'padding-right';\nvar textAlign = formatter.getFormat('text_align');\nvar alignItems = 'center';\nif (orientation === 'vertical' && textAlign) {\n alignItems = textAlign === 'left' ? 'flex-start' : (textAlign === 'right' ? 'flex-end' : 'center');\n}\n// Only dump the text/style-relevant formats onto the <a>. Without this\n// filter, non-style keys (align, orientation, separator, separator_gap)\n// get kebab-cased into the style attribute as invalid CSS like\n// `align: center; orientation: horizontal;` — benign visually but\n// confusing when inspecting and makes audits harder to reason about.\nvar anchorStyles = formatter.toStyleStringAll(['font_family', 'font_weight', 'font_size', 'text_color']);\n%>\n<div style=\"display: flex; justify-content: <%- formatter.getFormat('align', 'center') %>;\">\n <ul style=\"display: flex; gap: <%- menu_gap %>px; flex-direction: <%- flexDirection %>; list-style: none; padding: 0; align-items: <%- alignItems %>; margin: 0;\">\n <% items.forEach(function(item, idx) { %>\n <li>\n <a href=\"<%= item.url %>\" style=\"text-decoration: none; <%- anchorStyles %>\">\n <%= item.text %>\n </a>\n </li>\n\n <% if (idx < items.length - 1 && formatter && formatter.getFormat && formatter.getFormat('separator')) { %>\n <li class=\"menu-separator\" style=\"display:flex; align-items:center; <%- separatorPaddingProp %>:<%- formatter.getFormat('separator_gap', menu_gap) %>px; <%- separatorPaddingProp2 %>:<%- formatter.getFormat('separator_gap', menu_gap) %>px;\">\n <span style=\"color: <%- formatter.getFormat('text_color') || '#000' %>;\"> <%- formatter.getFormat('separator') %> </span>\n </li>\n <% } %>\n\n <% }); %>\n </ul>\n</div>","HTML":"<%- html %>","Video":"<div style=\"display: flex; align-items: center; justify-content: <%- formatter.getFormat('align', 'center') %>;\">\n <% if (!src) { %>\n <%\n // Apply the same width/height the real <video> would use, so the\n // user can visualize how large the video placeholder will render\n // while they tweak the sliders. Fall back to `auto` when unset so\n // the empty state stays compact on first drop.\n var placeholderStyle = formatter.toStyleStringAll([\n 'width','height',\n 'border_top_style','border_top_width','border_top_color',\n 'border_right_style','border_right_width','border_right_color',\n 'border_bottom_style','border_bottom_width','border_bottom_color',\n 'border_left_style','border_left_width','border_left_color',\n 'border_radius'\n ]);\n %>\n <div class=\"bjs-video-placeholder\" style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 2.25rem 1rem; text-align: center; box-sizing: border-box; max-width: 100%; min-width: 240px; min-height: 180px; <%- placeholderStyle %> border: 1px dashed #c7ccd4; background: #f6f7f9; color: #5a6270;\">\n <svg width=\"96\" height=\"72\" viewBox=\"0 0 96 72\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" style=\"flex: 0 0 auto;\">\n <rect x=\"1\" y=\"1\" width=\"94\" height=\"62\" rx=\"6\" fill=\"#ffffff\" stroke=\"#c7ccd4\" stroke-width=\"1.5\"/>\n <path d=\"M40 22 L62 32 L40 42 Z\" fill=\"#3b82f6\" opacity=\"0.85\"/>\n <line x1=\"10\" y1=\"68\" x2=\"86\" y2=\"68\" stroke=\"#c7ccd4\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n </svg>\n <div>\n <h4 style=\"margin: 0 0 4px; font-weight: 600; font-size: 15px; color: #1f2937;\" inline-edit=\"text\">No video yet</h4>\n <p style=\"margin: 0; font-size: 13px; line-height: 1.5;\" inline-edit=\"text\">Paste a direct video URL (.mp4, .webm, .ogg) in the right panel to preview it here.</p>\n </div>\n </div>\n <% } else { %>\n <div style=\"position: relative; max-width: 100%;\">\n <video\n style=\"display: block; max-width: 100%; <%- formatter.toStyleStringAll(['width','height','border_top_style','border_top_width','border_top_color','border_right_style','border_right_width','border_right_color','border_bottom_style','border_bottom_width','border_bottom_color','border_left_style','border_left_width','border_left_color','border_radius']) %> box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\"\n src=\"<%= src %>\"\n autoplay=\"[[AUTOPLAY]]\" loop=\"[[LOOP]]\" muted=\"[[MUTED]]\" playsinline=\"[[PLAYSINLINE]]\" controls=\"[[CONTROLS]]\"\n onerror=\"var e=this.nextElementSibling; if(e){e.hidden=false;} this.style.visibility='hidden';\"\n >\n <source src=\"<%= src %>\" type=\"video/mp4\">\n <source src=\"[[SRC_OGG]]\" type=\"video/ogg\">\n <source src=\"[[SRC_WEBM]]\" type=\"video/webm\">\n Your browser does not support the video tag.\n </video>\n <div hidden style=\"position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 16px; text-align: center; border: 1px dashed #e0b4b4; border-radius: 8px; background: #fff5f5; color: #b04343; font-size: 13px; line-height: 1.5;\">\n <span>The video URL couldn't be played. The file may be missing, blocked by CORS, or not a supported format (mp4/webm/ogg).</span>\n </div>\n </div>\n <% } %>\n</div>\n","SocialIcons":"<div width=\"100%\" builder-element=\"BlockElement\">\n <div style=\"text-align: <%- align %>;\">\n <div class=\"\" style=\"display: inline-block;\">\n <div builder-element=\"IconsContainerElement\"\n style=\"display: flex; gap: <%= gap %>px; <%- formatter.toStyleStringAll() %>\">\n <% items.forEach(function(item) { %>\n <div style=\"display: inline-block;\">\n <a builder-element=\"IconLinkElement\" data-value=\"twitter\" href=\"<%= item.link %>\"\n class=\"\">\n <span class=\"social-icon\" inline-edit=\"text\">\n <img alt=\"<%= item.label %>\" src=\"<%= item.image_url %>\" width=\"<%- size %>px\" height=\"<%- size %>px\">\n </span>\n </a>\n </div>\n <% }); %>\n </div>\n </div>\n </div>\n</div>","TextInput":"<div<% if (formatter && formatter.getFormat && formatter.getFormat('align')) { %> style=\"text-align:<%- formatter.getFormat('align') %>\"<% } %>>\n <input type=\"<%- type %>\" name=\"<%- name %>\" value=\"<%- value %>\"\n <%- required ? 'required' : '' %>\n placeholder=\"<%- placeholder %>\" style=\"<%- formatter.toStyleStringAll() %>box-sizing: border-box;\n <% if (formatter && formatter.getFormat && formatter.getFormat('width')) { %>\n display:block;\n <% if (formatter.getFormat('align') === 'center') { %> margin-left: auto; margin-right: auto; <% } %>\n <% } else { %>\n display:block; width:100%;\n <% } %>\" />\n</div>","Select":"<div style=\"width: 100%;\">\n <select\n name=\"<%- inputName || 'select_field' %>\"\n style=\"<%- formatter.toStyleStringAll() %>box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 12px; color: #0f172a; background-color: <%- formatter.getFormat('background_color', '#ffffff') %>; <% if (!formatter.getFormat('width')) { %>width: 100%;<% } %> <% if (!formatter.getFormat('height')) { %>min-height: 44px;<% } %>\"\n >\n <% items.forEach(function(item, index) { %>\n <option value=\"<%- item.value %>\" <%= index === 0 ? 'selected' : '' %>><%- item.label %></option>\n <% }); %>\n </select>\n</div>\n","Radio":"<div style=\"display: grid; gap: 10px;\">\n <% var groupName = inputName || 'radio_group'; %>\n <% items.forEach(function(item, index) { %>\n <% var optionId = groupName + '_' + index; %>\n <label for=\"<%- optionId %>\" style=\"display: flex; align-items: flex-start; gap: 10px; cursor: pointer; color: #0f172a;\">\n <input id=\"<%- optionId %>\" type=\"radio\" name=\"<%- groupName %>\" value=\"<%- item.value %>\" <%= index === 0 ? 'checked' : '' %> style=\"margin-top: 3px;\" />\n <span><%- item.label %></span>\n </label>\n <% }); %>\n</div>\n","Checkbox":"<div style=\"display: grid; gap: 10px;\">\n <% var groupName = inputName || 'checkbox_group'; %>\n <% items.forEach(function(item, index) { %>\n <% var optionId = groupName + '_' + index; %>\n <label for=\"<%- optionId %>\" style=\"display: flex; align-items: flex-start; gap: 10px; cursor: pointer; color: #0f172a;\">\n <input id=\"<%- optionId %>\" type=\"checkbox\" name=\"<%- groupName %>[]\" value=\"<%- item.value %>\" style=\"margin-top: 3px;\" />\n <span><%- item.label %></span>\n </label>\n <% }); %>\n</div>\n","List":"<ul style=\"list-style: disc; padding-left: 1.5em; margin: 0;\">\n<% items.forEach(function(item) { %>\n <li style=\"margin-bottom: 0.4em;\"><%- item %></li>\n<% }); %>\n</ul>\n","PricingCards":"<div data-pricing-cards-style=\"<%= (typeof style === 'string' && style) ? style : 'highlighted' %>\" style=\"<%- formatter.toStyleStringAll() %> display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); width: 100%; gap: <%= cell_gap %>px; align-items: <%= align_items || 'stretch' %>;\">\n <% cells.forEach(function(cell) { %>\n <%- cell %>\n <% }); %>\n</div>\n","PricingCard":"<%\n// PricingCard.template.html — 13-style card wrapper.\n// Context: style, highlighted, badge, icon, accent, renderIcon(name,size,color)\n// cell_index, cell_count (0-based + total, used by banded/tier)\n// + blocks, formatter, vertical_align, cell_gap, cell_count\n\nvar _style = (typeof style === 'string' && style) ? style : 'highlighted';\nvar _highlighted = !!highlighted;\nvar _badge = (typeof badge === 'string' && badge.trim()) ? badge.trim() : '';\nvar _icon = (typeof icon === 'string' && icon.trim()) ? icon.trim() : '';\nvar _accent = (typeof accent === 'string' && accent.trim()) ? accent.trim() : '#ffcc2a';\nvar _idx = (typeof cell_index === 'number' && cell_index >= 0) ? cell_index : 0;\nvar _count = (typeof cell_count === 'number' && cell_count > 0) ? cell_count : 1;\nvar _isFirst = (_idx === 0);\nvar _isLast = (_idx === _count - 1);\n\n// Inline SVG icon renderer — falls back to emoji passthrough for non-ASCII\n// input and first-letter circle for unknown names. See pricingCardIcons.js.\nvar _renderIcon = (typeof renderIcon === 'function') ? renderIcon : function () { return ''; };\n\nvar TOKENS = {\n minimal: { radius: '12px', pad: '24px 20px', shadow: '0 1px 2px rgba(15,23,42,0.04)', border: '1px solid #e5e7eb', bg: '#ffffff', color: '#111827' },\n highlighted: { radius: '12px', pad: '24px 20px', shadow: '0 1px 2px rgba(15,23,42,0.04)', border: '1px solid #e5e7eb', bg: '#ffffff', color: '#111827' },\n featured: { radius: '18px', pad: '36px 20px 28px', shadow: '0 16px 40px rgba(15,23,42,0.08)', border: '1px solid #f0d0c8', bg: '#ffffff', color: '#111827' },\n compact: { radius: '10px', pad: '18px 16px', shadow: '0 1px 2px rgba(15,23,42,0.04)', border: '1px solid #d1d5db', bg: '#ffffff', color: '#111827' },\n glass: { radius: '16px', pad: '28px 22px', shadow: '0 8px 32px rgba(49,46,129,0.12)', border: '1px solid rgba(255,255,255,0.5)', bg: 'rgba(255,255,255,0.65)', color: '#312e81' },\n pill: { radius: '24px', pad: '32px 22px', shadow: '0 2px 8px rgba(0,0,0,0.04)', border: '0', bg: '#ffffff', color: '#111827' },\n neon: { radius: '10px', pad: '24px 20px', shadow: '0 0 24px rgba(0,229,255,0.25)', border: '1px solid rgba(0,229,255,0.35)', bg: '#14141c', color: '#e5e7eb' },\n brutalist: { radius: '0', pad: '24px 20px', shadow: '8px 8px 0 0 #000', border: '2px solid #000', bg: '#ffffff', color: '#111827' },\n editorial: { radius: '2px', pad: '28px 22px', shadow: '0 2px 8px rgba(28,25,23,0.06)', border: '1px solid #d4d4aa', bg: '#ffffff', color: '#1c1917' },\n gradient: { radius: '14px', pad: '28px 22px', shadow: '0 12px 32px rgba(0,0,0,0.12)', border: '0', bg: 'linear-gradient(135deg,' + _accent + ',' + _accent + 'cc)', color: '#ffffff' },\n // Joined table — border-radius computed dynamically below (only outer\n // corners round, inner corners sharp). Inner vertical border creates\n // the divider between cells. Highlighted card gets a light-blue top\n // strip for the Mailchimp \"Best value\" label.\n banded: { radius: 'DYN', pad: '32px 28px', shadow: '0 1px 2px rgba(15,23,42,0.05)', border: 'DYN', bg: '#ffffff', color: '#1c1917' },\n // Staircase elevation — each card sits at a different margin-top so\n // the row of cards looks like steps climbing from index 0 → N-1.\n // Shadow grows with index to reinforce the climb.\n tier: { radius: '16px', pad: '28px 22px', shadow: 'DYN', border: '1px solid #e0e7ff', bg: '#ffffff', color: '#1e1b4b' },\n // Neumorphism — soft gray surface with dual-direction shadow (outer\n // bright + outer dark) for the \"pushed out\" look. Highlighted flips\n // to inset for \"pushed in\".\n neo: { radius: '22px', pad: '32px 26px', shadow: 'DYN', border: '0', bg: '#e9edf3', color: '#1e293b' }\n};\nvar T = TOKENS[_style] || TOKENS.highlighted;\n\n// Resolve dynamic tokens for banded / tier / neo.\nvar _extraStyles = ''; // injected into rootStyle list below (banded divider)\nif (_style === 'banded') {\n // Outer radius on first/last card only, 0 on inner corners. Middle\n // cards have no radius at all. Highlighted override handled\n // separately below.\n var _bRadius = '0';\n if (_count === 1) _bRadius = '12px';\n else if (_isFirst) _bRadius = '12px 0 0 12px';\n else if (_isLast) _bRadius = '0 12px 12px 0';\n T = Object.assign({}, T, {\n radius: _bRadius,\n border: '1px solid #e5e7eb',\n });\n // Drop the right border on non-last cards so the divider reads as a\n // single shared line between joined cells (otherwise adjacent 1px\n // borders stack visually to 2px). Done via separate extra style so\n // it composes cleanly with the cardBorder highlighted override below.\n if (!_isLast) _extraStyles += ' border-right: 0;';\n}\nif (_style === 'tier') {\n // Shadow ramp: index 0 = smallest, last = largest. Keeps the base\n // shadow subtle so stacks of 4 cards don't feel crushed.\n var _shadowStep = Math.round((_idx / Math.max(1, _count - 1)) * 20);\n T = Object.assign({}, T, {\n shadow: '0 ' + (4 + _shadowStep) + 'px ' + (16 + _shadowStep * 2) + 'px rgba(79,70,229,' + (0.06 + _idx * 0.02) + ')',\n });\n}\nif (_style === 'neo') {\n // Dual-direction neumorphism — LIGHT shadow on top-left, DARK on\n // bottom-right. Values tuned against #e9edf3 card bg.\n T = Object.assign({}, T, {\n shadow: _highlighted\n ? 'inset 8px 8px 16px #c7cdd6, inset -8px -8px 16px #ffffff'\n : '10px 10px 24px #c7cdd6, -10px -10px 24px #ffffff',\n });\n}\n\nvar cardBorder = _highlighted ? ('2px solid ' + _accent) : T.border;\nvar cardShadow = _highlighted ? ('0 16px 36px ' + _accent + '33, ' + T.shadow) : T.shadow;\n// No translateY on root — it broke featured ribbon's translateX anchor and\n// caused painted shadow/border to overlap across style switches. Apply lift\n// via a top margin-bottom offset that doesn't stack-context collide.\nvar cardMarginTop = (_style === 'highlighted' && _highlighted) ? '-6px' : '0';\nvar cardMarginBottom = (_style === 'highlighted' && _highlighted) ? '6px' : '0';\nif (_style === 'brutalist' && _highlighted) {\n cardBorder = '3px solid #000';\n cardShadow = '10px 10px 0 0 ' + _accent;\n // Flood the highlighted brutalist card with accent so white-on-white\n // text doesn't disappear — also matches the genre (solid accent =\n // \"the loud one\"). Override rootStyle's background token further down.\n T = Object.assign({}, T, { bg: _accent, color: '#ffffff' });\n}\nif (_style === 'neon' && _highlighted) {\n cardBorder = '2px solid ' + _accent;\n cardShadow = '0 0 36px ' + _accent + '66, inset 0 0 16px ' + _accent + '22';\n}\nif (_style === 'banded' && _highlighted) {\n // Subtle tinted background on highlighted Mailchimp-style cell;\n // card keeps its joined-row appearance (don't lift or add stroke\n // that would break the table).\n cardBorder = T.border;\n cardShadow = T.shadow;\n T = Object.assign({}, T, { bg: '#fffdf0' });\n}\nif (_style === 'tier') {\n // Staircase offset — first card sits LOWEST (largest top margin),\n // last card sits HIGHEST (zero top margin). Climbs left→right.\n var _tierStep = 12;\n var _fromBottom = (_count - 1 - _idx);\n cardMarginTop = (_fromBottom * _tierStep) + 'px';\n cardMarginBottom = (_idx * _tierStep) + 'px';\n if (_highlighted) {\n cardBorder = '2px solid ' + _accent;\n cardShadow = '0 18px 40px ' + _accent + '33';\n }\n}\nif (_style === 'neo' && _highlighted) {\n // Already set T.shadow to inset above; also add a subtle accent ring.\n cardBorder = '2px solid ' + _accent + '66';\n}\n\n// Width via grid — parent uses display: grid, so cells stretch naturally. Keep\n// height override for vertical sizing only.\nvar height = formatter.getFormat('height');\nvar heightStyle = '';\nif (height !== null && height !== '' && height !== undefined) {\n heightStyle = 'height: ' + height + (typeof height === 'number' ? 'px' : '') + ';';\n}\n\n// Other formats (user-customized bg/border/padding on the cell formatter itself)\nvar otherStyles = formatter.toStyleStringAll().replace(/width:\\s*[^;]+;?\\s*/gi, '').replace(/height:\\s*[^;]+;?\\s*/gi, '').trim();\n\nvar rootStyle = [\n 'position: relative',\n 'display: flex',\n 'flex-direction: column',\n 'gap: 8px',\n 'min-height: 100%',\n 'box-sizing: border-box',\n 'border: ' + cardBorder,\n 'border-radius: ' + T.radius,\n 'padding: ' + T.pad,\n 'background: ' + T.bg,\n 'box-shadow: ' + cardShadow,\n 'margin-top: ' + cardMarginTop,\n 'margin-bottom: ' + cardMarginBottom,\n 'color: ' + T.color,\n 'transition: box-shadow 150ms ease, border-color 150ms ease',\n 'overflow: visible',\n heightStyle,\n otherStyles\n].filter(function (s) { return s && s.length; }).join('; ') + ';' + _extraStyles;\n\nif (_style === 'glass') {\n rootStyle += ' backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);';\n}\n\n// ---- BADGE ----\nvar badgeHtml = '';\nif (_badge) {\n if (_style === 'featured') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"ribbon\" style=\"position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: ' + _accent + '; color: #ffffff; font-size: 10px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap; padding: 5px 12px; border-radius: 999px; white-space: nowrap; box-shadow: 0 6px 14px ' + _accent + '40; z-index: 2;\">' + _badge + '</div>';\n } else if (_style === 'highlighted' && _highlighted) {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"band\" style=\"margin: -' + T.pad.split(' ')[0] + ' -' + T.pad.split(' ')[1] + ' 4px; background: ' + _accent + '; color: #1a1a1a; font-size: 11px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; padding: 7px 10px; text-align: center; border-radius: ' + T.radius + ' ' + T.radius + ' 0 0;\">' + _badge + '</div>';\n } else if (_style === 'brutalist') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"blocky\" style=\"background: #000; color: #fff; font-weight: 900; font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; padding: 4px 8px; align-self: flex-start;\">' + _badge + '</div>';\n } else if (_style === 'neon') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"outline\" style=\"background: transparent; color: ' + _accent + '; font-weight: 700; font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase; white-space: nowrap; border: 1px solid ' + _accent + '; padding: 2px 8px; border-radius: 999px; align-self: flex-start; box-shadow: 0 0 12px ' + _accent + '44;\">' + _badge + '</div>';\n } else if (_style === 'editorial') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"inline-serif\" style=\"color: ' + _accent + '; font-family: Georgia, serif; font-size: 11px; font-weight: 700; font-style: italic; letter-spacing: 0.05em; align-self: flex-start; padding: 2px 0; border-bottom: 1px solid ' + _accent + ';\">' + _badge + '</div>';\n } else if (_style === 'gradient') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"frost\" style=\"background: rgba(255,255,255,0.22); color: #ffffff; font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; padding: 3px 9px; border-radius: 999px; align-self: flex-start;\">' + _badge + '</div>';\n } else if (_style === 'pill') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"center-pill\" style=\"background: ' + _accent + '; color: #ffffff; font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; padding: 3px 9px; border-radius: 999px; align-self: center;\">' + _badge + '</div>';\n } else if (_style === 'glass') {\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"glass-chip\" style=\"background: ' + _accent + '22; color: ' + _accent + '; font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; padding: 3px 9px; border-radius: 999px; align-self: flex-start;\">' + _badge + '</div>';\n } else if (_style === 'banded') {\n // Mailchimp-style \"Best value\" label — centered, underline, soft\n // blue tint strip that sits flush on top of the card (negative\n // margin matches the card's top padding so the strip fills the\n // full card width at the top edge).\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"banded-strip\" style=\"margin: -' + T.pad.split(' ')[0] + ' -' + T.pad.split(' ')[1] + ' 18px; background: #e8f1ff; color: #1e3a8a; font-family: Georgia, serif; font-style: italic; font-size: 13px; text-align: center; padding: 8px 12px; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px;\">' + _badge + '</div>';\n } else if (_style === 'tier') {\n // Tier badge — bold uppercase pill in the accent color, sits\n // above the heading. Quiet unless highlighted.\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"tier-pill\" style=\"background: ' + _accent + '; color: #ffffff; font-size: 10px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; padding: 4px 10px; border-radius: 6px; align-self: flex-start; box-shadow: 0 4px 10px ' + _accent + '55;\">' + _badge + '</div>';\n } else if (_style === 'neo') {\n // Neumorphic chip — inset soft shadow, no fill, accent text.\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"neo-chip\" style=\"background: #eef1f6; color: ' + _accent + '; font-size: 10px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap; padding: 5px 12px; border-radius: 999px; align-self: flex-start; box-shadow: inset 2px 2px 4px #c7cdd6, inset -2px -2px 4px #ffffff;\">' + _badge + '</div>';\n } else {\n // minimal + compact fallback\n badgeHtml = '<div data-role=\"pricing-badge\" data-variant=\"chip\" style=\"display: inline-flex; align-self: flex-start; background: ' + _accent + '22; color: ' + _accent + '; font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; padding: 3px 9px; border-radius: 999px;\">' + _badge + '</div>';\n }\n}\n\n// ---- ICON (inline SVG via registry) ----\nvar iconHtml = '';\nif (_icon) {\n if (_style === 'featured') {\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"circle\" style=\"align-self: center; width: 52px; height: 52px; border-radius: 50%; background: ' + _accent + '1c; color: ' + _accent + '; display: inline-flex; align-items: center; justify-content: center; margin: 2px 0 4px;\">' + _renderIcon(_icon, 26, _accent) + '</div>';\n } else if (_style === 'pill') {\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"stamp\" style=\"align-self: center; color: ' + _accent + '; margin-bottom: 6px;\">' + _renderIcon(_icon, 36, _accent) + '</div>';\n } else if (_style === 'compact') {\n iconHtml = ''; // compact hides icon\n } else if (_style === 'gradient') {\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"overlay\" style=\"align-self: flex-start; color: #ffffff; margin-bottom: 2px;\">' + _renderIcon(_icon, 22, '#ffffff') + '</div>';\n } else if (_style === 'neon') {\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"glow\" style=\"align-self: flex-start; color: ' + _accent + '; filter: drop-shadow(0 0 6px ' + _accent + '88); margin-bottom: 2px;\">' + _renderIcon(_icon, 22, _accent) + '</div>';\n } else if (_style === 'editorial') {\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"inline\" style=\"align-self: flex-start; color: ' + _accent + '; margin-bottom: 2px;\">' + _renderIcon(_icon, 20, _accent) + '</div>';\n } else if (_style === 'brutalist') {\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"stamp-hard\" style=\"align-self: flex-start; color: #000; margin-bottom: 2px;\">' + _renderIcon(_icon, 24, '#000') + '</div>';\n } else if (_style === 'banded') {\n // Subtle mono icon in serif-card context — small, aligned with\n // the serif heading below it.\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"banded-line\" style=\"align-self: flex-start; color: #44403c; margin-bottom: 4px;\">' + _renderIcon(_icon, 20, '#44403c') + '</div>';\n } else if (_style === 'tier') {\n // Accent-filled circle chip — feels like a \"rank medallion\" for\n // each tier.\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"tier-chip\" style=\"align-self: flex-start; width: 40px; height: 40px; border-radius: 12px; background: ' + _accent + '18; color: ' + _accent + '; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 6px;\">' + _renderIcon(_icon, 22, _accent) + '</div>';\n } else if (_style === 'neo') {\n // Neumorphic icon cell — soft inset square with accent glyph.\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"neo-cell\" style=\"align-self: flex-start; width: 44px; height: 44px; border-radius: 14px; background: #eef1f6; color: ' + _accent + '; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 6px; box-shadow: inset 3px 3px 6px #c7cdd6, inset -3px -3px 6px #ffffff;\">' + _renderIcon(_icon, 22, _accent) + '</div>';\n } else {\n // minimal + highlighted + glass fallback\n iconHtml = '<div data-role=\"pricing-icon\" data-variant=\"leading\" style=\"align-self: flex-start; color: ' + _accent + '; margin-bottom: 2px;\">' + _renderIcon(_icon, 22, _accent) + '</div>';\n }\n}\n%><section data-pricing-card data-style=\"<%= _style %>\" data-highlighted=\"<%= _highlighted ? '1' : '0' %>\" style=\"<%- rootStyle %>\"><%- badgeHtml %><%- iconHtml %><% if (blocks && blocks.length) { blocks.forEach(function (block) { %><%- block %><% }); } %></section>\n","Divider":"<div style=\"<%- formatter.toStyleStringAll(['padding_top', 'padding_right', 'padding_bottom', 'padding_left']) %>\">\n <div style=\"<%- formatter.toStyleStringAll(['background_color', 'height']) %><% if (formatter && formatter.getFormat && formatter.getFormat('divider_style')) { %><% var _h = formatter.getFormat('height') || 1; var _style = formatter.getFormat('divider_style'); var _bw = (_style === 'double') ? Math.max(4, _h*2) : _h; %> border-top: <%- _bw %>px <%- _style %> <%- formatter.getFormat('background_color') || '#000' %>; background-color: transparent; height: 0;<% } %><% if (formatter && formatter.getFormat && formatter.getFormat('divider_css')) { %><%- formatter.getFormat('divider_css') %><% } %>\"></div>\n</div>\n ","Field":"<% if (fieldType === 'text') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <input type=\"text\" name=\"<%- fieldName %>\" value=\"<%- fieldValue %>\" placeholder=\"<%- fieldPlaceholder %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n </div>\n\n<% } else if (fieldType === 'textarea') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <textarea name=\"<%- fieldName %>\" placeholder=\"<%- fieldPlaceholder %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %>><%- fieldValue %></textarea>\n </div>\n\n<% } else if (fieldType === 'number') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <input type=\"number\" name=\"<%- fieldName %>\" value=\"<%- fieldValue %>\" placeholder=\"<%- fieldPlaceholder %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n </div>\n\n<% } else if (fieldType === 'email') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <input type=\"email\" name=\"<%- fieldName %>\" value=\"<%- fieldValue %>\" placeholder=\"<%- fieldPlaceholder %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %><% if (emailErrorMessage) { %> data-msg-email=\"<%- emailErrorMessage %>\"<% } %> />\n </div>\n\n<% } else if (fieldType === 'date') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <input type=\"date\" name=\"<%- fieldName %>\" value=\"<%- fieldValue %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n </div>\n\n<% } else if (fieldType === 'datetime') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <input type=\"datetime-local\" name=\"<%- fieldName %>\" value=\"<%- fieldValue %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n </div>\n\n<% } else if (fieldType === 'dropdown') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <select name=\"<%- fieldName %>\" class=\"form-select\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %>>\n <% if (required && !fieldValue) { %>\n <option value=\"\"><%- fieldPlaceholder %></option>\n <% } %>\n <% fieldOptions.forEach(function(opt) { %>\n <option value=\"<%- opt.value %>\" <%= fieldValue == opt.value ? \"selected\" : \"\" %>><%- opt.text %></option>\n <% }); %>\n </select>\n </div>\n<% } else if (fieldType === 'multiselect') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <select name=\"<%- fieldName %>\" class=\"form-select\" multiple<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %>>\n <% if (required && !fieldValue) { %>\n <option value=\"\"><%- fieldPlaceholder %></option>\n <% } %>\n <% fieldOptions.forEach(function(opt) { %>\n <option value=\"<%- opt.value %>\" <%= fieldValue == opt.value ? \"selected\" : \"\" %>><%- opt.text %></option>\n <% }); %>\n </select>\n </div>\n\n<% } else if (fieldType === 'checkbox') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <% fieldOptions.forEach(function(opt, idx) { %>\n <div class=\"form-check\">\n <input class=\"form-check-input\" type=\"checkbox\" name=\"<%- fieldName %>[]\" value=\"<%- opt.value %>\" id=\"<%- fieldName %>_<%- opt.value %>\" <%= (Array.isArray(fieldValue) && fieldValue.includes(opt.value)) ? \"checked\" : \"\" %><% if (required && idx === 0) { %> required<% } %><% if (errorMessage && idx === 0) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n <label class=\"form-check-label\" for=\"<%- fieldName %>_<%- opt.value %>\"><%- opt.text %></label>\n </div>\n <% }); %>\n </div>\n\n<% } else if (fieldType === 'radio') { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <% fieldOptions.forEach(function(opt, idx) { %>\n <div class=\"form-check\">\n <input class=\"form-check-input\" type=\"radio\" name=\"<%- fieldName %>\" value=\"<%- opt.value %>\" id=\"<%- fieldName %>_<%- opt.value %>\" <%= (fieldValue == opt.value) ? \"checked\" : \"\" %><% if (required && idx === 0) { %> required<% } %><% if (errorMessage && idx === 0) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n <label class=\"form-check-label\" for=\"<%- fieldName %>_<%- opt.value %>\"><%- opt.text %></label>\n </div>\n <% }); %>\n </div>\n\n<% } else { %>\n <div class=\"\">\n <label class=\"form-label\" inline-edit=\"fieldLabel\"<% if (!showLabel) { %> style=\"display:none\" aria-hidden=\"true\"<% } %>><%- fieldLabel %></label><% if (required && requiredIndicator && showLabel) { %><span class=\"bjs-field-required-indicator\" style=\"display:inline-block;margin-left:7px;color:<%- requiredIndicatorColor %>;\"><%- requiredIndicator %></span><% } %>\n <input type=\"text\" name=\"<%- fieldName %>\" value=\"<%- fieldValue %>\" placeholder=\"<%- fieldPlaceholder %>\" class=\"form-control\"<% if (required) { %> required<% } %><% if (errorMessage) { %> data-msg-required=\"<%- errorMessage %>\"<% } %> />\n </div>\n<% } %>\n","SubmitButton":"\n<div style=\"text-align: <%- formatter.getFormat(\"align\", \"center\") %>;\">\n <button type=\"submit\" style=\"<%- formatter.toStyleStringAll() %>box-sizing: border-box;\n <% if (formatter && formatter.getFormat && formatter.getFormat('width')) { %>\n display:block;\n <% if (formatter.getFormat('align') === 'center') { %> margin-left: auto; margin-right: auto; <% } %>\n <% } else { %>\n display:inline-block;\n <% } %>\n text-decoration: none;\"\n inline-edit=\"text\"><%- text %>\n </button>\n</div>","TermsOfService":"<%\n var msgAttr = errorMessage ? ' data-msg-required=\"' + String(errorMessage).replace(/\"/g, '"') + '\"' : '';\n var requiredAttr = required ? ' required' : '';\n /* Structural flex styles are emitted INLINE because the builder canvas\n * iframe doesn't load the host's visitor-side form-popup stylesheet.\n * Class-based rules only apply on the visitor-side popup; inline styles\n * guarantee the same layout in both surfaces. */\n var rowStyle = 'display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px;margin:0;';\n /* `outline:none` suppresses Chrome/Edge's default contenteditable focus\n * ring around the legal-text host — BuilderJS already paints its own\n * selection chrome around the parent block, so the native ring just\n * stacks awkwardly. Visitor-side the host has no `contenteditable`\n * attribute (added at runtime by BuilderJS only inside the builder),\n * so the rule is a no-op on the public form. */\n /* `min-width:0` + `overflow-wrap:anywhere` lets the text shrink below\n * its natural longest-word width — without it, narrow viewports force\n * a flex-wrap that knocks the checkbox onto its own row in inline\n * layouts. With anywhere-wrap, min-content collapses to 1 character so\n * the input + text always share the row regardless of viewport width.\n * The validation error label still drops to a new row because IT has\n * an explicit `flex-basis:100%` rule that forces the wrap. */\n /* `flex: 1 1 0` (basis ZERO not auto) is critical: with `flex-basis:auto`\n * the text item's intrinsic basis equals its full content width — on\n * narrow viewports that exceeds container width and triggers a flex-\n * wrap BEFORE the browser tries to shrink the item, dropping the\n * checkbox onto its own row. With basis:0 the item starts at 0 width\n * and grows into available space (= container - input - gap), so the\n * sum of bases (input + 0) is always less than container — wrap never\n * kicks in for the inline children. The validation error label still\n * drops to a new row because IT has its own `flex-basis:100%` rule\n * that explicitly forces wrap.\n *\n * `overflow-wrap:anywhere` + `word-break:break-word` belt-and-suspenders\n * for ultra-narrow viewports where even a single long word wouldn't\n * fit — text breaks mid-word rather than overflowing horizontally. */\n var textStyle = 'flex:1 1 0;min-width:0;overflow-wrap:anywhere;word-break:break-word;outline:none;';\n var inputStyle = 'flex:0 0 auto;margin-top:3px;';\n var inputHtml = '<input type=\"checkbox\" name=\"' + inputName + '\" value=\"1\"' + requiredAttr + msgAttr + ' class=\"acm-terms__input\" style=\"' + inputStyle + '\" />';\n%>\n<% if (checkboxPosition === 'inline_left') { %>\n <div class=\"acm-terms acm-terms--inline acm-terms--inline-left\" style=\"<%- formatter.toStyleStringAll() %>;<%- rowStyle %>\">\n <%- inputHtml %>\n <div class=\"acm-terms__text\" inline-edit=\"termsText\" style=\"<%- textStyle %>\"><%- termsText %></div>\n </div>\n<% } else if (checkboxPosition === 'inline_right') { %>\n <div class=\"acm-terms acm-terms--inline acm-terms--inline-right\" style=\"<%- formatter.toStyleStringAll() %>;<%- rowStyle %>\">\n <div class=\"acm-terms__text\" inline-edit=\"termsText\" style=\"<%- textStyle %>\"><%- termsText %></div>\n <%- inputHtml %>\n </div>\n<% } else if (checkboxPosition === 'above') { %>\n <div class=\"acm-terms acm-terms--stacked acm-terms--above\" style=\"<%- formatter.toStyleStringAll() %>\">\n <label class=\"acm-terms__agree\" style=\"<%- rowStyle %>cursor:pointer;user-select:none;\">\n <%- inputHtml %>\n <span class=\"acm-terms__agree-label\" style=\"<%- textStyle %>line-height:1.45;\"><%- checkboxLabel %></span>\n </label>\n <div class=\"acm-terms__text\" inline-edit=\"termsText\" style=\"margin-top:6px;outline:none;\"><%- termsText %></div>\n </div>\n<% } else { %>\n <div class=\"acm-terms acm-terms--stacked acm-terms--below\" style=\"<%- formatter.toStyleStringAll() %>\">\n <div class=\"acm-terms__text\" inline-edit=\"termsText\" style=\"margin-bottom:6px;outline:none;\"><%- termsText %></div>\n <label class=\"acm-terms__agree\" style=\"<%- rowStyle %>cursor:pointer;user-select:none;\">\n <%- inputHtml %>\n <span class=\"acm-terms__agree-label\" style=\"<%- textStyle %>line-height:1.45;\"><%- checkboxLabel %></span>\n </label>\n </div>\n<% } %>\n","Checkout":"<style>\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .checkout-form {\n background: #fff;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .checkout-header {\n padding: 16px 24px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n color: #666;\n }\n\n .checkout-header-icon {\n width: 20px;\n height: 20px;\n }\n\n .section {\n padding: 32px 24px;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .section:last-child {\n border-bottom: none;\n }\n\n .section-title {\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n margin-bottom: 20px;\n letter-spacing: 0.3px;\n }\n\n .form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .form-row.full-width {\n grid-template-columns: 1fr;\n }\n\n .form-group {\n position: relative;\n }\n\n .form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: #555;\n margin-bottom: 8px;\n }\n\n .form-label .optional {\n color: #999;\n font-weight: 400;\n }\n\n .form-input,\n .form-select {\n width: 100%;\n padding: 14px 16px;\n border: 1.5px solid #c5c5c5;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n color: #333;\n background: #fff;\n transition: all 0.2s ease;\n outline: none;\n appearance: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n }\n\n .form-select {\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23333' d='M1 1l5 5 5-5'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 16px center;\n padding-right: 40px;\n }\n\n .form-input:focus,\n .form-select:focus {\n border-color: #4a90e2;\n box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);\n }\n\n .form-input::placeholder {\n color: #aaa;\n }\n\n .phone-input-wrapper {\n display: flex;\n gap: 8px;\n }\n\n .phone-flag {\n width: 70px;\n flex-shrink: 0;\n padding: 12px 14px;\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n background: #fff;\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .phone-flag:hover {\n border-color: #4a90e2;\n }\n\n .phone-flag img {\n width: 20px;\n height: 14px;\n object-fit: cover;\n }\n\n .phone-flag-arrow {\n font-size: 10px;\n color: #666;\n margin-left: auto;\n }\n\n .phone-input {\n flex: 1;\n }\n\n .product-list {\n margin-bottom: 24px;\n }\n\n .product-item {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 16px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n background: #fafafa;\n }\n\n .product-radio {\n width: 20px;\n height: 20px;\n accent-color: #4a90e2;\n cursor: pointer;\n }\n\n .product-name {\n flex: 1;\n font-size: 14px;\n font-weight: 500;\n color: #333;\n }\n\n .product-price {\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .delivery-info {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 24px;\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n margin-bottom: 24px;\n }\n\n .delivery-icon {\n width: 32px;\n height: 32px;\n color: #666;\n }\n\n .delivery-text {\n font-size: 13px;\n color: #666;\n }\n\n .checkbox-group {\n display: flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n }\n\n .checkbox-input {\n width: 18px;\n height: 18px;\n accent-color: #4a90e2;\n cursor: pointer;\n }\n\n .checkbox-label {\n font-size: 14px;\n font-weight: 500;\n color: #333;\n cursor: pointer;\n user-select: none;\n }\n\n .payment-summary {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n background: #f8f9fa;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .payment-summary:hover {\n background: #f0f1f2;\n }\n\n .payment-icon {\n width: 24px;\n height: 24px;\n color: #333;\n }\n\n .payment-title {\n flex: 1;\n font-size: 15px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .payment-cta {\n font-size: 13px;\n color: #4a90e2;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .payment-arrow {\n font-size: 10px;\n }\n\n .submit-button {\n width: 100%;\n margin-top: 20px;\n padding: 14px 16px;\n border: none;\n border-radius: 6px;\n background: linear-gradient(135deg, #4a90e2, #3a7acb);\n color: #fff;\n font-size: 15px;\n font-weight: 700;\n letter-spacing: 0.2px;\n cursor: pointer;\n box-shadow: 0 6px 18px rgba(58, 122, 203, 0.25);\n transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;\n }\n\n .submit-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 8px 22px rgba(58, 122, 203, 0.3);\n }\n\n .submit-button:active {\n transform: translateY(0);\n box-shadow: 0 4px 12px rgba(58, 122, 203, 0.2);\n filter: brightness(0.96);\n }\n\n .submit-button:focus-visible {\n outline: 3px solid rgba(74, 144, 226, 0.35);\n outline-offset: 2px;\n }\n\n .footer-notice {\n text-align: center;\n padding: 24px;\n font-size: 12px;\n color: #999;\n background: #f8f9fa;\n }\n</style>\n\n<form action=\"<%= action %>\" method=\"<%= method %>\">\n <div class=\"checkout-container\" style=\"<%- formatter.toStyleStringAll() %>\">\n <div class=\"checkout-form\">\n <!-- Header -->\n <div class=\"checkout-header\">\n <svg class=\"checkout-header-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z\"/>\n </svg>\n <span><%- values.headerTitle %></span>\n </div>\n\n <!-- Contact Information Section -->\n <section class=\"section\">\n <h2 class=\"section-title\"><%- values.headlines.contact %></h2>\n \n <div class=\"form-row\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.firstName %></label>\n <input type=\"text\" name=\"first_name\" class=\"form-input\" placeholder=\"<%- values.contactLabels.firstName %>\">\n </div>\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.lastName %></label>\n <input type=\"text\" name=\"last_name\" class=\"form-input\" placeholder=\"<%- values.contactLabels.lastName %>\">\n </div>\n </div>\n \n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.email %></label>\n <input type=\"email\" name=\"email\" class=\"form-input\" placeholder=\"<%- values.contactLabels.email %>\">\n </div>\n </div>\n \n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.phone %></label>\n <div class=\"phone-input-wrapper\">\n <div class=\"phone-flag\">\n <img src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 20'%3E%3Crect fill='%23da251d' width='30' height='20'/%3E%3Cpolygon fill='%23ff0' points='15,4 11.47,14.85 20.71,8.15 9.29,8.15 18.53,14.85'/%3E%3C/svg%3E\" alt=\"VN\">\n <span class=\"phone-flag-arrow\">▼</span>\n </div>\n <input type=\"tel\" name=\"phone\" class=\"form-input phone-input\" placeholder=\"<%- values.contactLabels.phone %>\">\n </div>\n </div>\n </div>\n </section>\n\n <!-- Billing Information Section -->\n <section class=\"section\">\n <h2 class=\"section-title\"><%- values.headlines.billing %></h2>\n \n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.address %></label>\n <input type=\"text\" name=\"billing_address\" class=\"form-input\" placeholder=\"<%- values.contactLabels.address %>\">\n </div>\n </div>\n\n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.apartment %> <span class=\"optional\"><%- values.contactLabels.apartmentOptional %></span></label>\n <input type=\"text\" name=\"billing_apartment\" class=\"form-input\" placeholder=\"<%- values.contactLabels.apartment %> <%- values.contactLabels.apartmentOptional %>\">\n </div>\n </div>\n\n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.country %></label>\n <select name=\"billing_country_id\" class=\"form-select\">\n <option value=\"vietnam\"><%- values.defaults.country %></option>\n <option value=\"us\">United States</option>\n <option value=\"uk\">United Kingdom</option>\n </select>\n </div>\n </div>\n\n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.state %></label>\n <select name=\"billing_state\" class=\"form-select\">\n <option value=\"an-giang\"><%- values.defaults.state %></option>\n <option value=\"hanoi\">Hanoi</option>\n <option value=\"hcm\">Ho Chi Minh</option>\n </select>\n </div>\n </div>\n\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.city %></label>\n <input type=\"text\" name=\"billing_city\" class=\"form-input\" placeholder=\"<%- values.contactLabels.city %>\">\n </div>\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.postal %></label>\n <input type=\"text\" name=\"billing_postal\" class=\"form-input\" placeholder=\"<%- values.contactLabels.postal %>\">\n </div>\n </div>\n </section>\n\n <!-- Payment Information Section -->\n <section class=\"section\">\n <h2 class=\"section-title\"><%- values.headlines.payment %></h2>\n \n <div class=\"payment-summary\">\n <svg class=\"payment-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"1\" y=\"4\" width=\"22\" height=\"16\" rx=\"2\" ry=\"2\"/>\n <line x=\"1\" y1=\"10\" x2=\"23\" y2=\"10\"/>\n </svg>\n <span class=\"payment-title\"><%- values.paymentSummary.title %></span>\n <div class=\"payment-cta\">\n <span><%- values.paymentSummary.cta %></span>\n <svg class=\"payment-arrow\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"6 9 12 15 18 9\"/>\n </svg>\n </div>\n </div>\n\n <button type=\"submit\" class=\"submit-button\"><%- values.buttonTexts.guestSubmit %></button>\n </section>\n\n <!-- Footer Notice -->\n <div class=\"footer-notice\">\n <%- values.headlines.privacy %>\n </div>\n </div>\n </div>\n</form>","CheckoutControl":"<div class=\"bjs-checkout-control bjs-control-stack\">\n <!-- UI Settings -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">UI Settings</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Preview Label</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.ui.previewLabel %>\"\n data-path=\"ui.previewLabel\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Mode Label</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.ui.modeLabel %>\"\n data-path=\"ui.modeLabel\">\n </label>\n </section>\n\n <!-- Header -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Header</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Header Title</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headerTitle %>\"\n data-path=\"headerTitle\">\n </label>\n </section>\n\n <!-- Button Texts -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Button Texts</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Guest Submit Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.buttonTexts.guestSubmit %>\"\n data-path=\"buttonTexts.guestSubmit\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Saved Button Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.buttonTexts.saved %>\"\n data-path=\"buttonTexts.saved\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Upgrade/Downgrade Button Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.buttonTexts.upgrade %>\"\n data-path=\"buttonTexts.upgrade\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Submitting Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.buttonTexts.submitting %>\"\n data-path=\"buttonTexts.submitting\">\n </label>\n </section>\n\n <!-- Section Headlines -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Section Headlines</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Contact Information Headline</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headlines.contact %>\"\n data-path=\"headlines.contact\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Billing Information Headline</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headlines.billing %>\"\n data-path=\"headlines.billing\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Payment Information Headline</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headlines.payment %>\"\n data-path=\"headlines.payment\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Privacy Notice</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headlines.privacy %>\"\n data-path=\"headlines.privacy\">\n </label>\n </section>\n\n <!-- Contact Input Labels -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Contact Input Labels</div>\n\n <div class=\"bjs-checkout-grid-2\">\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">First Name</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.firstName %>\"\n data-path=\"contactLabels.firstName\">\n </label>\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Last Name</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.lastName %>\"\n data-path=\"contactLabels.lastName\">\n </label>\n </div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Email</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.email %>\"\n data-path=\"contactLabels.email\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Phone Number</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.phone %>\"\n data-path=\"contactLabels.phone\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Address</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.address %>\"\n data-path=\"contactLabels.address\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Apartment/Building</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.apartment %>\"\n data-path=\"contactLabels.apartment\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Optional Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.apartmentOptional %>\"\n data-path=\"contactLabels.apartmentOptional\">\n </label>\n\n <div class=\"bjs-checkout-grid-2\">\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Country</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.country %>\"\n data-path=\"contactLabels.country\">\n </label>\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">State</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.state %>\"\n data-path=\"contactLabels.state\">\n </label>\n </div>\n\n <div class=\"bjs-checkout-grid-2\">\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">City</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.city %>\"\n data-path=\"contactLabels.city\">\n </label>\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Postal Code</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.postal %>\"\n data-path=\"contactLabels.postal\">\n </label>\n </div>\n </section>\n\n <!-- Default Values -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Default Values</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Default Country</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.defaults.country %>\"\n data-path=\"defaults.country\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Default State</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.defaults.state %>\"\n data-path=\"defaults.state\">\n </label>\n </section>\n\n <!-- Payment Summary -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Payment Summary</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Summary Title</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.paymentSummary.title %>\"\n data-path=\"paymentSummary.title\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Summary CTA</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.paymentSummary.cta %>\"\n data-path=\"paymentSummary.cta\">\n </label>\n </section>\n\n <!-- Colors -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Colors</div>\n\n <div class=\"bjs-checkout-color-row\">\n <span class=\"bjs-checkout-field-label\">Text Color</span>\n <label class=\"bjs-checkout-color-chip\" style=\"--bjs-color-current: <%= values.colors && values.colors.text ? values.colors.text : '#ffffff' %>\">\n <input type=\"color\" class=\"bjs-checkout-color-native\"\n value=\"<%= values.colors && values.colors.text ? values.colors.text : '#ffffff' %>\"\n data-path=\"colors.text\">\n <span class=\"bjs-checkout-color-preview\"></span>\n </label>\n </div>\n\n <div class=\"bjs-checkout-color-row\">\n <span class=\"bjs-checkout-field-label\">Link Color</span>\n <label class=\"bjs-checkout-color-chip\" style=\"--bjs-color-current: <%= values.colors && values.colors.link ? values.colors.link : '#ffffff' %>\">\n <input type=\"color\" class=\"bjs-checkout-color-native\"\n value=\"<%= values.colors && values.colors.link ? values.colors.link : '#ffffff' %>\"\n data-path=\"colors.link\">\n <span class=\"bjs-checkout-color-preview\"></span>\n </label>\n </div>\n </section>\n</div>\n","CheckoutSimple":"<style>\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n .checkout-container {\n max-width: 800px;\n margin: 0 auto;\n padding: 20px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n color: #333;\n background: #f5f5f5;\n }\n\n .checkout-form {\n background: #fff;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .checkout-header {\n padding: 16px 24px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n color: #666;\n }\n\n .checkout-header-icon {\n width: 20px;\n height: 20px;\n }\n\n .section {\n padding: 32px 24px;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .section:last-child {\n border-bottom: none;\n }\n\n .section-title {\n font-size: 16px;\n font-weight: 600;\n color: #1a1a1a;\n margin-bottom: 20px;\n letter-spacing: 0.3px;\n }\n\n .form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n .form-row.full-width {\n grid-template-columns: 1fr;\n }\n\n .form-group {\n position: relative;\n }\n\n .form-label {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: #555;\n margin-bottom: 8px;\n }\n\n .form-input {\n width: 100%;\n padding: 14px 16px;\n border: 1.5px solid #c5c5c5;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n color: #333;\n background: #fff;\n transition: all 0.2s ease;\n outline: none;\n appearance: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n }\n\n .form-input:focus {\n border-color: #4a90e2;\n box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);\n }\n\n .form-input::placeholder {\n color: #aaa;\n }\n\n .phone-input-wrapper {\n display: flex;\n gap: 8px;\n }\n\n .phone-flag {\n width: 70px;\n flex-shrink: 0;\n padding: 12px 14px;\n border: 1px solid #d0d0d0;\n border-radius: 4px;\n background: #fff;\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .phone-flag:hover {\n border-color: #4a90e2;\n }\n\n .phone-flag img {\n width: 20px;\n height: 14px;\n object-fit: cover;\n }\n\n .phone-flag-arrow {\n font-size: 10px;\n color: #666;\n margin-left: auto;\n }\n\n .phone-input {\n flex: 1;\n }\n\n .footer-notice {\n text-align: center;\n padding: 24px;\n font-size: 12px;\n color: #999;\n background: #f8f9fa;\n }\n\n .submit-button {\n width: 100%;\n padding: 16px;\n font-size: 16px;\n font-weight: 600;\n color: #fff;\n background: #4a90e2;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-top: 24px;\n }\n\n .submit-button:hover {\n background: #357abd;\n }\n\n .submit-button:active {\n transform: translateY(1px);\n }\n</style>\n\n<div class=\"checkout-container\">\n <div class=\"checkout-form\">\n <!-- Header -->\n <div class=\"checkout-header\">\n <svg class=\"checkout-header-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z\"/>\n </svg>\n <span><%- values.headerTitle %></span>\n </div>\n\n <!-- Contact Information Section -->\n <section class=\"section\">\n <h2 class=\"section-title\"><%- values.headlines.contact %></h2>\n \n <div class=\"form-row\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.firstName %></label>\n <input type=\"text\" class=\"form-input\" placeholder=\"<%- values.contactLabels.firstName %>\">\n </div>\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.lastName %></label>\n <input type=\"text\" class=\"form-input\" placeholder=\"<%- values.contactLabels.lastName %>\">\n </div>\n </div>\n\n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.email %></label>\n <input type=\"email\" class=\"form-input\" placeholder=\"<%- values.contactLabels.email %>\">\n </div>\n </div>\n\n <div class=\"form-row full-width\">\n <div class=\"form-group\">\n <label class=\"form-label\"><%- values.contactLabels.phone %></label>\n <div class=\"phone-input-wrapper\">\n <div class=\"phone-flag\">\n <img src=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 20'%3E%3Crect fill='%23da251d' width='30' height='20'/%3E%3Cpolygon fill='%23ff0' points='15,4 11.47,14.85 20.71,8.15 9.29,8.15 18.53,14.85'/%3E%3C/svg%3E\" alt=\"VN\">\n <span class=\"phone-flag-arrow\">▼</span>\n </div>\n <input type=\"tel\" class=\"form-input phone-input\" placeholder=\"<%- values.contactLabels.phone %>\">\n </div>\n </div>\n </div>\n\n <button class=\"submit-button\"><%- values.buttonTexts.submit %></button>\n </section>\n\n <!-- Footer Notice -->\n <div class=\"footer-notice\">\n <%- values.headlines.privacy %>\n </div>\n </div>\n</div>\n","CheckoutSimpleControl":"<div class=\"bjs-checkout-control bjs-control-stack\">\n <!-- UI Settings -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">UI Settings</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Preview Label</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.ui.previewLabel %>\"\n data-path=\"ui.previewLabel\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Mode Label</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.ui.modeLabel %>\"\n data-path=\"ui.modeLabel\">\n </label>\n </section>\n\n <!-- Header -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Header</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Header Title</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headerTitle %>\"\n data-path=\"headerTitle\">\n </label>\n </section>\n\n <!-- Button Texts -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Button Texts</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Submit Button Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.buttonTexts.submit %>\"\n data-path=\"buttonTexts.submit\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Submitting Text</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.buttonTexts.submitting %>\"\n data-path=\"buttonTexts.submitting\">\n </label>\n </section>\n\n <!-- Section Headlines -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Section Headlines</div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Contact Information Headline</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headlines.contact %>\"\n data-path=\"headlines.contact\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Privacy Notice</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.headlines.privacy %>\"\n data-path=\"headlines.privacy\">\n </label>\n </section>\n\n <!-- Contact Input Labels -->\n <section class=\"bjs-checkout-section\">\n <div class=\"bjs-checkout-section-title\">Contact Input Labels</div>\n\n <div class=\"bjs-checkout-grid-2\">\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">First Name</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.firstName %>\"\n data-path=\"contactLabels.firstName\">\n </label>\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Last Name</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.lastName %>\"\n data-path=\"contactLabels.lastName\">\n </label>\n </div>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Email</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.email %>\"\n data-path=\"contactLabels.email\">\n </label>\n\n <label class=\"bjs-checkout-field\">\n <span class=\"bjs-checkout-field-label\">Phone Number</span>\n <input type=\"text\" class=\"bjs-text-input\"\n value=\"<%= values.contactLabels.phone %>\"\n data-path=\"contactLabels.phone\">\n </label>\n </section>\n</div>\n","FormContainerCell":"<div style=\"<%\n var width = formatter.getFormat('width', 'auto');\n var gap = (typeof cell_gap === 'number' && cell_gap > 0) ? cell_gap : 0;\n var count = (typeof cell_count === 'number' && cell_count > 0) ? cell_count : 1;\n var flexStyle = '';\n if (width === 'auto' || width === null || width === '') {\n flexStyle = '';\n } else if (width === 'fill') {\n flexStyle = 'flex: 1 1 0; min-width: 0;';\n } else {\n var str = String(width).trim();\n if (/%$/.test(str)) {\n var pct = parseFloat(str);\n if (!isNaN(pct) && pct > 0) {\n if (gap > 0 && count > 1) {\n var gapShare = (gap * (count - 1) / count);\n flexStyle = 'flex: 0 0 calc(' + pct + '% - ' + gapShare + 'px); min-width: 0;';\n } else {\n flexStyle = 'flex: 0 0 ' + pct + '%; min-width: 0;';\n }\n } else {\n flexStyle = 'flex: 1 1 0; min-width: 0;';\n }\n } else if (/(px|em|rem|vw|vh|ch|ex)$/i.test(str)) {\n flexStyle = 'flex: 0 0 ' + str + '; min-width: 0;';\n } else {\n var numWidth = parseFloat(str);\n if (!isNaN(numWidth) && numWidth > 0) {\n flexStyle = 'flex: ' + numWidth + ' 1 0; min-width: 0;';\n } else {\n flexStyle = 'flex: 1 1 0; min-width: 0;';\n }\n }\n }\n\n var height = formatter.getFormat('height');\n var heightStyle = '';\n if (height !== null && height !== '' && height !== undefined) {\n heightStyle = 'height: ' + height + (typeof height === 'number' ? 'px' : '') + ';';\n }\n\n var otherStyles = formatter.toStyleStringAll().replace(/width:\\s*[^;]+;?\\s*/gi, '').replace(/height:\\s*[^;]+;?\\s*/gi, '').trim();\n var finalStyle = (flexStyle + ' ' + heightStyle + ' ' + otherStyles).trim();\n%><%- finalStyle %>\">\n <% if (blocks && blocks.length) { blocks.forEach(function(block) { %>\n <%- block %>\n <% }); } %>\n</div>\n"};
window.THEME_CONFIG_DATA = {"name":"default","title":"Default Theme","description":"ACM Default Theme","pages":["Page","Form"],"templates":["Block","Grid","Cell","P","Link","H1","H2","H3","H4","H5","Heading","Image","Label","Button","Alert","RSS","Youtube","Menu","HTML","Video","SocialIcons","TextInput","Select","Radio","Checkbox","List","PricingCards","PricingCard","Divider","Field","SubmitButton","TermsOfService","Checkout","CheckoutControl","CheckoutSimple","CheckoutSimpleControl","FormContainerCell"]};
window.MEDIA_URL = "../../../themes/default";
</script>
<script src="../../../dist/builder.js"></script>
<script>
// Four arguments, four named ingredients above — IDENTICAL to snippet.php.
const builder = new Builder({
mainContainer: '#MyCanvas',
widgetsContainer: '#MyWidgets',
settingsContainer: '#MySettings',
});
builder.load(THEME_JSON, THEME_TEMPLATES, THEME_CONFIG_DATA, MEDIA_URL);
window.builder = builder; // exposes for browser-console + legacy globals
// Two read methods, no backend needed.
document.getElementById('myGetHtml').addEventListener('click', () => {
alert(builder.getHtml());
});
document.getElementById('myGetJson').addEventListener('click', () => {
alert(JSON.stringify(builder.getData(), null, 2));
});
</script>
</body>
</html>