* Curated, human-written facts the editor's code cannot express: a one-line
* purpose per component, and short usage tips where the real behaviour is
* surprising. Everything else in the catalog (props, types, enums, defaults,
* animation, icon support, child rules) is derived from the code. Adding a
* component to the resolver without an entry here fails the generator.
*/
exportconstNOTES={
Accordion:{purpose:'Expand/collapse FAQ list (items: title + content, isOpen)'},
BackgroundSection:{purpose:'Full-width section over a bg image/colour with a dark overlay; children sit inside',tips:'Text over bgImage needs overlayColor+overlayOpacity (>=0.45) for contrast.'},
BookingEmbed:{purpose:'Embedded booking widget (calendly|square|custom url)',tips:'Only if the user supplied a booking URL.'},
ButtonLink:{purpose:'Link styled as a button; look comes from style (backgroundColor, color, padding, borderRadius, fontWeight)',tips:'No variant prop: set style yourself, e.g. primary = {backgroundColor:"var(--wsb-primary)",color:"var(--wsb-bg)",padding:"14px 28px",borderRadius:"var(--wsb-button-radius)",fontWeight:"600"}; secondary = transparent bg + border.'},
CTASection:{purpose:'Centered call-to-action band on a gradient (prop gradient = CSS gradient string); ctas[] buttons',tips:'Use ctas[], not buttonText/buttonHref.'},
CallToAction:{purpose:'Call-to-action band on colour/gradient/image (bgType+bgValue, overlay); ctas[] buttons',tips:'Use ctas[], not buttonText/secondaryButton*.'},
ColumnLayout:{purpose:'Row of columns; each direct child becomes one column and stacks on mobile',tips:'ALWAYS set style display:"flex" and flexDirection:"row": published pages stack columns on phones ONLY via that inline pair (measured; without it columns stay side by side, squeezed). columns follows the child count; split must be an allowed string; style.alignItems:"center" centres vertically.'},
ContactForm:{purpose:'Complete contact form with fields[], submit button, spam captcha option (no children)',tips:'Delivery is configured by the owner (recipientEmail / webhook): never invent an address; leave recipientEmail empty.'},
Container:{purpose:'Generic block (flow layout). Children stack vertically; use style for padding/background/maxWidth/flex',tips:'tag is one of div|section|article|header|footer|main. The page root is a Container.'},
ContentSlider:{purpose:'Auto-playing slider of content/image slides',tips:'Use sparingly; a static hero is almost always better.'},
Countdown:{purpose:'Countdown to targetDate (YYYY-MM-DD)',tips:'Only for a real, user-given date.'},
Divider:{purpose:'Horizontal rule'},
FeaturesGrid:{purpose:'Responsive grid of cards {icon,title,description,image?,buttonText?,buttonUrl?}',tips:'icon is PLAIN TEXT (a single glyph/emoji), NOT Font Awesome: never put fa-* there. For Font Awesome icon cards build ColumnLayout > Container > Icon + Heading + TextBlock instead. An image per feature (image,imageAlt) replaces the icon.'},
Footer:{purpose:'Simple footer text line (copyright). For richer footers compose Container/ColumnLayout/SocialLinks/Menu',tips:'Colour comes from style.color (default is dark text: set a light color on a dark footer). Emit in the top-level "footer" slot for scope=site.'},
FormButton:{purpose:'Submit button inside a FormContainer'},
FormContainer:{purpose:'Wrapper for hand-built forms (InputField/TextareaField/FormButton children)',tips:'Prefer ContactForm.'},
Gallery:{purpose:'Image grid (images[] {src,alt,caption?}), columns, optional lightbox',tips:'Use the user\'s uploaded images only; no placeholders in galleries.'},
Heading:{purpose:'Heading h1-h6; size/weight/colour/letterSpacing via style',tips:'One h1 per page. Set fontFamily:"var(--wsb-heading-font)" and a size from the type scale; leave html empty.'},
HeroSimple:{purpose:'Pre-built hero: heading, subtitle, ctas[], bg colour/gradient/image/video with overlay, minHeight',tips:'With bgType=image set overlayColor+overlayOpacity (>=0.45) so light text passes contrast. No entrance animation support.'},
HtmlBlock:{purpose:'Sanitised raw HTML. Last resort only',tips:'Scripts/handlers are stripped.'},
Icon:{purpose:'One Font Awesome icon (icon="fa-solid fa-<name>" or bare "fa-<name>"), optional circle/rounded background',tips:'The ONLY real-icon component. size/color/bgColor/bgShape/bgSize; link optional.'},
ImageBlock:{purpose:'Image. Crop/size/radius via style (width, maxWidth, aspectRatio, objectFit, borderRadius)',tips:'Always set alt. Use style.aspectRatio with objectFit:"cover" (e.g. "4 / 3", "16 / 9", "1 / 1") and a consistent borderRadius. src: "assets/<file>" for uploaded images.'},
InputField:{purpose:'Single-line form input inside FormContainer'},
Logo:{purpose:'Text or image logo linking home'},
MapEmbed:{purpose:'Embedded map for an address',tips:'Only with a real address from the user.'},
Menu:{purpose:'Link list (horizontal/vertical) with optional CTA-styled link'},
Navbar:{purpose:'Top navigation bar: logo (text|image), links[] {text,href,isCta?}, sticky option',tips:'Set showMobileMenu:true or the links overflow on phones. Emit in the top-level "header" slot. In-page links use href "#anchor"; other pages "/page-slug".'},
NumberCounter:{purpose:'Row of animated stats (counters[] {number,suffix,label})',tips:'Only real figures supplied by the user.'},
OpeningHours:{purpose:'Opening hours table (rows[] {day,hours,closed})',tips:'Only real hours supplied by the user.'},
PricingTable:{purpose:'Pricing cards (plans[] {name,price,period,features[],buttonText,buttonHref,isFeatured})',tips:'Only real prices supplied by the user; otherwise use a "Request a quote" CTA.'},
ReviewsStrip:{purpose:'Strip of customer reviews (reviews[] {name,rating,text,source,url}) with link to all reviews',tips:'Only real reviews supplied by the user.'},
SearchBar:{purpose:'Search form'},
Section:{purpose:'Full-width section band with a centred inner column (innerMaxWidth); optional top/bottom divider shapes',tips:'Backgrounds and vertical padding go in style (backgroundColor, paddingTop/paddingBottom).'},
SocialLinks:{purpose:'Row of social icons (links[] {platform,url})',tips:'Only profiles the user gave.'},
Spacer:{purpose:'Vertical gap'},
StarRating:{purpose:'Star rating glyphs (rating 0-5)',tips:'Only real ratings.'},
StickyCallBar:{purpose:'Mobile-only sticky call/directions bar',tips:'Only with a real phone number.'},
Testimonials:{purpose:'Testimonial cards (testimonials[] {quote,name,title,rating}) in a grid or single layout',tips:'Only real quotes supplied by the user.'},
TextBlock:{purpose:'Paragraph text; size/colour/lineHeight/maxWidth via style',tips:'Set fontFamily:"var(--wsb-body-font)", lineHeight 1.6-1.75, maxWidth about 65ch for long copy; leave html empty.'},
TextareaField:{purpose:'Multi-line form input inside FormContainer'},
VideoBlock:{purpose:'Video (file/youtube/vimeo) or background video',tips:'Only with a URL the user gave.'},
};
exportconstGLOBAL_NOTES={
common:{
rules:[
'Every prop below that is a colour accepts a hex, rgb()/hsl(), a gradient (where noted) or a design token var(--wsb-<name>).',
'Entrance animation (animation, animationDelay) exists only on components marked animation:true; HeroSimple, Divider, Spacer, HtmlBlock do not support it.',
],
},
icons:{
components:'Icon renders Font Awesome. FeaturesGrid.icon is plain text, not Font Awesome.',