{
  "version": "3.2",
  "source": "588 Elementor Template Kits \u2014 254,465 widgets across 10,624 JSON files + real-world conversion learnings from client landing pages (Bodystyle Fitness, Hall Brothers, Hypnotherapy, AMWL)",
  "generated": "2026-03-24",
  "stats": "19 section types, 48 builder methods, 29 layout variants",
  "widget_reference": {
    "_note": "Every setting uses Elementor's standard value types: dimension({unit,size,sizes}), spacing({unit,top,right,bottom,left,isLinked}), media({url,id,alt}), icon({value,library}). Responsive overrides: append _mobile or _tablet to any setting key.",
    "_value_types": {
      "dimension": {
        "unit": "px",
        "size": 20,
        "sizes": []
      },
      "spacing": {
        "unit": "px",
        "top": "10",
        "right": "10",
        "bottom": "10",
        "left": "10",
        "isLinked": true
      },
      "media": {
        "url": "https://...",
        "id": "",
        "alt": "description"
      },
      "icon": {
        "value": "fas fa-icon-name",
        "library": "fa-solid"
      },
      "link": {
        "url": "https://...",
        "is_external": false,
        "nofollow": false
      }
    },
    "heading": {
      "count": 73470,
      "use_for": "Section titles, hero headlines, any text heading",
      "key_settings": {
        "title": "string \u2014 the heading text",
        "header_size": "h1|h2|h3|h4|h5|h6|div|span|p",
        "align": "left|center|right",
        "title_color": "hex color",
        "typography_typography": "'custom' to enable font settings",
        "typography_font_family": "font name string",
        "typography_font_weight": "100-900 string",
        "typography_font_size": "dimension",
        "typography_font_size_mobile": "dimension (responsive)",
        "typography_letter_spacing": "dimension",
        "typography_line_height": "dimension (unit: em)",
        "typography_text_transform": "uppercase|lowercase|capitalize|none"
      },
      "helper": "heading_w($cfg, $text, $tag, $align, $color, $size, $weight, $letter_spacing, $line_height, $transform, $size_mobile)"
    },
    "text-editor": {
      "count": 30517,
      "use_for": "Body text, rich HTML content, inline-styled paragraphs",
      "key_settings": {
        "editor": "HTML string \u2014 supports <strong>, <em>, <span style=...>, etc.",
        "align": "left|center|right|justify",
        "align_mobile": "responsive alignment override",
        "text_color": "hex color",
        "typography_typography": "'custom'",
        "typography_font_family": "font name",
        "typography_font_size": "dimension",
        "typography_font_weight": "string",
        "typography_line_height": "dimension (unit: em)"
      },
      "helper": "text_w($cfg, $html, $align, $color, $size)"
    },
    "image": {
      "count": 29416,
      "use_for": "Photos, screenshots, illustrations, logos",
      "key_settings": {
        "image": "media object {url, id, alt, source: 'library'}",
        "image_size": "full|custom|medium|large|thumbnail",
        "image_custom_dimension": "{width, height} \u2014 only when image_size='custom'",
        "align": "left|center|right",
        "width": "dimension (% or px) \u2014 image display width",
        "height": "dimension \u2014 image display height",
        "object-fit": "cover|contain|fill",
        "image_border_radius": "spacing \u2014 rounded corners",
        "image_box_shadow_box_shadow_type": "'yes' to enable shadow",
        "image_box_shadow_box_shadow": "{horizontal, vertical, blur, spread, color}",
        "link": "link object \u2014 makes image clickable"
      },
      "helper": "image_w($url, $alt, $width, $radius, $shadow, $align)"
    },
    "button": {
      "count": 16906,
      "use_for": "CTAs, links, form submits",
      "key_settings": {
        "text": "button label string",
        "link": "link object {url, is_external, nofollow}",
        "align": "left|center|right",
        "size": "xs|sm|md|lg|xl",
        "selected_icon": "icon object \u2014 adds icon to button",
        "icon_align": "left|right",
        "icon_indent": "dimension \u2014 spacing between icon and text",
        "background_color": "hex \u2014 button bg color",
        "button_text_color": "hex \u2014 button text color",
        "border_border": "solid|dashed|dotted|none",
        "border_width": "spacing \u2014 border thickness",
        "border_color": "hex \u2014 border color",
        "border_radius": "spacing \u2014 rounded corners",
        "text_padding": "spacing \u2014 inner padding",
        "typography_typography": "'custom'",
        "typography_font_family": "font name",
        "typography_font_weight": "string",
        "typography_font_size": "dimension"
      },
      "helper": "btn_w($cfg, $text, $url, $bg, $text_color, $border_color, $icon, $align)"
    },
    "icon-box": {
      "count": 13674,
      "use_for": "Feature cards, service lists, info boxes \u2014 REPLACES separate icon+heading+text combo",
      "key_settings": {
        "selected_icon": "icon object",
        "title_text": "string \u2014 box title",
        "description_text": "string \u2014 box description",
        "position": "top|left|right \u2014 icon position relative to text",
        "text_align": "left|center|right",
        "view": "default|stacked|framed \u2014 icon display style",
        "shape": "circle|square \u2014 when view is stacked/framed",
        "primary_color": "hex \u2014 icon color (or bg color when stacked)",
        "secondary_color": "hex \u2014 bg color (stacked) or border color (framed)",
        "title_size": "h1-h6|div|p|span",
        "icon_size": "dimension",
        "icon_space": "dimension \u2014 gap between icon and text",
        "title_bottom_space": "dimension \u2014 gap between title and description",
        "title_color": "hex",
        "description_color": "hex",
        "title_typography_typography": "'custom'",
        "title_typography_font_family": "font name",
        "title_typography_font_weight": "string",
        "title_typography_font_size": "dimension",
        "description_typography_typography": "'custom'",
        "description_typography_font_family": "font name",
        "description_typography_font_size": "dimension",
        "description_typography_line_height": "dimension"
      },
      "helper": "icon_box_w($cfg, $icon, $title, $desc, $icon_color, $position, $view, $shape, $secondary_color, $align)"
    },
    "icon-list": {
      "count": 8940,
      "use_for": "Bullet points with icons \u2014 checkmarks, X marks, feature lists, benefit lists",
      "key_settings": {
        "icon_list": "array of {text, selected_icon: {value, library}, _id}",
        "space_between": "dimension \u2014 gap between items",
        "icon_color": "hex \u2014 icon color for all items",
        "icon_size": "dimension \u2014 icon pixel size",
        "text_color": "hex \u2014 text color for all items",
        "text_indent": "dimension \u2014 gap between icon and text",
        "typography_typography": "'custom'",
        "typography_font_family": "font name",
        "typography_font_size": "dimension",
        "typography_font_size_mobile": "dimension (responsive)",
        "typography_line_height": "dimension (unit: em)"
      },
      "helper": "icon_list_w() \u2014 NEEDED, not yet built",
      "patterns": {
        "benefit_checklist": {
          "icon": "fas fa-check",
          "icon_color": "#4CAF50",
          "text_color": "white or dark depending on bg",
          "use": "Hero bullet points, competitive edge benefits"
        },
        "negative_checklist": {
          "icon": "fas fa-times",
          "icon_color": "#971B2F",
          "text_color": "rgba(255,255,255,0.5) \u2014 faded to show these are the bad things",
          "use": "Competitor negatives in comparison sections"
        }
      }
    },
    "icon": {
      "count": 5883,
      "use_for": "Standalone icons, decorative elements, small indicators",
      "key_settings": {
        "selected_icon": "icon object",
        "view": "default|stacked|framed",
        "shape": "circle|square",
        "primary_color": "hex \u2014 icon color",
        "secondary_color": "hex \u2014 background (stacked) or border (framed)",
        "size": "dimension \u2014 icon size",
        "align": "left|center|right"
      },
      "helper": "icon_w($icon_class, $color, $size, $view, $shape, $secondary_color)"
    },
    "image-box": {
      "count": 5466,
      "use_for": "Team members, service cards with photos, portfolio items",
      "key_settings": {
        "image": "media object {url, id, alt}",
        "title_text": "string",
        "description_text": "string",
        "position": "top|left|right \u2014 image position",
        "text_align": "left|center|right",
        "title_size": "h1-h6|div|p",
        "image_size": "dimension \u2014 image display size",
        "image_space": "dimension \u2014 gap between image and text",
        "image_border_radius": "dimension \u2014 rounded corners",
        "title_bottom_space": "dimension",
        "title_color": "hex",
        "description_color": "hex"
      },
      "helper": "image_box_w($cfg, $img_url, $title, $desc, $align, $img_size, $position)"
    },
    "counter": {
      "count": 3594,
      "use_for": "Statistics, metrics, achievement numbers with animation",
      "key_settings": {
        "starting_number": "integer \u2014 animation start (usually 0)",
        "ending_number": "integer \u2014 target number",
        "prefix": "string \u2014 text before number (e.g. '$')",
        "suffix": "string \u2014 text after number (e.g. '+')",
        "duration": "integer \u2014 animation duration in ms",
        "thousand_separator": "yes|no",
        "thousand_separator_char": "','",
        "title": "string \u2014 label below number",
        "number_color": "hex",
        "title_color": "hex",
        "typography_typography": "'custom' \u2014 for number font",
        "typography_font_family": "font name",
        "typography_font_weight": "string",
        "typography_font_size": "dimension",
        "title_typography_typography": "'custom' \u2014 for label font",
        "title_typography_font_family": "font name",
        "title_typography_font_size": "dimension"
      },
      "helper": "Used directly via PressGo_Element_Factory::widget('counter', ...)"
    },
    "social-icons": {
      "count": 3261,
      "use_for": "Social media links in footer, about sections, team cards",
      "key_settings": {
        "social_icon_list": "array of {social_icon: icon_object, link: link_object, item_icon_color, _id}",
        "icon_size": "dimension",
        "icon_color": "custom|default",
        "icon_primary_color": "hex \u2014 icon color when icon_color='custom'",
        "shape": "circle|square|rounded",
        "align": "left|center|right",
        "icon_spacing": "dimension \u2014 gap between icons",
        "icon_padding": "dimension \u2014 padding inside icon shape",
        "hover_animation": "pop|shrink|grow",
        "columns": "string number \u2014 for grid layout"
      },
      "helper": "social_icons_w($icons, $size, $color, $primary_color, $shape, $align, $spacing)"
    },
    "divider": {
      "count": 7539,
      "use_for": "Section separators, visual breaks between content",
      "key_settings": {
        "color": "hex or rgba \u2014 line color",
        "weight": "dimension \u2014 line thickness",
        "width": "dimension \u2014 line width",
        "gap": "dimension \u2014 vertical space above/below",
        "align": "center|left|right",
        "text": "string \u2014 optional label text on the divider"
      },
      "helper": "divider_w()"
    },
    "spacer": {
      "count": 6637,
      "use_for": "Vertical spacing between elements",
      "key_settings": {
        "space": "dimension \u2014 height in px",
        "space_mobile": "dimension \u2014 responsive height"
      },
      "helper": "spacer_w($px)"
    },
    "toggle": {
      "count": 1043,
      "use_for": "FAQ sections, expandable content (FREE \u2014 use instead of Pro accordion)",
      "key_settings": {
        "tabs": "array of {tab_title, tab_content}",
        "border_color": "hex or rgba",
        "title_color": "hex \u2014 closed state title color",
        "tab_active_color": "hex \u2014 open state title color",
        "title_typography_typography": "'custom'",
        "title_typography_font_family": "font name",
        "title_typography_font_weight": "string",
        "title_typography_font_size": "dimension",
        "content_typography_typography": "'custom'",
        "content_typography_font_family": "font name",
        "content_typography_font_size": "dimension",
        "content_color": "hex",
        "space_between": "integer \u2014 gap between items",
        "toggle_icon_align": "left|right"
      },
      "helper": "Used directly via PressGo_Element_Factory::widget('toggle', ...)"
    },
    "star-rating": {
      "count": 437,
      "use_for": "Review ratings, quality indicators",
      "key_settings": {
        "rating": "number \u2014 0 to 5 (supports decimals like 4.5)",
        "star_style": "star_fontawesome|star_unicode",
        "icon_size": "dimension \u2014 star size",
        "icon_space": "dimension \u2014 gap between stars",
        "stars_color": "hex \u2014 star color",
        "align": "left|center|right",
        "title": "string \u2014 optional label next to stars",
        "title_gap": "dimension \u2014 gap between stars and title"
      },
      "helper": "star_rating_w($rating, $size, $color, $align)"
    },
    "testimonial": {
      "count": 615,
      "use_for": "Customer quotes with avatar, built-in testimonial layout",
      "key_settings": {
        "testimonial_content": "string \u2014 the quote text",
        "testimonial_name": "string \u2014 person's name",
        "testimonial_job": "string \u2014 role/title",
        "testimonial_image": "media object \u2014 avatar image",
        "testimonial_alignment": "left|center|right",
        "image_size": "dimension \u2014 avatar size",
        "image_border_radius": "spacing \u2014 avatar corner radius",
        "content_content_color": "hex",
        "name_text_color": "hex",
        "job_text_color": "hex",
        "content_typography_typography": "'custom'",
        "content_typography_font_family": "font name",
        "content_typography_font_size": "dimension",
        "content_typography_font_style": "normal|italic",
        "name_typography_typography": "'custom'",
        "name_typography_font_family": "font name",
        "name_typography_font_weight": "string"
      },
      "helper": "testimonial_w($cfg, $quote, $name, $role, $image_url, $align)"
    },
    "video": {
      "count": 194,
      "use_for": "YouTube/Vimeo embeds, product demos, explainer videos",
      "key_settings": {
        "youtube_url": "full YouTube URL",
        "vimeo_url": "full Vimeo URL",
        "show_image_overlay": "yes|'' \u2014 show poster image",
        "image_overlay": "media object \u2014 poster image",
        "aspect_ratio": "'169'|'219'|'43'|'32'|'11'",
        "lightbox": "yes|'' \u2014 open in lightbox",
        "_border_radius": "spacing \u2014 rounded corners"
      },
      "helper": "video_w($url, $overlay_img, $border_radius)"
    },
    "google_maps": {
      "count": 594,
      "use_for": "Location maps for businesses, contact pages",
      "key_settings": {
        "address": "string \u2014 full address or place name",
        "height": "dimension \u2014 map height",
        "zoom": "dimension \u2014 zoom level (1-20)",
        "css_filters_css_filter": "'custom' \u2014 enable desaturation",
        "css_filters_saturate": "dimension \u2014 0 for grayscale"
      },
      "helper": "google_map_w($address, $height, $zoom)"
    },
    "progress-bar": {
      "count": 326,
      "use_for": "Skills display, completion indicators, loading bars",
      "key_settings": {
        "title": "string \u2014 bar label",
        "percent": "dimension \u2014 fill percentage",
        "display_percentage": "yes|''",
        "bar_color": "hex \u2014 fill color",
        "bar_bg_color": "hex \u2014 track background",
        "bar_height": "dimension \u2014 bar thickness",
        "bar_border_radius": "spacing \u2014 rounded corners",
        "title_color": "hex",
        "typography_typography": "'custom'"
      },
      "helper": "Used directly via PressGo_Element_Factory::widget('progress-bar', ...)"
    },
    "form": {
      "count": 0,
      "use_for": "Lead capture forms \u2014 Elementor Pro required",
      "key_settings": {
        "form_name": "string \u2014 internal form name",
        "form_fields": "array of {custom_id, _id, field_type, field_label, placeholder, required, width}",
        "field_types": "text, email, tel, textarea, select, date, time, number, url, hidden",
        "field_width": "'50' = half row, '100' = full row",
        "submit_actions": "['email'] \u2014 what happens on submit",
        "email_to": "recipient email address",
        "email_subject": "subject line",
        "email_from": "sender email",
        "email_from_name": "sender display name",
        "email_reply_to": "'field_fld_email' \u2014 prefix 'field_' + the field's _id",
        "email_content_type": "'html' for HTML emails",
        "email_content": "HTML template with [field id='name'] shortcodes",
        "success_message": "string shown after submit",
        "button_text": "submit button text",
        "button_background_color": "hex",
        "button_color": "hex \u2014 button text color",
        "button_width": "'100' for full width",
        "field_border_radius": "number (no unit)",
        "field_border_width": "number",
        "field_border_color": "hex"
      },
      "helper": "form_w() \u2014 NEEDED, not yet built",
      "patterns": {
        "hero_lead_form": {
          "fields": [
            "name (text)",
            "phone (tel)",
            "email (email)",
            "goal (select)",
            "message (textarea, optional)"
          ],
          "button_text": "YES, BOOK MY INTRO SESSION",
          "success_message": "Thank you! We'll be in touch within 24 hours.",
          "note": "Keep to 4-5 fields max. Goal/interest dropdown helps qualify leads."
        },
        "simple_contact": {
          "fields": [
            "name (text)",
            "email (email)",
            "message (textarea)"
          ],
          "button_text": "SEND MESSAGE",
          "note": "Minimum viable form for simple contact pages"
        }
      }
    },
    "self_hosted_video": {
      "count": 0,
      "use_for": "Video without YouTube branding \u2014 keeps visitors on page",
      "implementation": "HTML widget with video tag + poster image + CSS play button overlay",
      "key_pattern": {
        "html": "<div style='position:relative;cursor:pointer;' onclick='...'><img src='POSTER' /><div class='play-btn'>\u25b6</div><video style='display:none'>...</video></div>",
        "play_button_css": "position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); width:72px; height:72px; background:rgba(accent,0.9); border-radius:50%;",
        "onclick": "Hide poster + play button, show video, call .play()"
      },
      "note": "YouTube embeds add channel links, share buttons, and suggested videos that pull visitors off landing pages. Always prefer self-hosted or Bunny CDN for paid traffic pages."
    }
  },
  "layout_patterns": {
    "hero_variants": {
      "split_text_image": {
        "frequency": 0.36,
        "description": "Text left + image right. Most common professional pattern."
      },
      "bg_image_overlay": {
        "frequency": 0.22,
        "description": "Full background image with dark overlay. Dramatic, immersive."
      },
      "centered_no_image": {
        "frequency": 0.18,
        "description": "Centered text on gradient. Clean, modern SaaS feel."
      },
      "creative_multi": {
        "frequency": 0.24,
        "description": "Multi-image grids, video, parallax. Complex layouts."
      }
    },
    "sections_per_page": {
      "average": 10,
      "min": 6,
      "max": 15,
      "mode": 10
    },
    "common_section_order": [
      "hero",
      "social_proof/logos",
      "features/services",
      "about/stats",
      "how_it_works/steps",
      "results/case_study",
      "testimonials",
      "pricing",
      "faq",
      "cta_final",
      "footer"
    ]
  },
  "widget_frequency": {
    "heading": 73470,
    "text-editor": 30517,
    "image": 29416,
    "button": 16906,
    "icon-box": 13674,
    "icon-list": 12381,
    "divider": 7539,
    "spacer": 6637,
    "icon": 5883,
    "image-box": 5466,
    "counter": 3594,
    "social-icons": 3261,
    "toggle": 1043,
    "testimonial": 615,
    "google_maps": 594,
    "star-rating": 437,
    "progress-bar": 326,
    "video": 194
  },
  "typography": {
    "popular_heading_fonts": [
      "Inter",
      "Poppins",
      "Plus Jakarta Sans",
      "Outfit",
      "Manrope",
      "Sora",
      "DM Sans",
      "Montserrat",
      "Playfair Display",
      "Raleway"
    ],
    "popular_body_fonts": [
      "Inter",
      "Poppins",
      "Lato",
      "DM Sans",
      "Manrope",
      "Nunito Sans",
      "Roboto",
      "Barlow",
      "Urbanist"
    ],
    "proven_combinations": [
      {
        "heading": "Inter",
        "body": "Inter",
        "vibe": "clean, modern, SaaS"
      },
      {
        "heading": "Poppins",
        "body": "Inter",
        "vibe": "friendly, approachable"
      },
      {
        "heading": "Playfair Display",
        "body": "Lato",
        "vibe": "elegant, upscale"
      },
      {
        "heading": "Plus Jakarta Sans",
        "body": "Inter",
        "vibe": "contemporary, tech"
      },
      {
        "heading": "Outfit",
        "body": "DM Sans",
        "vibe": "bold, startup"
      },
      {
        "heading": "Manrope",
        "body": "Manrope",
        "vibe": "geometric, minimal"
      },
      {
        "heading": "Sora",
        "body": "Inter",
        "vibe": "futuristic, AI/tech"
      },
      {
        "heading": "Montserrat",
        "body": "Lato",
        "vibe": "professional, corporate"
      }
    ]
  },
  "color_palettes": {
    "popular_accent_colors": [
      {
        "hex": "#4284CB",
        "usage": "blue, trust, SaaS"
      },
      {
        "hex": "#FFDD07",
        "usage": "yellow, energy, attention"
      },
      {
        "hex": "#E64529",
        "usage": "red-orange, urgency, food"
      },
      {
        "hex": "#0145FA",
        "usage": "electric blue, tech, innovation"
      },
      {
        "hex": "#10B981",
        "usage": "emerald green, growth, health"
      },
      {
        "hex": "#FF4E7A",
        "usage": "pink, creative, beauty"
      },
      {
        "hex": "#8B5CF6",
        "usage": "purple, premium, AI"
      },
      {
        "hex": "#F59E0B",
        "usage": "amber, warmth, trust"
      }
    ],
    "dark_bg_colors": [
      "#0F172A",
      "#1A1A2E",
      "#1C1917",
      "#030320",
      "#222831"
    ],
    "light_bg_colors": [
      "#F8FAFC",
      "#FFF8F5",
      "#FFFBEB",
      "#F0F9FF",
      "#F5F3FF"
    ]
  },
  "image_guidelines": {
    "hero_images": {
      "min_width": 800,
      "recommended_width": 1200,
      "orientation": "landscape for bg, portrait/square for split"
    },
    "feature_images": {
      "recommended_width": 600,
      "aspect_ratio": "4:3 or 16:9"
    },
    "pexels_search_strategies": {
      "hero": "{industry} professional {context}",
      "features": "{specific_service} professional quality",
      "about": "team professional people office",
      "testimonials": "happy customer satisfied portrait"
    }
  },
  "section_variants": {
    "_note": "All 19 section types with all 48 builder methods. Set 'variant' key in section config to use non-default.",
    "hero": {
      "default": "Centered text on dark gradient. Bold, impactful. SaaS/tech.",
      "split": "Text left + image right on light bg. Professional. Needs 'image' key.",
      "image": "Full background image with dark overlay. Dramatic. Needs 'image' key.",
      "video": "Centered text + video embed on light bg. Engaging. Needs 'video' key.",
      "gradient": "Colorful gradient bg with wave shape divider. Vibrant, playful.",
      "minimal": "Clean white bg, centered text, no gradient. Simple, elegant.",
      "form": "Split hero: text + bullet points left, lead capture form right. Highest converting hero variant for paid traffic. Requires Elementor Pro for form widget."
    },
    "stats": {
      "default": "White cards with icons, negative margin overlaps hero. After dark hero.",
      "dark": "Dark gradient bg with colored counters. After light sections.",
      "inline": "Minimal horizontal counter row with dividers. Compact accent strip."
    },
    "social_proof": {
      "default": "Industry pill badges on light bg.",
      "dark": "Industry pill badges on dark bg. After light hero."
    },
    "features": {
      "default": "3-column icon-box card grid with accent top borders. Classic. 3 items.",
      "alternating": "Alternating text/image rows (zigzag). 2-4 items with images.",
      "minimal": "Clean icons with text, no card backgrounds. Elegant. 3-6 items.",
      "image_cards": "Image on top of each card. Visual, product-focused. 3 items with images.",
      "grid": "2-column card grid with icons. Best for 4+ features (even count)."
    },
    "steps": {
      "default": "Numbered circles on light bg cards. Traditional, clear.",
      "compact": "Numbered pill badges with connecting divider. Modern SaaS.",
      "timeline": "Vertical timeline with connecting line. Sequential, narrative."
    },
    "results": {
      "default": "Dark gradient with animated counter cards. Dramatic.",
      "bars": "Light bg with animated progress bars. Clean, data-focused."
    },
    "competitive_edge": {
      "default": "Text + icon-list checklist on light bg. Simple. Takes a flat 'benefits' array of strings.",
      "image": "Text + checkmarks left, image right. Visual proof. Needs 'image' key.",
      "cards": "Benefit cards with icons in 3-column grid. Feature-like."
    },
    "testimonials": {
      "default": "3-column cards with star ratings. Balanced. 3 items.",
      "featured": "One large featured quote + smaller cards below. 3+ items.",
      "grid": "2-column card grid with avatars. 4+ items.",
      "minimal": "Centered quotes with dividers, no cards. Elegant."
    },
    "faq": {
      "default": "Centered toggle accordion. Clean, focused.",
      "split": "Header/description left, accordion right. Premium feel."
    },
    "blog": {
      "default": "Posts grid. REQUIRES Elementor Pro."
    },
    "pricing": {
      "default": "Full-width plan cards with feature lists. Standard.",
      "compact": "Left-aligned cards, smaller price, bordered highlight. Modern."
    },
    "logo_bar": {
      "default": "Light bg 'Trusted by' logo row with grayscale filter.",
      "dark": "Dark bg logo row."
    },
    "team": {
      "default": "Full cards with photo, name, role, bio, social links.",
      "compact": "Small photos, name + role only, no bio or cards."
    },
    "gallery": {
      "default": "Image grid with lightbox. Configurable columns.",
      "cards": "2-column image cards with optional captions."
    },
    "newsletter": {
      "default": "Card with headline, description, CTA, privacy note.",
      "inline": "Gradient bar with headline + button side by side."
    },
    "map": {
      "default": "Google Maps embed with optional header. Mobile height auto-scales."
    },
    "cta_final": {
      "default": "Gradient bar with centered text and button. Bold.",
      "card": "White card on light background. Elegant.",
      "image": "Full background image with dark overlay. Dramatic. Needs 'image' key."
    },
    "footer": {
      "default": "Multi-column dark footer with brand, link columns, contact.",
      "light": "White/light bg footer with colored icons."
    },
    "disclaimer": {
      "default": "Small centered disclaimer text. Also renders top-level social_icons."
    }
  },
  "section_rules": {
    "_note": "Guidance for all 19 section types. Sections render in order of the 'sections' array.",
    "hero": "Always first. Match variant to industry: split for services/fitness/healthcare (NEEDS image URL), image for hospitality/events (NEEDS image URL), default for bold SaaS/tech, gradient for creative/startups, minimal for portfolio/consulting, video for courses/demos (NEEDS video URL). Split hero is the most common professional pattern (36% of templates).",
    "stats": "Hard numbers build trust. Default overlaps hero (negative margin) \u2014 ONLY after dark hero. Dark variant after light sections. Inline for compact accent strips. 3-4 items ideal. Values auto-parsed: '2,500+' \u2192 prefix='', number=2500, suffix='+'. Use realistic numbers with commas for thousands.",
    "social_proof": "Industry trust indicators. Place immediately after hero or stats. Dark variant after light hero. Categories are pill badges. Alternative: use logo_bar for client logos instead.",
    "features": "Core value proposition. IMPORTANT: 3 items for default/image_cards, 2-3 for alternating (MUST have image URLs), 3-6 for minimal, 4-6 for grid (even numbers). Each item needs icon, title, desc. NEVER use alternating or image_cards without image URLs \u2014 use default or minimal instead.",
    "steps": "How-it-works process. 3 steps is golden. Compact for modern SaaS, timeline for narrative flow. Each item needs num, title, desc.",
    "results": "Measurable outcomes. Default (dark) for dramatic contrast, bars for data-focused. Metrics need value, label, and individual color hex. CTA optional.",
    "competitive_edge": "Differentiator section. 'default' = simple checklist (benefits is a flat string array, NOT objects). 'image' adds a photo on the right. 'cards' = 3-col grid with {icon, title, desc} items. There is NO comparison_table variant \u2014 it was removed because it's not implemented in the builder.",
    "testimonials": "Customer quotes. KEEP QUOTES SHORT (1-2 sentences max, ~30 words). Default 3-card for balanced coverage, featured when one quote stands out (first item is featured), grid for 4+, minimal for elegant/understated. Items need quote, name, role. Photos optional. Each quote should include one specific detail (a number, a result, a timeframe).",
    "faq": "Toggle widget (FREE). Never use accordion (Pro-only). 4-6 items ideal. Split variant adds description and CTA on left side. Items use q/a keys (not question/answer).",
    "blog": "REQUIRES Elementor Pro. Check before including. Shows recent posts grid. Eyebrow, headline, posts_per_page.",
    "pricing": "2-3 plans recommended. One plan should have highlighted:true. Each plan: name, price (string), period, features (string array), cta, badge. Compact variant for modern/restrained.",
    "logo_bar": "Client/partner logos. Alternative to social_proof. Logos render at fixed 120px width with grayscale filter. Dark variant after light sections.",
    "team": "Team member cards. Default has full details (photo, name, role, bio, social). Compact is name+role+photo only. 3-4 members for default, 4-6 for compact.",
    "gallery": "Image showcase. Default uses Elementor gallery widget with lightbox. Cards variant shows 2-per-row with optional captions. Images can be URL strings or {url, alt, caption} objects.",
    "newsletter": "Email capture. Default has card with headline, description, button, privacy note. Inline is a gradient bar. CTA points to signup URL.",
    "map": "Google Maps embed. Good for local businesses. Height auto-scales to 5/8 on mobile (min 200px). Address, height, zoom configurable.",
    "cta_final": "Strong closing push before footer. Gradient (default) for bold, card for elegant, image for dramatic (needs image URL). Always include trust_line for reassurance.",
    "footer": "Always last visible section \u2014 EVERY page should have a footer. Dark (default) or light variant. Include brand (name + short description), 2-3 link columns, contact info, copyright year, social icons.",
    "disclaimer": "Optional. Very bottom after footer. Small centered text. Also picks up top-level social_icons array.",
    "general_variant_selection": "Don't use 'default' for everything \u2014 pick variants that match the industry. Fitness: split hero, default features, default testimonials. SaaS: default hero, default features, compact steps. Restaurant: image hero, image_cards features, minimal testimonials. Portfolio: minimal hero, minimal features, minimal testimonials."
  },
  "content_quality_rules": {
    "_note": "Derived from analysis of high-converting landing pages. Concise copy converts better.",
    "max_word_counts": {
      "hero_headline": 8,
      "hero_subheadline": 25,
      "feature_title": 4,
      "feature_desc": 20,
      "step_title": 4,
      "step_desc": 20,
      "testimonial_quote": 30,
      "faq_answer": 50,
      "cta_headline": 8,
      "button_text": 4,
      "pricing_description": 12
    },
    "content_patterns": {
      "headlines": "Use power words: Transform, Unlock, Discover, Build, Launch, Grow, Master, Elevate. Avoid: Welcome to, We are, Our company",
      "testimonials": "Include ONE specific detail (a number, a timeframe, a concrete result). Bad: 'Great service!' Good: 'Cut our onboarding from 3 weeks to 2 days.'",
      "features": "Lead with the benefit, not the feature. Bad: 'Advanced analytics dashboard.' Good: 'See what's working in real time.'",
      "cta_buttons": "Action verb + outcome. Bad: 'Submit', 'Click Here'. Good: 'Start Free Trial', 'Book Your Class', 'Get Your Quote'"
    },
    "visual_rhythm": {
      "rule": "Alternate light and dark backgrounds. Never place two same-bg sections adjacent.",
      "typical_flow": [
        "dark_hero",
        "light_stats",
        "light_features",
        "dark_results",
        "light_testimonials",
        "light_faq",
        "dark_cta",
        "dark_footer"
      ],
      "section_count": "7-10 sections for a standard page. More than 12 feels bloated."
    },
    "no_fancy_punctuation": {
      "rule": "Em dashes, en dashes, smart quotes, and ellipses render correctly. Use them freely.",
      "historical_note": "Pre-2026-05-11 builds had a double-escape bug; obsolete now."
    },
    "shorter_copy": {
      "rule": "Landing page copy should be punchy and direct. Max word counts per element: hero headline 8 words, hero subheadline 15 words, bullet points 8 words each, card descriptions 25 words, section headlines 6 words.",
      "reason": "AI-generated copy tends to be too long. Real client feedback consistently asks for shorter, clearer text.",
      "learned_from": "Lindsay Allen (Bodystyle): 'the messaging has to be crystal clear' \u2014 shorter won every time"
    }
  },
  "config_schema_ref": "See config-schema.json for complete config structure with all field types, required/optional flags, examples, variant pairing guide, and industry recommendations.",
  "elementor_technical": {
    "layout": "Flexbox containers (Elementor 3.6+). All layout primitives are containers with flex properties.",
    "hierarchy": "container(outer, direction:column, content_width:boxed) > widget | container(row, direction:row) > container(col, direction:column) > widget",
    "nesting": "Unlimited \u2014 containers nest freely",
    "icon_format": {
      "value": "fas fa-icon-name",
      "library": "fa-solid"
    },
    "icon_libraries": {
      "fa-solid": "fas fa-*",
      "fa-regular": "far fa-*",
      "fa-brands": "fab fa-*"
    },
    "never_use": [
      "_animation (causes elementor-invisible class, content disappears)",
      "Legacy sections/columns (elType: section) \u2014 use containers only"
    ],
    "key_patterns": {
      "outer": "Top-level container: flex column, boxed content, responsive padding (desktop \u2192 3/4 tablet \u2192 1/2 mobile)",
      "row": "Horizontal container: flex row, auto-calculates child widths, stacks on mobile",
      "col": "Vertical container: flex column, holds widgets, flex_gap: 0",
      "spacing": "Via spacer widgets (not flex gap) \u2014 spacers auto-scale 2/3 on mobile"
    },
    "data_storage": "update_post_meta($id, '_elementor_data', wp_slash(wp_json_encode($elements)))"
  },
  "mobile_design_patterns": {
    "_note": "Learned from iterative mobile QA on pressgodigital.com landing pages. These patterns prevent the most common mobile layout issues.",
    "split_hero_mobile": {
      "problem": "Split hero (text-left, image-right) stacks vertically on mobile. Text alignment and padding need overrides.",
      "fixes": [
        "Set align_mobile='center' on headings, subheadlines, and buttons in the text column",
        "Reset padding_mobile to {0, 0, 20, 0} on the text column (removes desktop-only right padding)",
        "Image column needs width_mobile=100% to prevent squeeze",
        "Trust line row (stars + text) needs flex_justify_content_mobile='center' and flex_align_items='center'"
      ]
    },
    "stats_inline_mobile": {
      "problem": "Inline stats (horizontal counter row) can overflow on mobile with 4 items.",
      "fixes": [
        "Use flex_wrap='wrap' on the stats row",
        "Set width_mobile='45%' on each stat column so they wrap to 2x2 grid",
        "Counter font sizes: desktop 36px \u2192 mobile 28px",
        "Label font sizes: desktop 13px \u2192 mobile 11px"
      ]
    },
    "typography_scaling": {
      "rule": "Any heading >= 28px needs explicit mobile size. Rough formula: mobile = desktop * 0.65, min 20px.",
      "examples": {
        "hero_headline": {
          "desktop": 48,
          "mobile": 32
        },
        "section_headline": {
          "desktop": 36,
          "mobile": 26
        },
        "card_title": {
          "desktop": 20,
          "mobile": 18
        },
        "eyebrow": {
          "desktop": 12,
          "mobile": 11
        }
      }
    },
    "section_padding_mobile": {
      "rule": "Desktop section padding (80-100px) is too much on mobile. Use 40-50px top/bottom, 20px sides.",
      "auto_calc": "outer() handles this: tablet = desktop * 3/4, mobile = desktop * 1/2 (min 40px)"
    },
    "button_alignment": {
      "rule": "Buttons in split layouts should be left-aligned on desktop, centered on mobile.",
      "pattern": "align='left', align_mobile='center'"
    },
    "image_sizing": {
      "rule": "Hero images in split layouts need explicit mobile handling to prevent CLS.",
      "pattern": "Set aspect-ratio on image container via custom CSS, use min-height on image columns"
    },
    "testing": {
      "tool": "Puppeteer screenshots at 375x812 (2x DPR) \u2014 test/screenshot-sections.js",
      "critical_checks": [
        "Hero text + buttons centered on mobile",
        "Stats don't overflow horizontally",
        "Feature cards stack cleanly (no text truncation)",
        "Pricing cards are full-width on mobile",
        "FAQ toggle items have enough tap target size",
        "Footer columns stack vertically"
      ]
    },
    "content_width_full": {
      "problem": "content_width: boxed adds max-width via .e-con-inner wrapper that clips content on mobile, especially in multi-column layouts.",
      "fix": "Use content_width: full on ALL containers. Control max-width via custom CSS: selector.e-con { max-width: 1140px; margin: 0 auto; } @media(max-width:767px) { selector.e-con { max-width: 100%; } }",
      "learned_from": "Bodystyle Fitness comparison table \u2014 boxed containers clipped text at 375px viewport"
    },
    "flex_direction_mobile_broken": {
      "problem": "Elementor's flex_direction_mobile setting gets ignored in many cases. Containers revert to column stacking regardless of the setting.",
      "fix": "Use custom_css with !important: @media(max-width:767px){selector.e-con{flex-direction:row!important;}}",
      "learned_from": "Multiple client builds \u2014 this is a consistent Elementor bug"
    },
    "hide_mobile_no_override": {
      "problem": "hide_mobile: hidden adds elementor-hidden class with display:none!important. This CANNOT be overridden with custom CSS.",
      "fix": "Only use hide_mobile for elements you truly never want on mobile. For conditional display, use custom_css media queries instead. For sticky/fixed elements, use Code Snippets plugin instead of Elementor containers.",
      "learned_from": "Bodystyle Fitness sticky mobile CTA bar \u2014 fixed positioning + hide_desktop broke on all devices"
    },
    "icon_text_centering": {
      "problem": "Icon + text side-by-side in a flex row: the text widget stretches to 100% width by default, pushing the pair off-center.",
      "fix": "Set _element_width: auto on BOTH the icon widget AND the text widget. Then use flex_justify_content: center on the parent container.",
      "learned_from": "Bodystyle Fitness comparison table \u2014 icons and text wouldn't center until both had auto width"
    },
    "minimum_text_sizes": {
      "rule": "Minimum readable mobile text: 13px for body/values, 11px for labels/categories. Never go below 11px.",
      "context": "At 375px viewport minus padding (~340px usable), two 48% columns = ~163px each. Keep text short for side-by-side mobile layouts.",
      "learned_from": "Bodystyle Fitness \u2014 11px was barely readable, 13px was the sweet spot for comparison values"
    },
    "dual_layout_pattern": {
      "problem": "Complex layouts (comparison tables, feature grids) that look great on desktop often can't be made responsive with a single Elementor container structure.",
      "fix": "Build TWO versions of the section \u2014 one optimized for desktop (hide on mobile via Elementor responsive visibility), one optimized for mobile (hide on desktop/tablet). More markup but guaranteed to look good on both.",
      "learned_from": "Bodystyle Fitness comparison table, Ardane Medical video embeds \u2014 the only reliable way to handle significantly different desktop vs mobile layouts"
    }
  },
  "cta_popup_pattern": {
    "_note": "For landing pages with a scheduling CTA (Calendly, etc), use this proven pattern.",
    "approach": "Custom HTML modal with iframe \u2014 zero external JS dependencies. Survives WP Rocket, ad blockers, slow CDNs.",
    "implementation": {
      "step_1": "Add HTML widget (widgetType: 'html') to the hero section containing the modal div + script",
      "step_2": "Set all CTA button URLs to '#schedule' (or any anchor)",
      "step_3": "Script uses addEventListener (NOT inline onclick) to intercept #schedule clicks and open modal",
      "step_4": "Iframe src set lazily on first open \u2014 doesn't slow page load"
    },
    "wp_rocket_compatibility": {
      "problem": "WP Rocket's Delay JavaScript rewrites inline onclick to data-rocket-onclick, breaking handlers. Also wraps scripts in type='rocketlazyloadscript'.",
      "solution": "Use ONLY addEventListener (no inline onclick/onmouseover). Add data-no-optimize='1' data-no-defer='1' data-no-minify='1' to the script tag. Also add 'pg-cal' (or your script identifier) to WP Rocket's delay_js_exclusions option.",
      "code_pattern": "document.addEventListener('click', function(e) { var a = e.target.closest('a[href*=\"#schedule\"]'); if (a) { e.preventDefault(); open(); } });"
    },
    "modal_features": [
      "Click overlay to close",
      "X button to close",
      "ESC key to close",
      "Body scroll lock when open",
      "Fade animation (200ms)",
      "Responsive sizing: min(calc(100vw - 24px), 480px) width, min(calc(100dvh - 40px), 660px) height",
      "border-radius: 16px for modern feel",
      "backdrop-filter: blur(3px) on overlay"
    ],
    "calendly_url_params": {
      "hide_event_type_details": "1 \u2014 hides event description sidebar",
      "hide_gdpr_banner": "1 \u2014 hides GDPR cookie banner",
      "primary_color": "hex without # \u2014 brand accent color (may require Calendly Pro)",
      "text_color": "hex without # \u2014 heading text color (may require Calendly Pro)"
    }
  },
  "landing_page_template": {
    "_note": "Proven landing page pattern from pressgodigital.com/get-started. Use Elementor Canvas template for true landing page feel (no header/footer chrome).",
    "page_setup": {
      "template": "Elementor Canvas (elementor_canvas) \u2014 removes theme header/footer",
      "set_via": "update_post_meta($id, '_wp_page_template', 'elementor_canvas')"
    },
    "recommended_section_flow": [
      "hero",
      "stats",
      "features",
      "testimonials",
      "pricing",
      "faq",
      "cta_final",
      "footer"
    ],
    "style_notes": {
      "warm_paper": "Light bg #FAF8F5, dark text #2C2421, muted #7A6E66, gold accent #B8860B. DM Sans for both heading/body. Subtle card shadows. Elegant, not flashy.",
      "boxed_width": "1100px keeps content readable without feeling cramped",
      "section_padding": "100px desktop, auto-scales to 40px mobile"
    },
    "logo_placement": {
      "approach": "Image widget at top of hero section, before headline. Left-aligned desktop, centered mobile.",
      "settings": "width: 140px desktop, 120px mobile. margin_bottom: 8px."
    }
  },
  "conversion_patterns": {
    "_note": "Real-world conversion learnings from paid traffic landing pages. These override template-kit patterns when in conflict.",
    "hero_with_form": {
      "pattern": "Split hero: bullet points + phone CTA left, lead capture form right. Form in hero above fold converts better than CTA button that scrolls to a form below.",
      "form_fields": "Name, Phone, Email, Goal/Interest (select dropdown), Message (optional). Keep it to 4-5 fields max.",
      "mobile": "Form stacks below hero text. Reduce bullet points to 4 on mobile (hide desktop list, show shorter mobile list).",
      "learned_from": "South Coast Hypnotherapy (highest converting hero), adapted for Bodystyle Fitness"
    },
    "comparison_over_features": {
      "pattern": "For businesses competing against big brands (OrangeTheory, Planet Fitness, etc.), a comparison table converts better than a generic features section. People need to see WHY you're different, not just WHAT you offer.",
      "implementation": "Red X + faded text for competitors, green check + bold text for client. Use generic competitor label ('Big Class Gyms') not specific brand names \u2014 avoids legal issues and broadens the comparison.",
      "learned_from": "Bodystyle Fitness \u2014 Lindsay specifically asked for a comparison section; it became the centerpiece of the page"
    },
    "video_self_hosted": {
      "pattern": "Self-hosted video (or Bunny CDN) instead of YouTube embeds. YouTube adds clickable channel links, share buttons, and suggested videos that pull visitors off the landing page.",
      "implementation": "HTML5 video with poster image + CSS play button overlay. On click: hide poster + play button, show video, call .play(). Keeps visitors on page.",
      "learned_from": "Bodystyle Fitness \u2014 YouTube Short converted to self-hosted to eliminate exit points"
    },
    "sticky_mobile_cta": {
      "pattern": "Fixed bottom bar on mobile with phone number + book/form CTA. Visible at all scroll positions.",
      "implementation": "Must use Code Snippets plugin, NOT Elementor fixed positioning with hide_desktop. CSS: position:fixed; bottom:0; display:none on desktop, display:flex on mobile via @media query.",
      "learned_from": "Bodystyle Fitness, Hall Brothers Moving \u2014 Elementor's native fixed + responsive hide doesn't work"
    },
    "client_voice_over_ai_copy": {
      "pattern": "Record client calls, transcribe with Whisper, extract selling points for landing page copy. Real client language converts better than AI-generated marketing speak.",
      "examples": "Lindsay Allen: 'people plateau because there's a limit to what the same moves can do' \u2192 became comparison point. 'our trainers are good enough to customize it even in a group' \u2192 became semi-private selling point.",
      "implementation": "Use mlx-whisper locally for transcription, extract key phrases, use in hero bullets and section copy."
    },
    "brand_guide_compliance": {
      "pattern": "Always use the client's actual brand guide fonts and colors, not what their previous designer used. Kilo used Lexend Giga + Space Grotesk for Bodystyle \u2014 brand guide said Montserrat + Open Sans.",
      "implementation": "Ask for brand guide PDF early. Extract: heading font, body font, primary color, accent color, dark/light backgrounds."
    }
  },
  "code_snippet_patterns": {
    "_note": "These are WordPress Code Snippets (plugin), not Elementor widgets. They handle functionality that Elementor can't do natively.",
    "sticky_mobile_cta": {
      "what": "Fixed bottom bar on mobile with phone + CTA button",
      "why": "Elementor's _position:fixed + hide_desktop/hide_tablet doesn't work (elementor-hidden overrides everything)",
      "implementation": "add_action('wp_footer', ...) with CSS: position:fixed; bottom:0; display:none; @media(max-width:767px){display:flex}"
    },
    "google_ads_tracking": {
      "what": "3 snippets: gtag config (header), form conversion (footer), phone swap (header)",
      "gtag": "add_action('wp_head', ...) with async gtag.js script + gtag('config', 'AW-ID')",
      "form_conversion": "jQuery(document).on('submit_success', '.elementor-form', function(){ gtag('event', 'conversion', {...}) })",
      "phone_swap": "gtag('config', 'AW-ID/LABEL', { phone_conversion_number: 'NUMBER' })"
    }
  },
  "section_schemas": {
    "_note": "Required vs optional fields per section type. 'item_shape' is the per-item object shape used in 'items' arrays. Aliases listed are accepted but the canonical key is listed first.",
    "hero": {
      "required": [
        "headline"
      ],
      "optional": [
        "eyebrow",
        "subheadline",
        "cta_primary",
        "cta_secondary",
        "image"
      ],
      "field_shapes": {
        "cta_primary": "{ text: string, url: string }",
        "cta_secondary": "{ text: string, url: string }",
        "image": "{ url: string, alt?: string } \u2014 only used by variants 'split' and 'image'"
      },
      "fallback_behavior": "Missing cta_primary suppresses button; missing eyebrow suppresses the pill above headline. Headline alone is enough."
    },
    "stats": {
      "required": [
        "items"
      ],
      "optional": [
        "headline",
        "eyebrow"
      ],
      "item_shape": "{ value: string|number, label: string }",
      "value_parsing": "Values are auto-split into prefix/number/suffix. '$2,500+' becomes prefix='$', number=2500, suffix='+'. Non-numeric strings ('many') currently render as 0 (TODO: fall back to plain heading).",
      "min_items": 2,
      "max_items": 4
    },
    "social_proof": {
      "required": [
        "items"
      ],
      "optional": [
        "headline",
        "eyebrow"
      ],
      "item_shape": "{ text: string } OR plain string",
      "note": "Both forms accepted."
    },
    "features": {
      "required": [
        "items"
      ],
      "optional": [
        "headline",
        "eyebrow"
      ],
      "item_shape": "{ icon: { value: string, library: string }, title: string, desc: string }",
      "field_aliases": "'description' is accepted as an alias for 'desc' for forgiveness.",
      "variants_min_items": {
        "default": 2,
        "alternating": 2,
        "minimal": 3,
        "image_cards": 3,
        "grid": 4
      },
      "variants_max_items": {
        "default": 3,
        "alternating": 4,
        "minimal": 6,
        "image_cards": 3,
        "grid": 6
      },
      "auto_wrap_warning": "When variant='default' and items > 3, layout overflows. Use variant='grid' for 4+ items."
    },
    "steps": {
      "required": [
        "items"
      ],
      "optional": [
        "headline",
        "eyebrow"
      ],
      "item_shape": "{ num: string, title: string, desc: string }",
      "field_aliases": "'description' accepted as alias for 'desc'.",
      "note": "'num' is a string ('01' not 1) so leading zeros are preserved."
    },
    "results": {
      "required": [
        "metrics"
      ],
      "optional": [
        "headline",
        "eyebrow",
        "cta"
      ],
      "item_shape_metrics": "{ value: string|number, label: string, color?: string (hex) }",
      "color_validation": "Invalid hex strings fall back to the global accent color silently."
    },
    "competitive_edge": {
      "default_variant": {
        "required": [
          "headline",
          "benefits"
        ],
        "item_shape_benefits": "string (flat array of strings, NOT objects)",
        "field_shapes": {
          "cta": "{ text: string, url: string }"
        }
      },
      "image_variant": {
        "required": [
          "headline",
          "benefits",
          "image"
        ],
        "field_shapes": {
          "image": "{ url: string, alt?: string }"
        }
      },
      "cards_variant": {
        "required": [
          "headline",
          "items"
        ],
        "item_shape_items": "{ icon: { value, library }, title: string, desc: string }"
      },
      "comparison_table_variant": "NOT IMPLEMENTED. Removed from variant list. Use 'default' with a flat benefits string array."
    },
    "testimonials": {
      "required": [
        "items"
      ],
      "optional": [
        "headline",
        "eyebrow"
      ],
      "item_shape": "{ quote: string, name: string, role?: string, image?: { url, alt } }",
      "empty_item_behavior": "Items with empty 'quote' are skipped entirely. Items with empty 'name' suppress the name/role line."
    },
    "faq": {
      "required": [
        "items"
      ],
      "optional": [
        "headline",
        "subheadline",
        "eyebrow",
        "cta"
      ],
      "item_shape": "{ q: string, a: string }",
      "min_items": 1,
      "ideal_items": "4-6"
    },
    "pricing": {
      "required": [
        "plans"
      ],
      "optional": [
        "headline",
        "eyebrow"
      ],
      "item_shape_plans": "{ name: string, price: string, period: string, features: string[], cta: { text, url }, highlighted?: bool, badge?: string }",
      "note": "Exactly one plan can have highlighted=true."
    },
    "cta_final": {
      "required": [
        "headline"
      ],
      "optional": [
        "description",
        "eyebrow",
        "cta",
        "trust_line"
      ],
      "field_shapes": {
        "cta": "{ text: string, url: string }"
      },
      "note": "If cta is missing the button is suppressed but the headline still renders."
    },
    "footer": {
      "required": [
        "brand"
      ],
      "optional": [
        "columns",
        "social_icons",
        "contact"
      ],
      "field_shapes": {
        "brand": "{ name: string, description?: string }",
        "columns": "[{ title: string, links: [{ text: string, url: string }] }]",
        "social_icons": "[{ name: 'twitter'|'github'|'youtube'|'linkedin'|'facebook'|'instagram', url: string }]",
        "contact": "{ email?: string, phone?: string, address?: string }"
      },
      "field_aliases": "Inside columns, 'items' is accepted as an alias for 'links'."
    }
  },
  "validation_behavior": {
    "hard_failures": [
      "Section type not in known list \u2014 rejected.",
      "Missing required field per section_schemas \u2014 rejected with field name.",
      "Malformed JSON \u2014 rejected."
    ],
    "silent_fallbacks": [
      "Unknown variant name \u2014 falls back to 'default' (TODO: emit warning).",
      "Invalid icon class \u2014 icon omitted, no placeholder shown.",
      "Invalid hex color \u2014 falls back to global accent.",
      "Empty item in 'items' array \u2014 that single item is skipped, rest of section renders."
    ],
    "url_sanitization": "Only http(s)://, mailto:, tel:, relative paths starting with /, and #anchors are accepted in CTA url fields. Anything else (javascript:, data:, file:, etc.) is replaced with '#'.",
    "string_escaping": "Headlines, button text, labels: sanitize_text_field. Rich content (descriptions, FAQ answers): wp_kses_post. Raw HTML in headline strings is stripped, not rendered.",
    "value_coercion": {
      "stat_values": "Numeric strings with prefix/suffix get parsed for counter animation. Non-numeric strings currently render as 0 (TODO).",
      "booleans_as_strings": "highlighted: 'true'/'false' is coerced to bool.",
      "numbers_as_strings": "price: '10' and price: 10 both accepted."
    }
  },
  "quickstart_minimal_configs": {
    "_note": "Smallest config that will render each section type cleanly. Use as templates and fill in real content.",
    "hero": {
      "type": "hero",
      "data": {
        "headline": "Your big promise here"
      }
    },
    "stats": {
      "type": "stats",
      "data": {
        "items": [
          {
            "value": "1,200+",
            "label": "Happy customers"
          },
          {
            "value": "98%",
            "label": "Retention"
          },
          {
            "value": "24/7",
            "label": "Support"
          }
        ]
      }
    },
    "features": {
      "type": "features",
      "data": {
        "items": [
          {
            "title": "Fast",
            "desc": "Built for speed.",
            "icon": {
              "value": "fas fa-bolt",
              "library": "fa-solid"
            }
          },
          {
            "title": "Simple",
            "desc": "No learning curve.",
            "icon": {
              "value": "fas fa-check",
              "library": "fa-solid"
            }
          },
          {
            "title": "Solid",
            "desc": "Built to last.",
            "icon": {
              "value": "fas fa-shield",
              "library": "fa-solid"
            }
          }
        ]
      }
    },
    "steps": {
      "type": "steps",
      "data": {
        "items": [
          {
            "num": "01",
            "title": "Sign up",
            "desc": "Create your account in 30 seconds."
          },
          {
            "num": "02",
            "title": "Connect",
            "desc": "Link your tools."
          },
          {
            "num": "03",
            "title": "Go live",
            "desc": "Launch your first campaign."
          }
        ]
      }
    },
    "competitive_edge": {
      "type": "competitive_edge",
      "data": {
        "eyebrow": "WHY US",
        "headline": "Built different",
        "description": "Three things our competitors won't.",
        "benefits": [
          "No long-term contracts",
          "Real human support",
          "Live in 5 minutes"
        ],
        "cta": {
          "text": "Get Started",
          "url": "/"
        }
      }
    },
    "testimonials": {
      "type": "testimonials",
      "data": {
        "items": [
          {
            "quote": "Real quote here.",
            "name": "Real Person",
            "role": "Real Title"
          }
        ]
      }
    },
    "faq": {
      "type": "faq",
      "data": {
        "items": [
          {
            "q": "How does it work?",
            "a": "It just works."
          },
          {
            "q": "How much?",
            "a": "It depends."
          }
        ]
      }
    },
    "pricing": {
      "type": "pricing",
      "data": {
        "plans": [
          {
            "name": "Free",
            "price": "0",
            "period": "forever",
            "features": [
              "Everything basic"
            ],
            "cta": {
              "text": "Start",
              "url": "/"
            }
          },
          {
            "name": "Pro",
            "price": "29",
            "period": "month",
            "features": [
              "Everything"
            ],
            "cta": {
              "text": "Go Pro",
              "url": "/pro"
            },
            "highlighted": true
          }
        ]
      }
    },
    "cta_final": {
      "type": "cta_final",
      "data": {
        "headline": "Ready to start?",
        "description": "It takes 30 seconds.",
        "cta": {
          "text": "Get Started",
          "url": "/"
        }
      }
    },
    "footer": {
      "type": "footer",
      "data": {
        "brand": {
          "name": "Your Brand",
          "description": "Tagline here."
        },
        "columns": [
          {
            "title": "Product",
            "links": [
              {
                "text": "Features",
                "url": "/features"
              },
              {
                "text": "Pricing",
                "url": "/pricing"
              }
            ]
          }
        ],
        "contact": {
          "email": "hello@example.com"
        }
      }
    }
  },
  "known_gotchas": [
    {
      "id": "hero_optionality",
      "rule": "Hero only requires 'headline'. Build with just a headline if the user hasn't given you more \u2014 don't invent.",
      "severity": "high"
    },
    {
      "id": "no_invented_testimonials",
      "rule": "Do NOT generate fake testimonials with first-name-last-initial attribution. If the user hasn't given real quotes, skip the testimonials section or use clearly-labeled placeholder ('TESTIMONIAL 1 \u2014 replace before publishing').",
      "severity": "high"
    },
    {
      "id": "stat_value_format",
      "rule": "Stat values are auto-parsed: '$2,500+' becomes prefix='$', number=2500, suffix='+'. Non-numeric strings currently render as 0 (will be fixed; for now use numeric values).",
      "severity": "low"
    },
    {
      "id": "fontawesome_v5_only",
      "rule": "Elementor bundles FontAwesome 5 — FA6-only icon names render as nothing (silent fail). Avoid: fa-faucet-drip, fa-mobile-screen-button, fa-house, fa-circle-info, fa-arrow-up-right-from-square. Safe FA5 names: fa-tint, fa-wrench, fa-mobile-alt, fa-seedling, fa-star, fa-clock, fa-home, fa-info-circle, fa-external-link-alt, fa-check-circle, fa-shield-alt, fa-bolt, fa-chart-line, fa-trophy, fa-rocket, fa-gem, fa-award, fa-handshake, fa-dollar-sign, fa-briefcase, fa-arrow-right.",
      "severity": "high"
    },
    {
      "id": "raw_unsplash_pexels_ids_unsafe",
      "rule": "Don't pass raw Unsplash photo IDs (e.g. photo-1416879595882-...) hoping they match your topic — Unsplash sometimes serves a totally different photo for old IDs. Either: (a) ask the user to drop a real image into the watch URL or wp-admin/upload.php and call list_recent_media, (b) use upload_media({url}) with a verified Pexels search URL, or (c) skip the image and pick a no-image variant.",
      "severity": "medium"
    },
    {
      "id": "update_section_preserves_variant",
      "rule": "update_section preserves the existing variant when you don't pass one. If you DO want to change variant, pass it explicitly. The data field is a full replace, not a merge — fields you omit will be cleared on rebuild.",
      "severity": "high"
    },
    {
      "id": "map_needs_full_address",
      "rule": "Map widget needs a complete address with city/state or ZIP. A bare street address ('123 Johnson St') will render a 'Map unavailable' placeholder. Pass full addresses like '123 Johnson St, Austin, TX 78701' or '123 Johnson St, 78701'.",
      "severity": "medium"
    },
    {
      "id": "comparison_table_does_not_exist",
      "rule": "competitive_edge has NO comparison_table variant. Use the default variant with a flat 'benefits' array of strings, OR variant='cards' for a 3-col icon-box layout.",
      "severity": "high"
    },
    {
      "id": "url_schemes",
      "rule": "Only http(s)://, mailto:, tel:, relative paths starting with /, and #anchors are accepted in CTA url fields. Anything else gets stripped to '#'.",
      "severity": "high"
    },
    {
      "id": "footer_columns_links_alias",
      "rule": "Footer columns use 'links' as the canonical key (preferred). 'items' also accepted as alias. Each link is { text, url }.",
      "severity": "medium"
    },
    {
      "id": "features_steps_desc_alias",
      "rule": "Features and steps items use 'desc' as canonical (preferred). 'description' also accepted as alias. Missing desc renders as empty (no Lorem fallback).",
      "severity": "medium"
    },
    {
      "id": "items_count_per_variant",
      "rule": "Each variant has documented min/max item counts (see section_schemas). Going outside the range overflows the layout. Pick the right variant for the count.",
      "severity": "medium"
    },
    {
      "id": "competitive_edge_cards_items_shape",
      "rule": "competitive_edge variant='cards' accepts EITHER `items: [{icon, title, desc}]` (canonical, recommended) OR legacy `benefits: [\"string\", ...]`. Pass items when you want icon+title+desc per card; pass benefits for a flat checklist with auto-assigned icons.",
      "severity": "medium"
    },
    {
      "id": "team_default_bio_alias",
      "rule": "team.default member items accept `bio` (canonical) or `description` (alias). When `photo` is missing, an initials-circle placeholder renders automatically \u2014 no more empty avatar slot.",
      "severity": "low"
    },
    {
      "id": "map_widget_lazy_iframe",
      "rule": "The Google Maps embed iframe is lazy-loaded by Elementor. Headless screenshots that don't scroll past the section can capture an empty iframe. The screenshot service was updated to force-load lazy iframes; if you still see an empty map in a screenshot, the issue is screenshot-side, not config-side. The actual rendered page in a real browser will show the map.",
      "severity": "medium"
    },
    {
      "id": "card_text_is_always_dark",
      "rule": "Builders that render content inside white card surfaces use a fixed near-black text color, regardless of the page's text_dark token. Dark-themed pages (text_dark set to a light value) used to render invisible text on white cards; that's now fixed across features.grid, features.minimal, competitive_edge.default, competitive_edge.cards, pricing both variants, results, faq.split, cta_final.card, hero.gradient CTA, testimonials.default, team.default. If you see invisible text on a card, report it.",
      "severity": "medium"
    },
    {
      "id": "primary_gradient_contrast_aware",
      "rule": "cta_final.default, newsletter.inline, and similar sections that render text on a primary-color gradient now pick text+button colors based on primary luminance. Light primaries (electric yellow, blush, lime, clay) get dark text; dark primaries get white text. No more invisible white-on-yellow headlines.",
      "severity": "low"
    },
    {
      "id": "image_url_normalization",
      "rule": "All image-bearing fields accept either a string URL or a media object {url, alt}. The brain's media type documents the object form; AI clients can pass either. image_w() normalizes both shapes. If a section silently shows no image, check that the URL is actually valid (curl it) \u2014 not the shape.",
      "severity": "low"
    },
    {
      "id": "disclaimer_requires_object_data",
      "rule": "The disclaimer section is rendered from a flat string, but add_section requires data to be a non-empty object. Pass `data: {text: \"your disclaimer copy here\"}` (also accepts `content` or `disclaimer` as the inner key). Passing a bare string (`data: \"...\"`) triggers 'Missing or invalid data payload' from the validator. The section also accepts an optional top-level `social_icons` array on the page config.",
      "severity": "medium"
    }
  ],
  "agent_instructions": [
    "Read section_schemas before building anything \u2014 don't guess field names.",
    "Call inspect_page after add_sections, not screenshot_page (inspect is authoritative; screenshot lags by ~1-3s after multi-write).",
    "Build the page in one add_sections call when possible to avoid cache lag on rapid multi-writes.",
    "Don't generate fake testimonials. If the user hasn't given quotes, skip the section or use obvious placeholders.",
    "Don't try variant names that aren't documented. If you want a layout that isn't listed, ask the user \u2014 don't invent variants.",
    "For long sessions, prefer create_page \u2192 add_sections \u2192 screenshot over editing existing pages \u2014 less cache surface area.",
    "For 'modern' style: hero.gradient (with a saturated mid-tone primary like emerald/violet/terracotta, NOT a light pastel) OR hero.split, button_radius 999 (pill), card_radius 16-24, Plus Jakarta Sans or Outfit heading.",
    "For 'editorial / premium / elegant' style: hero.minimal, card_radius 0, button_radius 0, section_padding 140+, Cormorant Garamond or DM Serif Display heading, generous whitespace, off-white background.",
    "For 'bold local service' style (auto detail, contractor, plumbing): hero.default OR hero.image (image overlay is now strong enough), stats.dark, Outfit heading, jet-black bg + electric primary (yellow/red/orange).",
    "For 'calming wellness' style (yoga, spa, therapy): hero.minimal OR hero.gradient, soft warm earth palette (cream + clay + sage), Cormorant Garamond + Lato, generous padding, no card radius."
  ],
  "dark_mode_patterns": {
    "_note": "Dark-themed pages (text_dark set to a light value, light_bg set to a dark value) used to expose color-inversion bugs across most builders. Those are now fixed via a card_text() helper that always returns near-black for content inside white card surfaces, plus contrast-aware text_on_color() for text on dynamic-color backgrounds. The lists below reflect post-fix reality.",
    "safe_variants_on_dark": [
      "hero.default", "hero.gradient", "hero.image", "hero.minimal", "hero.split", "hero.video",
      "social_proof.default", "social_proof.dark",
      "stats.default", "stats.dark", "stats.inline",
      "logo_bar.default", "logo_bar.dark",
      "features.default", "features.minimal", "features.grid", "features.image_cards", "features.alternating",
      "competitive_edge.default", "competitive_edge.cards", "competitive_edge.image",
      "steps.default", "steps.compact", "steps.timeline",
      "results.default", "results.bars",
      "testimonials.default", "testimonials.featured", "testimonials.grid", "testimonials.minimal",
      "faq.default", "faq.split",
      "pricing.default", "pricing.compact",
      "team.default", "team.compact",
      "newsletter.default", "newsletter.inline",
      "cta_final.default", "cta_final.card", "cta_final.image",
      "footer.default", "footer.light"
    ],
    "still_imperfect": [
      "gallery.default — works but image binding requires the URL to be a known WP attachment (use upload_media first, not raw external URLs)",
      "logo_bar.dark — image logos are lazy-loaded; a real-browser visit shows them, but a screenshot taken before the section scrolls into view may show empty slots"
    ]
  },
  "industry_recommendations": {
    "editorial_food_drink": {
      "_note": "Coffee roastery, restaurant, wine bar, bakery — places where craft and provenance matter.",
      "sections": ["hero", "stats.inline", "features.alternating", "steps.default", "competitive_edge.default", "faq.split", "cta_final.card", "footer.default"],
      "typography": {"heading": "DM Serif Display OR Playfair Display", "body": "Inter OR Lato"},
      "palette": "warm: cream bg (#F5EDE0/#FAF6F0) + espresso text (#2A1810) + single warm accent (#C7522A terracotta)",
      "layout": {"card_radius": 0, "button_radius": 0, "section_padding": 130}
    },
    "luxury_minimal_professional": {
      "_note": "Architecture firm, law firm, designer studio, gallery — restraint as a value signal.",
      "sections": ["hero.minimal", "features.minimal", "competitive_edge.default", "steps.timeline", "faq.split", "cta_final.card", "footer.light"],
      "typography": {"heading": "Cormorant Garamond OR DM Serif Display", "body": "Inter"},
      "palette": "monochrome: pure off-white (#FAFAF7) + near-black text (#1A1A1A) + single muted accent (#A89F87 stone)",
      "layout": {"card_radius": 0, "button_radius": 0, "section_padding": 160, "boxed_width": 1280}
    },
    "bold_local_service": {
      "_note": "Auto detail, plumbing, HVAC, contractor, roofing — masculine, bold, action-forward.",
      "sections": ["hero.default OR hero.image", "stats.dark", "features.default", "competitive_edge.default", "pricing.compact", "faq.default", "cta_final.card", "footer.default"],
      "typography": {"heading": "Outfit OR Plus Jakarta Sans", "body": "Inter"},
      "palette": "high contrast: jet black bg (#0A0A0A) + electric primary (yellow/red/orange) + white text",
      "layout": {"card_radius": 4, "button_radius": 4, "section_padding": 110}
    },
    "calming_wellness": {
      "_note": "Yoga studio, spa, therapy practice, meditation — soft, breathable, intentional.",
      "sections": ["hero.minimal OR hero.gradient", "features.minimal", "steps.timeline", "newsletter.default", "faq.default", "cta_final.card", "footer.light"],
      "typography": {"heading": "Cormorant Garamond OR Playfair Display", "body": "Lato"},
      "palette": "warm earth: cream (#FAF6F0) + clay text (#3F3024) + sage primary (#8A9D7A) + blush accent (#D9A299)",
      "layout": {"card_radius": 0, "button_radius": 0, "section_padding": 160, "boxed_width": 1040}
    },
    "modern_tech_saas": {
      "_note": "AI/ML startup, dev tooling, fintech, B2B SaaS — confident, tech-forward, polished.",
      "sections": ["hero.gradient OR hero.default", "logo_bar.dark", "features.minimal OR features.grid", "competitive_edge.cards", "results.default", "pricing.compact", "faq.split", "cta_final.card", "footer.default"],
      "typography": {"heading": "Plus Jakarta Sans OR Sora OR Space Grotesk", "body": "Inter OR DM Sans"},
      "palette": "dark base (#0A0118) + saturated mid-tone primary (violet/lime/electric blue), text_dark light (#F5F3FF) for sections + card_text helper auto-inverts on white cards",
      "layout": {"card_radius": 16, "button_radius": 8, "section_padding": 110}
    }
  },
  "variant_reliability": {
    "_note": "Quick reliability lookup. 'good' = verified working post-fix. 'caveat' = works with a constraint. Anything missing is also 'good'.",
    "competitive_edge.cards": "good — accepts items[{icon,title,desc}] (canonical) or legacy benefits[string]",
    "competitive_edge.image": "good — image renders as bg-cover on right column, fills any size",
    "features.alternating": "good — image timing fixed via screenshot lazy-load wait",
    "features.image_cards": "good — fixed-height bg-image bands keep cards equal height",
    "cta_final.default": "good — contrast-aware text on primary gradient",
    "cta_final.card": "good — card text uses card_text() (always near-black on white)",
    "cta_final.image": "good — overlay reduced from 0.7 to 0.5 so photo reads through",
    "newsletter.inline": "good — contrast-aware headline + button on primary gradient",
    "team.default": "good — bio/description alias, initials placeholder when photo missing",
    "team.compact": "good — initials placeholder when photo missing",
    "gallery.default": "caveat — pass URLs that resolve to real WP attachments (use upload_media first)",
    "gallery.cards": "caveat — same attachment-binding rule",
    "map": "good — full address required (street + city OR ZIP), heuristic shows visible warning if address is too vague",
    "hero.gradient": "good — primary CTA label uses card_text() instead of text_dark, won't render white-on-white even with light primaries",
    "hero.image": "good — overlay strengthened from 0.65 to 0.78 for legibility on busy photos"
  }
}