# Divi 4 — Building Reference (for the parallel D4 specimen library)

Status: scaffolding. Purpose: make PressConduit build well on Divi 4 sites
(the majority of the live Divi install base) for as long as D4 is around.

## Why the ET blog is NOT the source for this
Swept ET's blog (2026-06): it went Divi-5-only by early 2025 — even Jan 2026
and Feb 2025 pages are wall-to-wall Divi 5 betas/tutorials. The classic FREE
Divi 4 "layout packs" live in the 2018–2023 archive (50+ pages deep) and are
aging full-page visual designs, not building references. So D4 building info
comes from the LIVE FORMAT (inspect a real D4 site / module schemas), the same
look-don't-guess discipline that worked for D5 — not from blog archaeology.

## The core reality: D4 ≠ D5 (different engine)
- **D5 = blocks**: `<!-- wp:divi/section ... -->`. **D4 = shortcodes**:
  `[et_pb_section][et_pb_row][et_pb_column type="4_4"][et_pb_text]…[/et_pb_text]
  [/et_pb_column][/et_pb_row][/et_pb_section]`. Not interchangeable — a D5 recipe
  is garbage on a D4 site and vice versa. The D4 library is a SEPARATE parallel
  set, rebuilt in shortcode form.
- **Module names**: `et_pb_text`, `et_pb_image`, `et_pb_button`, `et_pb_blurb`,
  `et_pb_cta`, `et_pb_pricing_tables`/`et_pb_pricing_table`, `et_pb_testimonial`,
  `et_pb_accordion`/`et_pb_accordion_item`, `et_pb_contact_form`/`et_pb_contact_field`,
  `et_pb_number_counter`, `et_pb_divider`, `et_pb_icon`, etc. (verify via
  divi_module_list engine=divi4 on a live D4 site).
- **Columns**: row `column_structure` / each `et_pb_column type="1_2"` (4_4, 1_2,
  1_3, 1_4, 3_5, 2_5…). Simpler than D5's flexType/24-grid.
- **Responsive**: suffixed attrs — `custom_padding_tablet`, `..._phone`, plus
  `_last_edited` flags. No 24-unit flex grid.

## Tokens / brand colors — the big difference
- D4 has NO Design Variables and NO `var(--gcid-…)` CSS-var references. Global
  colors live in the `et_global_colors` option, and shortcodes reference them by
  storing the literal value PLUS a `global_colors_info` attribute mapping a gcid
  to the field(s) using it (e.g. `background_color="#26292F"
  global_colors_info='{"gcid-xxx":["background_color"]}'`). 
- => The D5 "fallback color" trick (`var(--x, #hex)`) does NOT apply. D4 recipes
  carry literal hex + the global_colors_info map; "restyle to brand" = swap the
  global color values, which propagates via the gcid mapping. VERIFY the exact
  attribute shape on a live D4 site before authoring.

## Tooling status for D4
- `divi_module_example` and `divi_module_schema` DO support engine=divi4 (real
  shortcode examples + get_fields()). This is the authoring source for D4
  specimens (the scissors `divi_capture`/`divi_layout_outline` are D5-block-only
  and do NOT work on D4 shortcodes).
