Doozer's AI-driven design engine — palette, typography, layout, and section variants that make each business site its own.
A Muse is a design recipe (palette, typography, layout axes, decorative flourishes, section variants) resolved per business and applied at render. It is how one codebase produces visually distinct sites at scale.
includes/muse-core.php — resolve the active Muse for a business (precedence:URL override, then businesses.custom_settings.muse_id, then domains.custom_settings.muse_id, then root_domains.muse_id). Muses live in the muses table; the design recipe is reading_json.
includes/muse-css-generators.php + includes/muse-apply.php — turn the Muse intothe per-site CSS (type scale, grid system, prose width, section tones).
includes/muse-contrast.php — readability guards (AA contrast; heading-color swapwhen the brand primary is too pale; section background rhythm).
includes/muse-direction-families.php — the 100-recipe Familylibrary the auto-generator draws from, niche-filtered.
includes/sections/<slug>/<variant>.php — the section frameworkpartials a Muse picks variants from.
museEmitHeadStyles() (includes/muse-apply.php) is where a Muse becomes the page's CSS. It composes in a deliberate ORDER, last-wins:
custom_css_snippet — the interpreter's AI-authored per-Muse flourishes, runthrough museSanitizeCustomCss() first (no @import, no external url(), no </style injection).
includes/muse-contrast.php) — AA-safe prose colors against theeffective backgrounds.
Because these are RENDER-TIME guards, fixing one heals all ~2,400 live sites with NO regeneration. That is the preferred way to fix a fleet-wide design fault.
--muse-* design tokens.** The AI snippet onceemitted body{--muse-font:sans-serif}, which clobbered the correct :root{--muse-font:"Inter",…} for the whole page, so every site rendered system-sans regardless of its family's type pairing. Fix: museSanitizeCustomCss() strips --muse-font* re-declarations (and tidies the emptied rules). Harden the interpreter prompt too if snippets keep doing this. (v2.2.207)
.btn-secondary is an outline button, so it always gets an outline. Archetype orsnippet CSS with !important could strip its border, leaving a white fill on a white header — the "invisible Directions button." The final safety rule is html body .btn.btn-secondary { border: 2px solid currentColor !important; }. currentColor is the button's own text color, which by definition contrasts its background, so the outline reads on light AND dark archetypes without forcing any palette. The selector specificity (0,2,2) must EXCEED the muse button-chrome rules (flat chrome emits body[data-muse-btn="flat"] .btn { border:0 !important } at 0,2,1) or it loses even with !important: the v2.2.209 rule shipped at (0,1,0) and was overridden on flat muses until the bump in v2.2.213. Design-correct (secondary = outlined), not a hack. Lesson worth keeping: verify design by LOOKING (screenshot + inspect every CTA's contrast on a calm AND a bold Muse), not by probing only the one token you changed.
Section dividers carry small SVG flourishes (includes/muse-flourishes.php, museEmitFlourish($slot, $muse), called at the top of about/services/photos/reviews/faq/ contact in business-site.php). The SVG library (arched frame, sunburst, scalloped, brutalist bar, risograph, hand-drawn, diamond, hairline rule) pulls its color from --muse-accent, so one SVG adapts to every palette.
Selection derives a compatible POOL from signals the Muse ALWAYS carries, most specific first: an explicit decorative_hints/homage_signals keyword match wins, else the template_archetype mapped to a curated taste-safe pool, else typography.family, else a restrained default. Pools never mix clashing vocabularies (no brutalist bar on a florist; civic and editorial archetypes stay restrained). The pool is rotated by a stable per-muse seed (the muse id), so two muses of the same archetype can lead with different signatures while a SHARED muse renders identically across its whole root or Cloud (cohesion is the goal). Per-page rhythm: the signature slots cycle up to two harmonious marks; quiet slots (photos/reviews/faq) get a hairline, never the same mark stamped six times.
This was a real sameness fix (v2.2.210): before it, any Muse whose AI hints did not match the keyword map fell to the bland hairline on every section, and even matches repeated one mark down the whole page. The lesson echoes the button bug: verify by LOOKING at the rendered DOM, not by trusting that a signal was wired.
v2 muses get a token-driven hero composition system (museCssHeroComposition, keyed by the hero_composition token: full-bleed-tint, sidebar-quote, arched-portal, marquee-strip, etc.). v1 muses do not, so every v1 hero rendered the same centered headline over a full-bleed photo — a major sameness driver, since the visible fleet is largely v1.
museHeroCompositionV1($muse) (includes/muse-apply.php) gives v1 heroes a per-archetype LAYOUT: 'center' (default) or 'editorial' (bottom-left, ranged-left, narrower measure) for the clearly-editorial archetypes. It emits data-hero-comp on the .hero section, and the scoped CSS in business-site.php changes only alignment and content measure. v2 muses always return 'center' here so their own system stays authoritative.
LOCK: the hero overlay (.hero-background::before) and the h1 / subheadline text-shadows are LOCKED (operator "PERFECT!"). This composition layer must never touch them. Bottom- anchored compositions are deliberately safe because they sit inside the existing bottom- darken band, so legibility is unchanged. Shipped v2.2.212 as one bounded variant (operator: look before going wide); more compositions and a wider archetype map come after visual review.
Movement is a real source of variation within a shared Muse (shared muse across a root or Cloud is a FEATURE for brand cohesion, not a bug). The scroll-reveal system (fade-up sections, staggered cards, hero-excluded, no-JS-safe, reduced-motion-safe via !important) has two intensities, chosen per business by museMotionLevel($muse, $bizTypes):
(reading_json.pace === 'kinetic' or an energetic motion_vocabulary) OR the business is an inherently energetic niche (bar, night_club, dance, gym, music, art_gallery, tattoo, brewery, event_venue, …).
business-site.php emits the lively tier behind a PHP conditional, with HARD CEILINGS (section travel ≤40px, ≤0.95s; cards lift ≤22px + ≤3% scale; stagger ≤0.34s) and an explicit NO list (no parallax, bounce, spin, 3D, blur, zoom-past-1.0, infinite loops, hero motion, or horizontal drift). prefers-reduced-motion always wins. So "lively" can never become tasteless or inaccessible.
The contrast/readability/width guards (the v2.0 overhaul) can flatten creativity if over-applied — see the creativity-regression thread in the site-quality backlog. The goal is unique AND readable. Design-shape problems are governed by Design Standards, not one-off CSS overrides.