@rubytech/create-maxy-code 0.1.398 → 0.1.400

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  3. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +17 -1
  4. package/payload/platform/plugins/docs/references/graph.md +15 -0
  5. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -0
  6. package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.d.ts +2 -0
  7. package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.d.ts.map +1 -0
  8. package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.js +123 -0
  9. package/payload/platform/plugins/email/mcp/dist/__tests__/connect-timeout-observability.test.js.map +1 -0
  10. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +9 -0
  11. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  12. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +36 -12
  13. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  14. package/payload/platform/plugins/email/mcp/dist/lib/smtp.d.ts.map +1 -1
  15. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js +18 -1
  16. package/payload/platform/plugins/email/mcp/dist/lib/smtp.js.map +1 -1
  17. package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -0
  18. package/payload/platform/plugins/web-designer/.claude-plugin/plugin.json +8 -0
  19. package/payload/platform/plugins/web-designer/PLUGIN.md +46 -0
  20. package/payload/platform/plugins/web-designer/bin/resolve-cf.sh +56 -0
  21. package/payload/platform/plugins/web-designer/skills/brand-extract/SKILL.md +86 -0
  22. package/payload/platform/plugins/web-designer/skills/socials/SKILL.md +139 -0
  23. package/payload/platform/plugins/web-designer/skills/socials/references/pii-strip.md +64 -0
  24. package/payload/platform/plugins/web-designer/skills/socials/references/sources.md +66 -0
  25. package/payload/platform/plugins/web-designer/skills/socials/templates/og.html +92 -0
  26. package/payload/platform/plugins/web-designer/skills/socials/templates/social-posts.md +46 -0
  27. package/payload/platform/plugins/web-designer/skills/socials/templates/socials.html +87 -0
  28. package/payload/platform/plugins/web-designer/skills/web-design/SKILL.md +98 -0
  29. package/payload/platform/plugins/web-designer/skills/web-design/references/deploy.md +59 -0
  30. package/payload/platform/plugins/web-designer/skills/web-design/references/e-sign.md +39 -0
  31. package/payload/platform/plugins/web-designer/skills/web-design/references/gating.md +55 -0
  32. package/payload/platform/plugins/web-designer/skills/web-design/references/placeholders.md +110 -0
  33. package/payload/platform/plugins/web-designer/skills/web-design/references/sections.md +52 -0
  34. package/payload/platform/plugins/web-designer/skills/web-design/templates/index.html +1004 -0
  35. package/payload/platform/plugins/web-designer/skills/web-design/templates/llms.txt +32 -0
  36. package/payload/platform/plugins/web-designer/skills/web-intake/SKILL.md +107 -0
  37. package/payload/platform/plugins/web-designer/skills/web-intake/references/intake.md +103 -0
  38. package/payload/platform/plugins/whatsapp/PLUGIN.md +7 -1
  39. package/payload/platform/scripts/merge-whatsapp-split-conversations.mjs +264 -0
  40. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  41. package/payload/platform/services/claude-session-manager/dist/http-server.js +22 -6
  42. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  43. package/payload/server/public/assets/{AdminLoginScreens-Bex48co9.js → AdminLoginScreens-rlk263u9.js} +1 -1
  44. package/payload/server/public/assets/{AdminShell-BOmS1NPG.js → AdminShell-C5vWTy2n.js} +1 -1
  45. package/payload/server/public/assets/{Checkbox-DDlXC5tq.js → Checkbox-Chz6bfy-.js} +1 -1
  46. package/payload/server/public/assets/{Transcript-CwItbNkx.js → Transcript-Bsx-bGgY.js} +2 -2
  47. package/payload/server/public/assets/{admin-n9zWE158.js → admin-BnZH758N.js} +1 -1
  48. package/payload/server/public/assets/{arc-C6-nr5UV.js → arc-DyzjCWmw.js} +1 -1
  49. package/payload/server/public/assets/architecture-YZFGNWBL-XiVs-N7Z.js +1 -0
  50. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DIdho_T6.js → architectureDiagram-Q4EWVU46-qWfElD16.js} +1 -1
  51. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-D5rTTBSy.js → blockDiagram-DXYQGD6D-DO2fFHC3.js} +1 -1
  52. package/payload/server/public/assets/{browser-CI8DR1YG.js → browser-CIrfyZWe.js} +1 -1
  53. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-pfUVHdmy.js → c4Diagram-AHTNJAMY-Ca-q5gDe.js} +1 -1
  54. package/payload/server/public/assets/{calendar-WuIAN47H.js → calendar-D6Z8Kmrn.js} +1 -1
  55. package/payload/server/public/assets/channel-CYBkRz7E.js +1 -0
  56. package/payload/server/public/assets/chat-h7SytZHx.js +1 -0
  57. package/payload/server/public/assets/chevron-left-DSTeS1_g.js +1 -0
  58. package/payload/server/public/assets/{chunk-2KRD3SAO-EF6cdj8U.js → chunk-2KRD3SAO-CX2PxuDw.js} +1 -1
  59. package/payload/server/public/assets/{chunk-336JU56O-DE-lcZAc.js → chunk-336JU56O-D8l6U4Ne.js} +2 -2
  60. package/payload/server/public/assets/chunk-426QAEUC-CPhOHriU.js +1 -0
  61. package/payload/server/public/assets/{chunk-4BX2VUAB--ANCzzyn.js → chunk-4BX2VUAB-CG32Lkbh.js} +1 -1
  62. package/payload/server/public/assets/{chunk-4TB4RGXK-DTO_X80J.js → chunk-4TB4RGXK-BnAQbO-v.js} +1 -1
  63. package/payload/server/public/assets/{chunk-55IACEB6-4J3nrz44.js → chunk-55IACEB6-ejqRH2M5.js} +1 -1
  64. package/payload/server/public/assets/{chunk-5FUZZQ4R-jH_eqAHL.js → chunk-5FUZZQ4R-ClkicQ-T.js} +1 -1
  65. package/payload/server/public/assets/{chunk-5PVQY5BW-C-qU6p6k.js → chunk-5PVQY5BW-Be-mE_rx.js} +1 -1
  66. package/payload/server/public/assets/{chunk-67CJDMHE-DXxH9Snb.js → chunk-67CJDMHE-DPsemed2.js} +1 -1
  67. package/payload/server/public/assets/{chunk-7N4EOEYR-CCkxuP7A.js → chunk-7N4EOEYR-BQRQJETl.js} +1 -1
  68. package/payload/server/public/assets/{chunk-AA7GKIK3-CyItuM8d.js → chunk-AA7GKIK3-B1wcAVCK.js} +1 -1
  69. package/payload/server/public/assets/{chunk-BSJP7CBP-Bg7gxYNf.js → chunk-BSJP7CBP-DIua053h.js} +1 -1
  70. package/payload/server/public/assets/{chunk-CIAEETIT-BAXbOqv5.js → chunk-CIAEETIT-C9GHSCS9.js} +1 -1
  71. package/payload/server/public/assets/{chunk-EDXVE4YY-vdoErQoc.js → chunk-EDXVE4YY-C1T3l1xj.js} +1 -1
  72. package/payload/server/public/assets/{chunk-ENJZ2VHE-Cz_z_6GK.js → chunk-ENJZ2VHE-8kPcXEQC.js} +1 -1
  73. package/payload/server/public/assets/{chunk-FMBD7UC4-Ck2jvOgW.js → chunk-FMBD7UC4-Bhoeq9sD.js} +1 -1
  74. package/payload/server/public/assets/{chunk-FOC6F5B3-DucR0Qo8.js → chunk-FOC6F5B3-BIBpJi8I.js} +1 -1
  75. package/payload/server/public/assets/{chunk-ICPOFSXX-DolYDD7g.js → chunk-ICPOFSXX-DELXp6fj.js} +2 -2
  76. package/payload/server/public/assets/{chunk-K5T4RW27-FRzx7E5I.js → chunk-K5T4RW27-DdckXldP.js} +1 -1
  77. package/payload/server/public/assets/{chunk-KGLVRYIC-Bb5S4Bmc.js → chunk-KGLVRYIC-BJhXejKT.js} +1 -1
  78. package/payload/server/public/assets/{chunk-LIHQZDEY-c8VZJ-lU.js → chunk-LIHQZDEY-BSd8nyM2.js} +1 -1
  79. package/payload/server/public/assets/{chunk-ORNJ4GCN-BAeh-LZ1.js → chunk-ORNJ4GCN-CBi55uxq.js} +1 -1
  80. package/payload/server/public/assets/{chunk-OYMX7WX6-BUg7Kysh.js → chunk-OYMX7WX6-Dfbzl8xh.js} +1 -1
  81. package/payload/server/public/assets/chunk-QZHKN3VN-CDnAAZVJ.js +1 -0
  82. package/payload/server/public/assets/{chunk-U2HBQHQK-K4r17UTk.js → chunk-U2HBQHQK-BN1w8_Cq.js} +1 -1
  83. package/payload/server/public/assets/{chunk-X2U36JSP-Byh-CyxC.js → chunk-X2U36JSP-CrRay1NJ.js} +1 -1
  84. package/payload/server/public/assets/{chunk-XPW4576I-DC7lhr93.js → chunk-XPW4576I-C_ZE776O.js} +1 -1
  85. package/payload/server/public/assets/{chunk-YZCP3GAM-B2cu7AEP.js → chunk-YZCP3GAM-CUSlqWBY.js} +1 -1
  86. package/payload/server/public/assets/{chunk-ZZ45TVLE-DH_p8sFX.js → chunk-ZZ45TVLE-BdPUfSiD.js} +1 -1
  87. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BdXUKVrS.js +1 -0
  88. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DAq-uJr_.js +1 -0
  89. package/payload/server/public/assets/clone-DQl28-jK.js +1 -0
  90. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-DeM6JI00.js → cose-bilkent-S5V4N54A-D4E9htTz.js} +1 -1
  91. package/payload/server/public/assets/{dagre-3drs3e4F.js → dagre-D1B5TQL1.js} +1 -1
  92. package/payload/server/public/assets/{dagre-KV5264BT-C6S-VUtN.js → dagre-KV5264BT-ByuvYV7N.js} +1 -1
  93. package/payload/server/public/assets/data-CL7CyX1C.js +1 -0
  94. package/payload/server/public/assets/{diagram-5BDNPKRD-BkvA32I3.js → diagram-5BDNPKRD-B-0KduTa.js} +1 -1
  95. package/payload/server/public/assets/{diagram-G4DWMVQ6-CVwtRMPB.js → diagram-G4DWMVQ6-CSQmo3eB.js} +1 -1
  96. package/payload/server/public/assets/{diagram-MMDJMWI5-DJ1JVMbd.js → diagram-MMDJMWI5-DK0kWiB3.js} +1 -1
  97. package/payload/server/public/assets/{diagram-TYMM5635-rfj-crtX.js → diagram-TYMM5635-CYbK2reZ.js} +1 -1
  98. package/payload/server/public/assets/{erDiagram-SMLLAGMA-TJPuweS5.js → erDiagram-SMLLAGMA-C-JQHvJF.js} +1 -1
  99. package/payload/server/public/assets/{flatten-BZkoyJ4e.js → flatten-ROVzlo1u.js} +1 -1
  100. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-LQWEUjbV.js → flowDiagram-DWJPFMVM-OTOd2czg.js} +1 -1
  101. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-DdYsxKRw.js → ganttDiagram-T4ZO3ILL-BWJ778ou.js} +1 -1
  102. package/payload/server/public/assets/gitGraph-7Q5UKJZL-BM-KaDZy.js +1 -0
  103. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-sx8-fokM.js → gitGraphDiagram-UUTBAWPF-CkNcyISe.js} +1 -1
  104. package/payload/server/public/assets/graph-XZJZHzvv.js +51 -0
  105. package/payload/server/public/assets/{graph-labels-DigwgCWY.js → graph-labels-Cabqmf35.js} +1 -1
  106. package/payload/server/public/assets/{graphlib-DixeJdCf.js → graphlib-8z59Ng6C.js} +1 -1
  107. package/payload/server/public/assets/info-OMHHGYJF-CtZajyAB.js +1 -0
  108. package/payload/server/public/assets/infoDiagram-42DDH7IO-C-_WYlmI.js +2 -0
  109. package/payload/server/public/assets/{isEmpty-VRKFml5R.js → isEmpty-DXf_sApt.js} +1 -1
  110. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BKUCuKhZ.js → ishikawaDiagram-UXIWVN3A-Cg7pRRGp.js} +1 -1
  111. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-BafZP6pM.js → journeyDiagram-VCZTEJTY-CZT_4r_Z.js} +1 -1
  112. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DQ4c6wRv.js → kanban-definition-6JOO6SKY-Dp1Vd5YM.js} +1 -1
  113. package/payload/server/public/assets/{line-0_x1Wivu.js → line-UPAcwY66.js} +1 -1
  114. package/payload/server/public/assets/{linear-CZrU-J2V.js → linear-BeeahLKw.js} +1 -1
  115. package/payload/server/public/assets/maximize-2-DPe-Noig.js +1 -0
  116. package/payload/server/public/assets/{mermaid-parser.core-DwJ1cyfO.js → mermaid-parser.core-CLKle8df.js} +2 -2
  117. package/payload/server/public/assets/{mermaid.core-DnUst0He.js → mermaid.core-CwRj5rL5.js} +3 -3
  118. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-oc-GwerE.js → mindmap-definition-QFDTVHPH-CNQUY9UJ.js} +1 -1
  119. package/payload/server/public/assets/{operator-D-Ad9OQ5.js → operator-BM_CDyCb.js} +1 -1
  120. package/payload/server/public/assets/{ordinal-v4prQcuq.js → ordinal-B2r5L1v4.js} +1 -1
  121. package/payload/server/public/assets/packet-4T2RLAQJ-T-SYedp7.js +1 -0
  122. package/payload/server/public/assets/{page-BjAKwjlS.js → page-CMIbXzSg.js} +3 -3
  123. package/payload/server/public/assets/{page-CpSVszGe.js → page-E7I5sEVt.js} +1 -1
  124. package/payload/server/public/assets/{pdf-render-NQmflZ7K.js → pdf-render-m08Xm2Nm.js} +1 -1
  125. package/payload/server/public/assets/pie-ZZUOXDRM-BWECFVVd.js +1 -0
  126. package/payload/server/public/assets/{pieDiagram-DEJITSTG-BnBtQNHM.js → pieDiagram-DEJITSTG-Scnq58FX.js} +1 -1
  127. package/payload/server/public/assets/{public-CbkiISZZ.js → public-CrdWCSk2.js} +1 -1
  128. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CkNqtzHI.js → quadrantDiagram-34T5L4WZ-wPfZo_vs.js} +1 -1
  129. package/payload/server/public/assets/radar-PYXPWWZC-Dx0YUklX.js +1 -0
  130. package/payload/server/public/assets/{reduce-0Mm1v1Rr.js → reduce-CFlDlJf5.js} +1 -1
  131. package/payload/server/public/assets/{requirementDiagram-MS252O5E-D7qc27KT.js → requirementDiagram-MS252O5E-yYkH12K2.js} +1 -1
  132. package/payload/server/public/assets/{rotate-ccw-BYmlylsD.js → rotate-ccw-Dwtj0NJ7.js} +1 -1
  133. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-2Y4sqRY7.js → sankeyDiagram-XADWPNL6-fVWRBzzV.js} +1 -1
  134. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-hXOiSHuT.js → sequenceDiagram-FGHM5R23-Dbap3X6h.js} +1 -1
  135. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Dq4hhkLA.js → stateDiagram-FHFEXIEX-DkPLAqIh.js} +1 -1
  136. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DceWO1u6.js +1 -0
  137. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-Dn9wBUEw.js → timeline-definition-GMOUNBTQ-DabKBJyd.js} +1 -1
  138. package/payload/server/public/assets/treeView-SZITEDCU-C3197Rac.js +1 -0
  139. package/payload/server/public/assets/treemap-W4RFUUIX-BPrpx29h.js +1 -0
  140. package/payload/server/public/assets/{useSubAccountSwitcher-DxHQSOzT.css → useSubAccountSwitcher-BH-YjP0H.css} +1 -1
  141. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BKVHJiIt.js → vennDiagram-DHZGUBPP-CaKT7c-c.js} +1 -1
  142. package/payload/server/public/assets/wardley-RL74JXVD-DW4Jlbhw.js +1 -0
  143. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Bn9vvpdK.js → wardleyDiagram-NUSXRM2D-BZ5TTSOL.js} +1 -1
  144. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-FIoMdPB3.js → xychartDiagram-5P7HB3ND-CLfeklJ9.js} +1 -1
  145. package/payload/server/public/browser.html +4 -4
  146. package/payload/server/public/calendar.html +5 -5
  147. package/payload/server/public/chat.html +10 -9
  148. package/payload/server/public/data.html +9 -8
  149. package/payload/server/public/graph.html +9 -8
  150. package/payload/server/public/index.html +12 -11
  151. package/payload/server/public/operator.html +12 -11
  152. package/payload/server/public/public.html +10 -9
  153. package/payload/server/server.js +41 -6
  154. package/payload/server/public/assets/architecture-YZFGNWBL-DdgmZ93B.js +0 -1
  155. package/payload/server/public/assets/channel-Da-UUyqx.js +0 -1
  156. package/payload/server/public/assets/chat-L6IVvC9k.js +0 -1
  157. package/payload/server/public/assets/chevron-left-uJSGyCGe.js +0 -1
  158. package/payload/server/public/assets/chunk-426QAEUC-B1kFAxo1.js +0 -1
  159. package/payload/server/public/assets/chunk-QZHKN3VN-BjE-Glwf.js +0 -1
  160. package/payload/server/public/assets/classDiagram-6PBFFD2Q-Dol3Ogeh.js +0 -1
  161. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-VUMj7gWH.js +0 -1
  162. package/payload/server/public/assets/clone-m7oGrdBC.js +0 -1
  163. package/payload/server/public/assets/data-4K_negqE.js +0 -1
  164. package/payload/server/public/assets/gitGraph-7Q5UKJZL-cJEaiQq4.js +0 -1
  165. package/payload/server/public/assets/graph-UHR4Ez-V.js +0 -51
  166. package/payload/server/public/assets/info-OMHHGYJF-BUxfOTKa.js +0 -1
  167. package/payload/server/public/assets/infoDiagram-42DDH7IO-CD-d3Rgf.js +0 -2
  168. package/payload/server/public/assets/packet-4T2RLAQJ-B9U_2639.js +0 -1
  169. package/payload/server/public/assets/pie-ZZUOXDRM-Glh-lJOT.js +0 -1
  170. package/payload/server/public/assets/radar-PYXPWWZC-CTSgob8w.js +0 -1
  171. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DTj9o2vZ.js +0 -1
  172. package/payload/server/public/assets/treeView-SZITEDCU-CXApkW1l.js +0 -1
  173. package/payload/server/public/assets/treemap-W4RFUUIX-DzvTXAcT.js +0 -1
  174. package/payload/server/public/assets/wardley-RL74JXVD-BExXV4oI.js +0 -1
  175. /package/payload/server/public/assets/{_baseFor-C0Y1mc3g.js → _baseFor-BVuIxZJh.js} +0 -0
  176. /package/payload/server/public/assets/{array-DqLCdDFv.js → array-B9UHiPd-.js} +0 -0
  177. /package/payload/server/public/assets/{cytoscape.esm-Cn5b4Cdu.js → cytoscape.esm-D5_TRwB-.js} +0 -0
  178. /package/payload/server/public/assets/{defaultLocale-CrJzLgRD.js → defaultLocale-5eAKkKJC.js} +0 -0
  179. /package/payload/server/public/assets/{dist-Dlks0Bfw.js → dist-CADCHk0t.js} +0 -0
  180. /package/payload/server/public/assets/{init-C0r9Gk5G.js → init-DlZdxViB.js} +0 -0
  181. /package/payload/server/public/assets/{katex-BFwFoEts.js → katex-Xo0SyWvP.js} +0 -0
  182. /package/payload/server/public/assets/{path-DIKpVbHL.js → path-6uRLdFF7.js} +0 -0
  183. /package/payload/server/public/assets/{preload-helper-C5gCWwwF.js → preload-helper-CvXuAEud.js} +0 -0
  184. /package/payload/server/public/assets/{rough.esm-DsHEmXUZ.js → rough.esm-Djo4Abte.js} +0 -0
  185. /package/payload/server/public/assets/{src-Bv7ZWAzF.js → src-Bi0fLV9t.js} +0 -0
  186. /package/payload/server/public/assets/{useSubAccountSwitcher-BRWAIbzr.js → useSubAccountSwitcher-BVCMAnWK.js} +0 -0
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: socials
3
+ description: Render share-ready Open Graph and social tiles plus per-platform captions for a named subject — a built site or a bespoke topic — with a PII gate for any source that can carry client data. Resolves the subject through a source adapter into candidate images and copy, overlays brand typography, screenshots landscape (1200×628), square (1200×1200) and portrait (1080×1350) PNG tiles via the on-device browser, assembles a gallery and a captions file for all five platforms, and emits a redaction report reconciling every source item. For a built site it also wires the site's og:image. Never posts — the bundle is for operator review. Trigger phrases include "og images for the site", "Instagram tiles + captions for <subject>", "social collateral for <business>".
4
+ allowed-tools:
5
+ - Bash
6
+ - Read
7
+ - Write
8
+ - Edit
9
+ - Glob
10
+ - Grep
11
+ - mcp__plugin_browser_browser__browser-render
12
+ - mcp__plugin_browser_browser__browser-navigate
13
+ - mcp__plugin_browser_browser__browser-evaluate
14
+ - mcp__plugin_browser_browser__browser-resize
15
+ - mcp__plugin_browser_browser__browser-screenshot
16
+ - mcp__plugin_browser_browser__browser-wait-for
17
+ ---
18
+
19
+ # socials
20
+
21
+ One skill, one subject at a time. You take a named **subject**, resolve it through a
22
+ source adapter, and emit a publication bundle: exact-size PNG social tiles, captions
23
+ for all five platforms, a gallery, and — for a subject that can carry client data —
24
+ a redaction report. You **never post**. The bundle is for the operator to review
25
+ and sign off. You ship no business content; every fact is the operator's own data,
26
+ read from the resolved source.
27
+
28
+ ## Inputs
29
+
30
+ - **subject** — a name (the business, the topic).
31
+ - **mode** — one of `microsite`, `bespoke`.
32
+ - **material** — the path the adapter reads (a built `site/`, or the operator's own
33
+ photos and notes).
34
+ - **output** — where the bundle lands (for `microsite`, the site's own `site/`).
35
+
36
+ ## 1. Resolve the subject, and echo it
37
+
38
+ Resolve the subject through its adapter in [`references/sources.md`](references/sources.md)
39
+ to the normalized intermediate (`topic`, headline/sub candidates, copy facts,
40
+ candidate images, `piiBearing`). **Before you render anything, state the resolved
41
+ subject and the source mode back** — e.g. "Resolving Meridian Advisory in
42
+ `microsite` mode from its built site." A mis-resolution must be visible here, not
43
+ discovered in a finished tile.
44
+
45
+ ## 2. PII gate (if `piiBearing`)
46
+
47
+ If the adapter's `piiBearing` is true (`bespoke`, unless the operator declared the
48
+ material brand-safe), run the gate in [`references/pii-strip.md`](references/pii-strip.md)
49
+ over **every** candidate image and **every** copy fact — before any of it reaches a
50
+ template. Write `redaction-report.md` reconciling every source photo and text field
51
+ as *used / redacted / rejected*. If every photo is rejected, stop and ask for a
52
+ usable image; never emit an imageless bundle. **Do not present a bundle without the
53
+ report.** `microsite` skips this step (the site is already published copy).
54
+
55
+ ## 3. Render the tiles (browser plugin, PNG)
56
+
57
+ 1. Build the resolved render page: copy [`templates/og.html`](templates/og.html) to
58
+ `<output>/og.html` and substitute `{{ CANDIDATES_JSON }}` with the vetted
59
+ candidate set (`{ key: { img, pos, head, sub }, … }`) plus the scalar tokens
60
+ (palette, eyebrow, specs, domain, phone). The logo and hero images resolve from
61
+ the output dir's `images/`.
62
+ 2. Serve `<output>/` over a local HTTP server so relative paths and webfonts load
63
+ (`cd <output> && python3 -m http.server <port>` via `Bash`; `file://` will not
64
+ load the fonts).
65
+ 3. For each tile, drive the on-device browser:
66
+ - `browser-navigate` to `http://localhost:<port>/og.html?format=F&hero=H`,
67
+ - `browser-resize` to the exact size — landscape `1200×628`, square `1200×1200`,
68
+ portrait `1080×1350`,
69
+ - `browser-evaluate` `document.fonts.ready.then(() => true)` (and a short settle)
70
+ so the webfonts have landed before capture,
71
+ - `browser-screenshot` to `<output>/socials/og-F-H.png`.
72
+ A **person** hero (`isPerson: true`) renders **portrait only** — a landscape or
73
+ square crop cuts off the head.
74
+ 4. `Read` each finished PNG to confirm it rendered: logo legible, copy readable,
75
+ scrim adequate. A **sub-20KB** file is a blank page (server not running, wrong
76
+ port, or the page threw) — diagnose before re-running, never ship it.
77
+
78
+ ## 4. Captions — all five platforms, plain register
79
+
80
+ Build `<output>/social-posts.md` from [`templates/social-posts.md`](templates/social-posts.md):
81
+ one block per tile, with copy for **Instagram, LinkedIn, TikTok, Facebook, and X**.
82
+ Write every line in the plain register (the `plainly` skill): no em-dashes, no
83
+ inflation vocabulary, no "it's not X, it's Y", no rhetorical openers, no emoji faking
84
+ enthusiasm. For a `piiBearing` subject, no caption names the client, the address or
85
+ postcode, any figure, any individual, or any email sender.
86
+
87
+ ## 5. Gallery, and (microsite only) the og:image
88
+
89
+ Build `<output>/socials.html` from [`templates/socials.html`](templates/socials.html):
90
+ repeat the group block once per tile key. For a **person** hero (`isPerson: true`),
91
+ write only the portrait `<div class="tile">` cell for that key — delete the landscape
92
+ and square cells, since those files are never rendered for a person.
93
+
94
+ The OG snippet (`<div class="snippet">…</div>`) is **microsite only**. In `microsite`
95
+ mode, keep it, render the tiles into the site's `site/socials/`, and set the site's
96
+ `og:image`/`twitter:image` to `<WEBSITE_URL>socials/og-landscape-main.png` (a true
97
+ 1200×628 card beats a raw hero for link previews; `WEBSITE_URL` is the site's absolute
98
+ URL from the brand pack). For a **bespoke** bundle there is no site to wire —
99
+ **delete the whole `<div class="snippet">` block** from the copied template before
100
+ substituting, so no `WEBSITE_URL` token survives.
101
+
102
+ ## Output contract
103
+
104
+ ```
105
+ <output>/
106
+ og.html # the resolved render page (kept for re-runs)
107
+ socials/
108
+ og-landscape-<key>.png og-square-<key>.png og-portrait-<key>.png # per tile
109
+ og-portrait-<person-key>.png # a person hero: portrait only
110
+ socials.html # gallery: each tile at size, download links
111
+ social-posts.md # captions per tile, all five platforms
112
+ redaction-report.md # piiBearing (bespoke) subjects only
113
+ ```
114
+
115
+ ## Completion criteria
116
+
117
+ - Every tile exists at its exact pixel size; a person tile is portrait only.
118
+ - Captions cover all five platforms; `socials.html` previews each tile with a working
119
+ download link.
120
+ - For a `piiBearing` subject, `redaction-report.md` is present and accounts for
121
+ **every** source photo and text field; no output names the client, the address, a
122
+ figure, an individual, or an email sender.
123
+ - In `microsite` mode, the site's `og:image`/`twitter:image` point at the landscape
124
+ main tile.
125
+ - `Read` each finished PNG — file size alone is not proof.
126
+
127
+ ## When NOT to use
128
+
129
+ - The operator wants to **post** — this skill never posts; it produces a reviewable
130
+ bundle and stops.
131
+ - The deliverable is the site itself → `web-design`. The full run (site + tiles) →
132
+ `web-intake`, which calls this skill in `microsite` mode.
133
+
134
+ ## Editions
135
+
136
+ Tile rendering drives the on-device `browser` plugin. Inside maxy-code that plugin is
137
+ present. A standalone (marketplace) install needs a browser MCP to render tiles; this
138
+ is a stated host dependency for the `socials` skill, not a silent gap. The `web-design`
139
+ build and its deploy do not depend on it.
@@ -0,0 +1,64 @@
1
+ # socials — PII gate
2
+
3
+ The gate runs for every `piiBearing` subject ([`sources.md`](sources.md)): `bespoke`
4
+ unless the operator declares the material brand-safe. It runs **before** anything
5
+ reaches a template — over every candidate image and every copy fact. `microsite`
6
+ skips it (the site is already published copy).
7
+
8
+ Detection is the agent's own visual and textual review against the taxonomy below
9
+ plus operator confirmation. No computer-vision model ships; the operator-approval
10
+ gate is the backstop. The skill **never posts** — the bundle is for review.
11
+
12
+ ## Text never emitted
13
+
14
+ "Rendered-tile copy" means **every** text field that reaches a tile or a caption:
15
+ the headline and sub (from the candidate set), the eyebrow, the four spec tags
16
+ (`copyFacts.specs[]`), and every platform caption. The gate reviews all of them.
17
+
18
+ No caption and no rendered-tile copy may contain:
19
+
20
+ - the **client name** (person or company),
21
+ - the **site address or postcode** (or any fragment that identifies the address),
22
+ - any **contract, invoice, or payment figure** — any currency amount, any
23
+ percentage that reveals a financial position,
24
+ - any **named individual** (worker, client, neighbour, surveyor),
25
+ - any **email sender** address or name lifted from an instruction email.
26
+
27
+ The work is the story. "New studio open on the high street" is publishable.
28
+ "Invoice 3 for the Smiths at 14 Mill Lane, £42,310 due" is not.
29
+
30
+ ## Photos that are rejected
31
+
32
+ Reject a candidate image (do not crop around it — reject the whole photo) if it
33
+ shows:
34
+
35
+ - a recognizable **face**,
36
+ - a **vehicle number plate**,
37
+ - a **house number, street sign, or nameplate**,
38
+ - **visible client paperwork** — a letter, an invoice, a drawing with a title block,
39
+ a screen showing client data.
40
+
41
+ A photo of the work itself — a finished space, a product, a completed project —
42
+ with none of the above is usable.
43
+
44
+ ## The redaction report
45
+
46
+ Every `piiBearing` run writes `redaction-report.md` to the output. It reconciles
47
+ **every** source photo and **every** source text field to one disposition:
48
+
49
+ - **used** — appears in the bundle (which tile / caption),
50
+ - **redacted** — the source carried PII that was removed before use (name the field
51
+ and what was stripped),
52
+ - **rejected** — not used at all (name the reason: which photo category, which text
53
+ rule).
54
+
55
+ The report is the standing reconciliation that makes a silent leak visible: a source
56
+ PII field that appears in an output but is absent from the report's redacted/rejected
57
+ list is the failure signature. A **missing or empty report is itself a failure** —
58
+ the skill refuses to present a bundle without it.
59
+
60
+ ## When every photo is rejected
61
+
62
+ If vetting rejects every candidate image, stop and ask the operator for a usable
63
+ photo. Never emit an imageless bundle, and never fall back to a stock or invented
64
+ image — the report shows all photos *rejected* and no tile is written.
@@ -0,0 +1,66 @@
1
+ # socials — source adapters
2
+
3
+ The `socials` skill resolves a named **subject** to a normalized intermediate that
4
+ the shared render core consumes. One adapter per subject kind. This file is the
5
+ contract and the **extension point**: a future subject (a testimonial, a launch, an
6
+ event) is a new row here, not a new skill.
7
+
8
+ ## The normalized intermediate
9
+
10
+ Every adapter resolves its subject to this shape:
11
+
12
+ ```
13
+ {
14
+ topic, // one short phrase naming what the tiles are about
15
+ headlineCandidates, // [string] — lead lines, strongest first
16
+ subCandidates, // [string] — supporting lines, paired to headlines
17
+ copyFacts, // { eyebrow, specs[], domain, phone } — overlay scalars
18
+ candidateImages, // [{ key, path, isPerson }] — vetted backgrounds
19
+ piiBearing // boolean — does this source carry client data?
20
+ }
21
+ ```
22
+
23
+ `candidateImages[].key` becomes the tile `hero` and the filename suffix
24
+ (`og-<format>-<key>.png`). `isPerson: true` marks a hero that must render
25
+ **portrait only** (a landscape or square crop cuts off the head). `piiBearing`
26
+ decides whether the PII gate in [`pii-strip.md`](pii-strip.md) runs before any of
27
+ this reaches a template.
28
+
29
+ ## Adapters
30
+
31
+ ### `microsite` — `piiBearing: false`
32
+
33
+ Reads a built `site/` directory (the output of `web-design`): the brand pack
34
+ (`brand.md` tokens, `images/logo.png`) and the curated hero photos at the fixed
35
+ paths the site uses:
36
+
37
+ | key | image | leads with |
38
+ |---|---|---|
39
+ | `main` | `images/hero-01.webp` | the business's lead line |
40
+ | `detail` | `images/hero-03.webp` | a quality / detail line |
41
+ | `work` | `images/hero-06.webp` | a what-we-do line |
42
+ | `founder` | `images/founder-portrait.webp` | "meet the…" — **`isPerson: true`, portrait only** |
43
+
44
+ `copyFacts` for `microsite` is the brand pack's own credential cells (sector,
45
+ services, areas, years) plus `domain`, `phone`, and the site's full `WEBSITE_URL`
46
+ (the microsite-only og:image wiring needs the absolute URL, not just the domain).
47
+
48
+ Brand-safe by nature (the site is already published copy), so the PII gate is
49
+ skipped. This is the path `web-intake` calls as its final stage; it also wires the
50
+ site's `og:image`/`twitter:image` to the landscape `main` tile. If the site has no
51
+ figurehead, drop the `founder` key.
52
+
53
+ ### `bespoke` — `piiBearing: true` (operator may waive)
54
+
55
+ An operator-chosen topic with the operator's own photos and notes. The operator
56
+ supplies the topic, the headline/sub ideas, and the candidate images. The PII gate
57
+ runs by default; the operator may declare the material brand-safe (their own
58
+ marketing photos, no client identifiable) to skip it — that declaration is the only
59
+ way the gate is waived for a bespoke subject.
60
+
61
+ ## Adding a subject later
62
+
63
+ A new subject kind (testimonial, launch, event, …) is a new adapter section here:
64
+ name what it reads, how it maps to the normalized intermediate, and its `piiBearing`
65
+ value. No new skill, no new render core, no new caption register. If the source can
66
+ carry client data, set `piiBearing: true` and the existing gate covers it.
@@ -0,0 +1,92 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-GB">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>{{ COMPANY_SHORT }} social tile renderer</title>
6
+ <link rel="preconnect" href="https://fonts.googleapis.com">
7
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8
+ <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400;1,500&family=Lora:ital,wght@0,400;1,400&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ /* Brand tokens — substituted per subject by the resolving adapter. */
11
+ :root{
12
+ --ink:{{ INK_900 }}; --ink-2:{{ INK_700 }}; --paper:{{ PAPER_100 }}; --paper-2:{{ PAPER_200 }};
13
+ --copper:{{ ACCENT }}; --copper-soft:{{ ACCENT_SOFT }}; --brand-grey:{{ BRAND_GREY }};
14
+ --serif:'Cormorant Garamond',Garamond,serif; --body:'Lora',Georgia,serif; --sans:'Inter',system-ui,sans-serif;
15
+ }
16
+ *{margin:0;padding:0;box-sizing:border-box;}
17
+ html,body{background:var(--ink);}
18
+ body{display:flex;align-items:flex-start;justify-content:flex-start;}
19
+ .stage{position:relative;overflow:hidden;background:var(--ink);color:#fff;}
20
+ .stage.landscape{width:1200px;min-width:1200px;max-width:1200px;height:628px;min-height:628px;max-height:628px;}
21
+ .stage.square{width:1200px;min-width:1200px;max-width:1200px;height:1200px;min-height:1200px;max-height:1200px;}
22
+ .stage.portrait{width:1080px;min-width:1080px;max-width:1080px;height:1350px;min-height:1350px;max-height:1350px;}
23
+ .hero{position:absolute;inset:0;background-size:cover;background-position:center;}
24
+ .scrim{position:absolute;inset:0;
25
+ background:linear-gradient(180deg, rgba(0,0,0,.22) 0%, rgba(0,0,0,.10) 32%, rgba(0,0,0,.55) 72%, rgba(0,0,0,.84) 100%);}
26
+ /* real brand logo on a white chip so it reads on any photo */
27
+ .logo-chip{position:absolute;top:48px;left:64px;background:#fff;padding:16px 22px;
28
+ box-shadow:0 6px 22px rgba(0,0,0,.30);border-top:3px solid var(--copper);}
29
+ .logo-chip img{display:block;height:84px;width:auto;}
30
+ .copy{position:absolute;left:64px;right:64px;bottom:60px;}
31
+ .eyebrow{font-family:var(--sans);font-weight:600;font-size:17px;letter-spacing:.30em;text-transform:uppercase;color:var(--copper);margin-bottom:18px;}
32
+ .headline{font-family:var(--serif);font-style:italic;font-weight:500;font-size:72px;line-height:1.02;color:#fff;}
33
+ .sub{font-family:var(--body);font-style:italic;font-size:26px;color:#fff;opacity:.94;margin-top:14px;}
34
+ .rule{width:64px;height:2px;background:var(--copper);margin:24px 0 20px;}
35
+ .specs{font-family:var(--sans);font-weight:500;font-size:16px;letter-spacing:.10em;text-transform:uppercase;color:#fff;opacity:.92;}
36
+ .specs span+span::before{content:"·";color:var(--copper);margin:0 9px;}
37
+ .foot{display:flex;justify-content:space-between;align-items:center;margin-top:26px;padding-top:18px;
38
+ border-top:1px solid rgba(255,255,255,.28);
39
+ font-family:var(--sans);font-weight:600;font-size:15px;letter-spacing:.16em;text-transform:uppercase;color:#fff;}
40
+ .foot .r{color:var(--copper);}
41
+ .stage.square .headline,.stage.portrait .headline{font-size:84px;}
42
+ .stage.square .sub,.stage.portrait .sub{font-size:30px;}
43
+ .stage.square .eyebrow,.stage.portrait .eyebrow{font-size:19px;}
44
+ .stage.square .logo-chip img,.stage.portrait .logo-chip img{height:62px;}
45
+ .stage.portrait .copy{bottom:78px;}
46
+ .stage.square .copy{bottom:72px;}
47
+ </style>
48
+ </head>
49
+ <body>
50
+ <div class="stage" id="stage">
51
+ <div class="hero" id="hero"></div>
52
+ <div class="scrim"></div>
53
+ <div class="logo-chip"><img src="images/logo.png" alt="{{ COMPANY_NAME }}"></div>
54
+ <div class="copy">
55
+ <div class="eyebrow">{{ TILE_EYEBROW }}</div>
56
+ <h1 class="headline" id="headline"></h1>
57
+ <div class="sub" id="sub"></div>
58
+ <div class="rule"></div>
59
+ <div class="specs"><span>{{ TILE_SPEC_1 }}</span><span>{{ TILE_SPEC_2 }}</span><span>{{ TILE_SPEC_3 }}</span><span>{{ TILE_SPEC_4 }}</span></div>
60
+ <div class="foot"><span>{{ WEBSITE_DOMAIN }}</span><span class="r">{{ PHONE_DISPLAY }}</span></div>
61
+ </div>
62
+ </div>
63
+ <script>
64
+ // CANDIDATES is substituted by the socials skill AFTER the PII gate. It is an
65
+ // object keyed by hero name; each entry names a background image (a path that
66
+ // resolves from this file's directory) and the headline/sub it leads with:
67
+ // { main: { img:'images/hero-01.webp', pos:'center', head:'…', sub:'…' }, … }
68
+ // For a built site the adapter supplies the brand themes; for a bespoke
69
+ // subject it supplies the vetted candidate images and the copy facts that
70
+ // survived redaction. No client PII ever reaches this object — the gate strips
71
+ // it upstream.
72
+ //
73
+ // A person hero (a founder portrait, a person) must be rendered
74
+ // PORTRAIT ONLY: a landscape or square crop cuts off the head. The socials
75
+ // skill enforces this by only emitting the portrait format for a person hero.
76
+ const CANDIDATES = {{ CANDIDATES_JSON }};
77
+ // ?format=landscape|square|portrait & ?hero=<key from CANDIDATES>
78
+ const q = new URLSearchParams(location.search);
79
+ const keys = Object.keys(CANDIDATES);
80
+ const fmt = q.get('format') || 'landscape';
81
+ const hero = q.get('hero') || keys[0];
82
+ const h = CANDIDATES[hero] || CANDIDATES[keys[0]];
83
+ const stage = document.getElementById('stage');
84
+ stage.classList.add(fmt);
85
+ const heroEl = document.getElementById('hero');
86
+ heroEl.style.backgroundImage = "url('" + h.img + "')";
87
+ heroEl.style.backgroundPosition = h.pos || 'center';
88
+ document.getElementById('headline').textContent = h.head;
89
+ document.getElementById('sub').textContent = h.sub;
90
+ </script>
91
+ </body>
92
+ </html>
@@ -0,0 +1,46 @@
1
+ # {{ COMPANY_NAME }} — social captions
2
+
3
+ Paste-ready copy for each tile. Phone {{ PHONE_DISPLAY }} · {{ WEBSITE_DOMAIN }}
4
+ {{ FACEBOOK_HANDLE }} · {{ INSTAGRAM_HANDLE }} · {{ TIKTOK_HANDLE }}
5
+
6
+ Write every caption in the plain register (the `plainly` skill): no em-dashes, no
7
+ inflation words (discover, unlock, nestled, stunning, boasts, bespoke-where-it-
8
+ isn't), no "it's not X, it's Y", no rhetorical openers, no emoji used to fake
9
+ enthusiasm. Read each one aloud; if it sounds like a marketing bot, rewrite it.
10
+
11
+ For a PII-bearing (bespoke) subject no caption may name the client, the address
12
+ or postcode, any contract / invoice / payment figure, any named individual, or
13
+ any email sender. The work is the story; the people and the money are not.
14
+
15
+ Repeat the block below once per rendered tile. `{{ TILE_KEY }}` matches the tile
16
+ filename (`og-*-{{ TILE_KEY }}.png`); a person tile is portrait only, so its block
17
+ references the portrait image alone.
18
+
19
+ ## Per-platform shape (a cap, not a target)
20
+
21
+ - **Instagram (feed + portrait)** — name first, then what is visible, then "link in
22
+ bio" if there is a site, then 4–6 hashtags. The portrait line leads with what is
23
+ in that specific hero.
24
+ - **LinkedIn** — three short paragraphs, professional, plain.
25
+ - **TikTok** — one short hook line plus 3–5 hashtags.
26
+ - **Facebook** — three short paragraphs, warmer than LinkedIn.
27
+ - **X** — under 280 characters, one clear line.
28
+
29
+ ---
30
+
31
+ ## {{ TILE_HEAD }} — `og-*-{{ TILE_KEY }}.png`
32
+
33
+ **Instagram (feed + portrait)**
34
+ {{ TILE_KEY_INSTAGRAM }}
35
+
36
+ **LinkedIn**
37
+ {{ TILE_KEY_LINKEDIN }}
38
+
39
+ **TikTok**
40
+ {{ TILE_KEY_TIKTOK }}
41
+
42
+ **Facebook**
43
+ {{ TILE_KEY_FACEBOOK }}
44
+
45
+ **X**
46
+ {{ TILE_KEY_X }}
@@ -0,0 +1,87 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-GB">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{ COMPANY_NAME }} · Social collateral</title>
7
+ <link rel="icon" href="favicon.png" type="image/png">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400&family=Lora:ital,wght@0,400;1,400&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
11
+ <style>
12
+ /* Brand tokens — substituted per subject by the resolving adapter. */
13
+ :root{
14
+ --ink:{{ INK_900 }}; --ink-2:{{ INK_700 }}; --slate:{{ INK_400 }}; --paper:{{ PAPER_100 }}; --paper-2:{{ PAPER_200 }};
15
+ --rule:{{ RULE_100 }}; --gold:{{ ACCENT_TEXT }}; --copper:{{ ACCENT }}; --brand-grey:{{ BRAND_GREY }};
16
+ --serif:'Cormorant Garamond',Garamond,serif; --body:'Lora',Georgia,serif; --sans:'Inter',system-ui,sans-serif;
17
+ }
18
+ *{margin:0;padding:0;box-sizing:border-box;}
19
+ body{font-family:var(--body);background:var(--paper-2);color:var(--ink);line-height:1.6;}
20
+ img{display:block;max-width:100%;height:auto;}
21
+ a{color:inherit;}
22
+ header{background:var(--paper);border-bottom:1px solid var(--rule);padding:22px 32px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:16px;}
23
+ header img{height:64px;width:auto;}
24
+ header .t{font-family:var(--sans);font-size:11px;font-weight:600;letter-spacing:.28em;text-transform:uppercase;color:var(--brand-grey);}
25
+ .wrap{max-width:1100px;margin:0 auto;padding:48px 32px 64px;}
26
+ .lede{font-family:var(--serif);font-style:italic;font-size:28px;color:var(--ink);margin-bottom:8px;}
27
+ .lede::after{content:'';display:block;width:40px;height:2px;background:var(--copper);margin-top:16px;}
28
+ .intro{font-size:15px;color:var(--ink-2);max-width:680px;margin-bottom:14px;}
29
+ .snippet{background:var(--ink);color:#fff;border-radius:2px;padding:20px 22px;margin:22px 0 40px;overflow-x:auto;}
30
+ .snippet .h{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.24em;text-transform:uppercase;color:var(--copper);margin-bottom:12px;}
31
+ .snippet pre{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.7;color:#e9e6df;white-space:pre;}
32
+ .group{margin-top:48px;}
33
+ .group h2{font-family:var(--serif);font-style:italic;font-size:24px;color:var(--ink);margin-bottom:4px;}
34
+ .group h2 .role{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.24em;text-transform:uppercase;color:var(--copper);margin-left:12px;vertical-align:middle;}
35
+ .group .cap{font-size:13.5px;color:var(--ink-2);max-width:680px;margin:8px 0 20px;border-left:3px solid var(--copper);padding-left:14px;}
36
+ .tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:22px;align-items:end;}
37
+ .tile{background:var(--paper);border:1px solid var(--rule);padding:12px;}
38
+ .tile img{width:100%;border:1px solid var(--rule);}
39
+ .tile .meta{display:flex;align-items:center;justify-content:space-between;margin-top:10px;}
40
+ .tile .fmt{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--slate);}
41
+ .tile .dl{font-family:var(--sans);font-size:10px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--paper);background:var(--ink);text-decoration:none;padding:7px 12px;}
42
+ .tile .dl:hover{background:var(--copper);}
43
+ footer{border-top:1px solid var(--rule);padding:28px 32px;text-align:center;font-family:var(--sans);font-size:10px;font-weight:500;letter-spacing:.2em;text-transform:uppercase;color:var(--slate);}
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <header>
48
+ <img src="images/logo.png" alt="{{ COMPANY_NAME }}">
49
+ <span class="t">Social collateral</span>
50
+ </header>
51
+ <div class="wrap">
52
+ <div class="lede">{{ GALLERY_LEDE }}</div>
53
+ <p class="intro">{{ GALLERY_INTRO }} Landscape (1200×628) for Facebook, LinkedIn and link previews; square (1200×1200) for the Instagram feed; portrait (1080×1350) for Instagram and Reels covers. Captions for every tile are in <a href="social-posts.md" style="text-decoration:underline;text-decoration-color:rgba(0,0,0,.25);">social-posts.md</a>.</p>
54
+
55
+ <!--
56
+ OG SNIPPET — microsite mode only. A bespoke bundle has no site to wire, so
57
+ omit this whole block for a bespoke subject. When present, it points at the
58
+ landscape main tile (PNG).
59
+ -->
60
+ <div class="snippet">
61
+ <div class="h">Paste into the page &lt;head&gt; for link previews</div>
62
+ <pre>&lt;meta property="og:image" content="{{ WEBSITE_URL }}socials/og-landscape-main.png"&gt;
63
+ &lt;meta property="og:image:width" content="1200"&gt;
64
+ &lt;meta property="og:image:height" content="628"&gt;
65
+ &lt;meta name="twitter:card" content="summary_large_image"&gt;
66
+ &lt;meta name="twitter:image" content="{{ WEBSITE_URL }}socials/og-landscape-main.png"&gt;</pre>
67
+ </div>
68
+
69
+ <!--
70
+ GROUP BLOCK — repeat once per resolved tile key. {{ TILE_KEY }} matches the
71
+ filename suffix; {{ TILE_HEAD }} / {{ TILE_CAPTION }} are the subject's copy.
72
+ A standard tile has all three formats. A PERSON tile is portrait only: emit
73
+ just the portrait cell for it (drop the landscape and square cells).
74
+ -->
75
+ <div class="group">
76
+ <h2>{{ TILE_HEAD }} <span class="role">{{ TILE_KEY }}</span></h2>
77
+ <p class="cap">{{ TILE_CAPTION }}</p>
78
+ <div class="tiles">
79
+ <div class="tile"><img src="socials/og-landscape-{{ TILE_KEY }}.png" alt="{{ TILE_HEAD }}, landscape tile"><div class="meta"><span class="fmt">Landscape · 1200×628</span><a class="dl" href="socials/og-landscape-{{ TILE_KEY }}.png" download>Download</a></div></div>
80
+ <div class="tile"><img src="socials/og-square-{{ TILE_KEY }}.png" alt="{{ TILE_HEAD }}, square tile"><div class="meta"><span class="fmt">Square · 1200×1200</span><a class="dl" href="socials/og-square-{{ TILE_KEY }}.png" download>Download</a></div></div>
81
+ <div class="tile"><img src="socials/og-portrait-{{ TILE_KEY }}.png" alt="{{ TILE_HEAD }}, portrait tile"><div class="meta"><span class="fmt">Portrait · 1080×1350</span><a class="dl" href="socials/og-portrait-{{ TILE_KEY }}.png" download>Download</a></div></div>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ <footer>© {{ COPYRIGHT_YEAR }} {{ COMPANY_LEGAL_NAME }} · {{ TAGLINE }}</footer>
86
+ </body>
87
+ </html>
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: web-design
3
+ description: Build a finished, scrollable single-page website for any business from the placeholder template, a brand pack, and the operator's curated photos and copy. Produces a self-contained site/ directory — index.html (hero rotator, about, what-we-do, how-we-work, optional founder section, testimonial, services, key facts, FAQ, CTA, footer, back-to-top) plus an llms.txt — with structured data (JSON-LD) whose type is operator-supplied. Domain-neutral, so no business category is baked in. Use after brand-extract, or directly when the brand pack and copy already exist. Trigger phrases include "build the site", "lay out the landing page", "turn this brand plus photos into a one-page site".
4
+ allowed-tools:
5
+ - Bash
6
+ - Read
7
+ - Write
8
+ - Edit
9
+ - Glob
10
+ - Grep
11
+ ---
12
+
13
+ # web-design
14
+
15
+ Renders the canonical single-page site from `templates/index.html` and
16
+ `templates/llms.txt` by substituting every `{{ PLACEHOLDER }}`. The template is
17
+ the spec: copy it, fill it, never rebuild it from scratch from this prose.
18
+
19
+ ## Canonical template — the only starting point
20
+
21
+ `templates/index.html` ships with this skill. It encodes the entire site —
22
+ structure, CSS, the JS hero rotator + drawer + sticky sub-nav + back-to-top, the
23
+ responsive rules, and the JSON-LD — with every business-specific value as a
24
+ `{{ TOKEN }}`. Copy it to `<output>/site/index.html` and substitute. Do the same
25
+ with `templates/llms.txt`. The full token list and what fills each is in
26
+ [`references/placeholders.md`](references/placeholders.md); the section catalogue
27
+ and the image-naming convention are in [`references/sections.md`](references/sections.md).
28
+
29
+ A run that hand-builds the HTML from this prose, or starts from a previous
30
+ client's rendered `index.html`, is a defect — it drifts every cycle. Copy the
31
+ canonical and substitute.
32
+
33
+ ## Substitution contract
34
+
35
+ - After substitution, `grep "{{" site/index.html site/llms.txt` returns **zero**
36
+ matches. Any surviving `{{ }}` is a bug.
37
+ - Palette tokens come from the brand pack's ten values. `ACCENT_TEXT` is the
38
+ deepened, legible form of `ACCENT` — not the same value.
39
+ - `SCHEMA_TYPE` is operator-supplied. Default to `LocalBusiness`; use
40
+ `Organization` for a business with no premises, or a more specific schema.org
41
+ subtype when one fits. Never fall back to a trade or industry type.
42
+ - Copy tokens are written in the plain register: no em-dashes in body text, no
43
+ inflation vocabulary, no marketing cadence. A real client testimonial is used
44
+ **verbatim**; never reword it.
45
+ - Never invent a fact to fill a token. If intake did not collect it, go back and
46
+ ask. An empty contact detail or a guessed year is a defect.
47
+
48
+ ## Images — curate by content, name by convention
49
+
50
+ The template references images by the fixed convention in
51
+ [`references/sections.md`](references/sections.md) so the operator can tell what
52
+ each slot is from the filename. Curate by **content, verified by eye**, never by
53
+ filename: lead the hero with the strongest "arrive here" shot, put the best
54
+ result in the testimonial, distribute the rest. A person's photo is founder-only;
55
+ it never goes in the rotator as a landscape crop. Every `alt` token is filled
56
+ from the real photo it sits on.
57
+
58
+ ### The 2000px rule
59
+
60
+ Never `Read` an image whose longest edge exceeds 2000px — measure first, preview a
61
+ downscaled `/tmp` copy if larger, delete it. Optimise to webp: body tier ~1300px
62
+ q82; hero tier source-resolution q88; the portrait kept tall.
63
+
64
+ ## Verify every page state
65
+
66
+ Serve `site/` over a local HTTP server and check, at a 1280px desktop and a 390px
67
+ mobile viewport: no horizontal overflow, no broken images, the hero rotates, the
68
+ drawer opens with every link, the sticky sub-nav reveals on scroll, the
69
+ back-to-top appears and works, and both JSON-LD blocks are valid JSON once
70
+ substituted. Bottom-align paired images and equal-height cards where the layout
71
+ calls for it.
72
+
73
+ ## Completion criteria
74
+
75
+ - `site/index.html` and `site/llms.txt` exist with zero surviving `{{ }}`.
76
+ - Every referenced image resolves; alts describe the real photos.
77
+ - Desktop + mobile verified; hero, drawer, sub-nav, back-to-top all work.
78
+ - Both JSON-LD blocks (the business, typed by the operator's `SCHEMA_TYPE`, and
79
+ `FAQPage`) parse and match the visible copy.
80
+
81
+ ## Deploy hand-off
82
+
83
+ The build ends at a self-contained `site/` tree. To go live, the tree becomes the
84
+ canonical Cloudflare Pages source at `<root>/pages/<project>/`, where `<root>`
85
+ comes from the resolver — never hardcode it:
86
+
87
+ ```bash
88
+ ROOT=$(sh "$(dirname "$0")/../../bin/resolve-cf.sh" site-root) # from the skill dir
89
+ # assemble <ROOT>/pages/<project>/ with the built site as its output dir
90
+ ```
91
+
92
+ Then follow [`references/deploy.md`](references/deploy.md) to publish, attach the
93
+ custom domain, wire any form to D1, and gate "done" on a live `200`. For a page or
94
+ document that must sit behind a login, follow [`references/gating.md`](references/gating.md).
95
+ For a document that must be signed, follow [`references/e-sign.md`](references/e-sign.md).
96
+
97
+ This skill runs in the admin/main session. It dispatches no specialist worker and
98
+ writes to no graph or memory store.
@@ -0,0 +1,59 @@
1
+ # Deploy hand-off
2
+
3
+ The build produces one canonical source tree at `<root>/pages/<project>/`, where
4
+ `<root>` is `bin/resolve-cf.sh site-root` and `<project>` is the site slug. That
5
+ tree holds the built output directory, `wrangler.toml`, and
6
+ `functions/api/<name>.ts` for any form the site captures. There is exactly one
7
+ tree per site; never deploy from a `.bak` clone or a stale sibling.
8
+
9
+ This reference composes existing deploy mechanics. It does not fork them.
10
+
11
+ ## Inside maxy-code: hand off to `cloudflare:site-deploy`
12
+
13
+ When the host carries the `cloudflare` plugin, hand the canonical tree to the
14
+ `cloudflare:site-deploy` skill and let it run unchanged. It resolves its own
15
+ account Pages-and-D1 token, runs `wrangler pages deploy` to the production alias,
16
+ attaches the custom domain, wires a contact or waitlist form to D1, checks Open
17
+ Graph correctness, and gates "done" on a cache-busted live `200` on the custom
18
+ domain. Do not re-implement any of that here — `site-deploy` is the deploy
19
+ authority; this skill only produces the tree it consumes.
20
+
21
+ ## Standalone: the generic Cloudflare Pages sequence
22
+
23
+ When `cloudflare:site-deploy` is not present (the marketplace edition), publish
24
+ with `wrangler` directly, following the public mechanics in the Cloudflare
25
+ references. Resolve the token through the seam — the resolver prints the **key
26
+ name**, never the value:
27
+
28
+ ```bash
29
+ KEY=$(sh bin/resolve-cf.sh token-key) # -> CLOUDFLARE_API_TOKEN, standalone
30
+ eval "export CLOUDFLARE_API_TOKEN=\"\$$KEY\""
31
+ cd "$(sh bin/resolve-cf.sh site-root)/pages/<project>"
32
+ wrangler pages deploy <output-dir> --project-name <project> --branch=main
33
+ ```
34
+
35
+ Then attach the custom domain to the Pages project and confirm a DNS record points
36
+ the hostname at `<project>.pages.dev` (a subdomain needs a `CNAME`; an apex needs
37
+ a proxied flattened `CNAME` plus a `www` `CNAME`). The token that carries
38
+ **Account · Cloudflare Pages · Edit** and **Account · D1 · Edit** is the one the
39
+ form work below also needs.
40
+
41
+ ## Forms and buckets: D1
42
+
43
+ A form on the site POSTs to a Pages Function (`functions/api/<name>.ts`) that
44
+ `INSERT`s into a D1 table bound in `wrangler.toml` as `[[d1_databases]]`. The token
45
+ must carry **both** Pages Edit and D1 Edit — a Pages-only token cannot touch D1,
46
+ and even a `SELECT` needs D1 Edit. One form is one table; additional form "buckets"
47
+ (a waitlist, a callback request, a survey) are additional tables and additional
48
+ `functions/api/<name>.ts` handlers on the same pattern. The full mechanics — table
49
+ schema, the `swept` read-cursor, the token discipline, and the D1 round-trip
50
+ proof — are in the host's `cloudflare/references/d1-data-capture.md`; follow it
51
+ rather than restating it.
52
+
53
+ ## Done-gate
54
+
55
+ "Done" is a live behavioural signal on the production custom domain, surfaced in
56
+ chat: a cache-busted `HTTP/2 200`, a content marker for this site (not a stale one
57
+ from a previous brand), brand-correct `og:*` tags, and — for a form-bearing site —
58
+ a test POST that appears in D1 unswept. Inside maxy-code this gate is
59
+ `site-deploy`'s; standalone it is the same curl-plus-D1-SELECT check.