- Specimen shelf already supports both engines: tag each specimen with an
  engine field; PressConduit detects the site's builder and serves the matching
  set. (D4 specimens would live e.g. specimens/d4/*.json or carry "engine":"divi4".)
- A D4 capture path (shortcode parse → portable shortcode) is a possible future
  tool; not required to START (author from divi_module_example instead).

## Plan (mirror the D5 play)
1. Get a SAFE Divi 4 test bench (NOT client drpolson / NOT live pcdesignstx).
   johnl598 sandbox is D4.27.6 but its builder was dormant on read — confirm.
2. Pull the real D4 format from that site: module_list(d4), a real et_pb_text /
   et_pb_section example, and how global colors are stored. Ground this note.
3. Build ONE D4 recipe first (hero) as a shortcode specimen → render-verify →
   then mass-produce the other 7. Same prove-small-then-crank approach.

## NEEDED FROM DYLLON
- Which Divi 4 site is safe to build/test on (johnl598 sandbox, or spin a fresh
  D4 staging site)?
- Scope to start: full 8-recipe D4 parallel library, or hero-first proof?

---

## CONFIRMED FORMAT (live read 2026-06-14, rubadub.cdpressuresolutions.com, Divi 4.27.6)

Real section pulled from their Home page (post 10) — this is the authoritative shape:

```
[et_pb_section fb_built="1" _builder_version="4.20.4" _module_preset="default"
   background_color="#EDF000" custom_padding="0px|0px|0px|0px|false|false"
   custom_css_main_element="display:flex;||" global_colors_info="{}"]
  [et_pb_row make_equal="on" _builder_version="4.20.4" _module_preset="default"
     custom_margin="||-53px||false|false" custom_padding="1px|1px|0px|1px|false|false"
     global_colors_info="{}"]
    [et_pb_column type="4_4" _builder_version="4.20.4" _module_preset="default" global_colors_info="{}"]
      [et_pb_text _builder_version="4.20.4" _module_preset="default"
         header_4_font="|800|||||||" header_4_text_align="center" header_4_font_size="26px"
         text_orientation="center" global_colors_info="{}"]<h4>Exterior Car Wash Only $50!</h4>[/et_pb_text]
      [et_pb_button button_text="Book Service" button_alignment="center" _builder_version="4.20.4"
         custom_button="on" button_text_size="12px" button_text_color="#FFFFFF"
         button_bg_color="#0C71C3" button_border_width="0px" button_border_radius="10px"
         button_url="#popup" global_colors_info="{}"][/et_pb_button]
    [/et_pb_column]
  [/et_pb_row]
[/et_pb_section]
```

### Confirmed rules
- **Nesting**: section > row > column(`type="4_4"|"1_2"|"1_3"|"1_4"|"3_5"|"2_5"…`) > module. Always closed (`[/et_pb_*]`).
- **Section** carries `fb_built="1"`. Every module carries `_builder_version` + usually `_module_preset="default"` + `global_colors_info`.
- **Text content** sits as raw HTML INSIDE the module: `[et_pb_text …]<h4>…</h4>[/et_pb_text]`. (D5 puts it in attributes; D4 puts it inline.)
- **Colors**: literal hex in attributes — `background_color="#EDF000"`, `button_bg_color="#0C71C3"`, `button_text_color="#FFFFFF"`. This site uses NO global colors (et_global_colors is null, `global_colors_info="{}"` everywhere).
- **Fonts**: `header_4_font="|800|||||||"` = family|weight|style|… ; sizes `header_4_font_size`, `_font_size`, body `body_font` etc.; alignment `text_orientation` / `header_4_text_align`.
- **Spacing**: `custom_padding="top|right|bottom|left|sync_v|sync_h"`, `custom_margin` same shape (px or "" empty slots).
- **Responsive**: suffix attrs `_tablet` / `_phone` + `_last_edited="on|tablet"` (e.g. `text_orientation_tablet="center"`).
- **Buttons**: need `custom_button="on"` to enable custom styling; then `button_bg_color`, `button_text_color`, `button_border_radius`, `button_border_width`, `button_text_size`, `button_url`, `button_alignment`.
- **Custom CSS** can ride inline via `custom_css_main_element="…"`.

### Brand-token approach for D4 specimens (vs D5 fallbacks)
- D4 has no `var(--…)`. To make a recipe re-brandable: set the site's `et_global_colors`
  (gcid → hex), put the literal hex in the module attr, AND map it in
  `global_colors_info='{"gcid-xxx":["background_color","button_bg_color"]}'`.
  Then "restyle to brand" = change the gcid's hex in et_global_colors → propagates.
- Need to VERIFY the exact global_colors_info shape on a D4 site that actually uses
  global colors (rubadub doesn't). For a first hero proof, literal hex + a documented
  swap list is acceptable; layer the gcid mapping in once verified.

### Tooling note (confirmed)
- `divi_module_example` engine=divi4 WORKS by scanning saved page content (got real
  blurb/button/section examples). `divi_module_list` engine=divi4 returns "builder not
  initialized" on REST (registry not loaded) — use module_example + module_schema instead.
- rubadub = live "Rub-A-Dub Auto Detailing" business site (Divi 4.27.6, PressConduit
  active). Read-only inspection done. BEFORE building: create test recipes as DRAFTS only,
  and confirm with Dyllon (real business site, not a throwaway sandbox).

---

## FIRST D4 BUILD (2026-06-16) — homepage on rubadub, draft page 1290
- Built a full 5-section auto-detailing homepage in Divi 4 shortcodes via divi_create_page
  (status=draft, et_full_width_page): hero (dark) / services 3-up blurb cards (light) /
  stats 4-up (accent) / testimonials 2-up cards (light) / final CTA (dark).
- MODULE SETTINGS ONLY, zero custom CSS (per the universal rule): bg via background_color,
  spacing via custom_padding/custom_margin, cards via border_radii + box_shadow_style=preset1,
  fonts via header_font/header_2_font/text_font + _font_size, responsive via _phone/_tablet +
  _last_edited. Brand: Kanit headings, Poppins body, accent #0C71C3 (their existing blue),
  dark #15171A, light #F5F6F8. Image blurbs use placehold.co (swap for real photos).
- create_page SAVED BYTE-PERFECT for D4 (verified wp_post_get) — D4 shortcodes round-trip
  through the controller cleanly (contrast: D5 contact-form children get stripped).
- GOTCHA: wp_page_rendered_html on a DRAFT returns "Page Not Found" (relay can't fetch a
  draft preview without a logged-in session). To verify a D4 draft render: review in the
  logged-in builder/preview URL, OR briefly publish → render → set back to draft. Do NOT
  publish on a client/business site without asking.

---

## KITCHEN-SINK BUILD (2026-06-16) — draft page 1291, "everything possible"
17 sections, ALL saved byte-perfect via divi_create_page (D4 round-trips cleanly).
Confirmed working module formats (module settings only, no CSS):
- promo bar / hero / logo strip (et_pb_image align=center) / about split (1_2,1_2 image+text+button)
- services (et_pb_blurb image cards) / stats (et_pb_number_counter number+percent_sign+title)
- process steps (numbered et_pb_text) / gallery (et_pb_image grid, border_radii)
- tabs: [et_pb_tabs][et_pb_tab title=""]<p>…</p>[/et_pb_tab]…[/et_pb_tabs]
- pricing (PARENT/CHILD): [et_pb_pricing_tables][et_pb_pricing_table featured="on/off" title currency per sum button_text button_url]<ul><li>…</li></ul>[/et_pb_pricing_table]…[/et_pb_pricing_tables]
- testimonials: [et_pb_testimonial author job_title portrait_url quote_icon="on" …]Quote[/et_pb_testimonial]
- team: [et_pb_team_member name position image_url …]bio[/et_pb_team_member]
- FAQ (PARENT/CHILD): [et_pb_accordion][et_pb_accordion_item title open="on/off"]Answer[/et_pb_accordion_item]…[/et_pb_accordion]
- video: [et_pb_video src="youtube url"]
- blog: [et_pb_blog fullwidth="off" posts_number show_excerpt …] (pulls site posts)
- contact (PARENT/CHILD): [et_pb_contact_form email submit_button_text …][et_pb_contact_field field_id field_title field_type="email/text" fullwidth_field="on/off"][/et_pb_contact_field]…[/et_pb_contact_form]
- social (PARENT/CHILD): [et_pb_social_media_follow][et_pb_social_media_follow_network social_network="facebook" url]facebook[/et_pb_social_media_follow_network]…[/et_pb_social_media_follow]
- final CTA: [et_pb_cta title button_text button_url background_color …]<p>sub</p>[/et_pb_cta]
- section anchor: module_id="contact" on the section for #contact links.

DELIBERATELY OMITTED (need external config — would render broken): et_pb_map (Google API key),
et_pb_signup / email optin (provider), et_pb_shop (WooCommerce), et_pb_countdown_timer (date).
Mention these but don't include without config.
