@o-a/cms-agent 0.1.0

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 (201) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +132 -0
  3. package/dist/boot.d.ts +15 -0
  4. package/dist/boot.js +33 -0
  5. package/dist/config.d.ts +19 -0
  6. package/dist/config.js +45 -0
  7. package/dist/create-site/cli.d.ts +2 -0
  8. package/dist/create-site/cli.js +31 -0
  9. package/dist/create-site/generate-site.d.ts +9 -0
  10. package/dist/create-site/generate-site.js +109 -0
  11. package/dist/create-site/mint-token-cli.d.ts +2 -0
  12. package/dist/create-site/mint-token-cli.js +26 -0
  13. package/dist/create-site/mint-token.d.ts +7 -0
  14. package/dist/create-site/mint-token.js +49 -0
  15. package/dist/create-site/template/content/menus/footerCompany.json +9 -0
  16. package/dist/create-site/template/content/menus/footerProduct.json +9 -0
  17. package/dist/create-site/template/content/menus/footerResources.json +9 -0
  18. package/dist/create-site/template/content/menus/main.json +9 -0
  19. package/dist/create-site/template/content/pages/404.json +22 -0
  20. package/dist/create-site/template/content/pages/about/careers.json +21 -0
  21. package/dist/create-site/template/content/pages/about/team.json +21 -0
  22. package/dist/create-site/template/content/pages/about.json +21 -0
  23. package/dist/create-site/template/content/pages/docs/deployment.json +21 -0
  24. package/dist/create-site/template/content/pages/docs/getting-started/quickstart.json +21 -0
  25. package/dist/create-site/template/content/pages/docs/getting-started.json +23 -0
  26. package/dist/create-site/template/content/pages/docs.json +21 -0
  27. package/dist/create-site/template/content/pages/index.json +181 -0
  28. package/dist/create-site/template/content/redirects.json +4 -0
  29. package/dist/create-site/template/dockerignore +6 -0
  30. package/dist/create-site/template/gitignore +11 -0
  31. package/dist/create-site/template/theme/assets/site.js +49 -0
  32. package/dist/create-site/template/theme/assets/style.css +1153 -0
  33. package/dist/create-site/template/theme/blocks/button.liquid +21 -0
  34. package/dist/create-site/template/theme/blocks/comparison-row.liquid +19 -0
  35. package/dist/create-site/template/theme/blocks/faq-item.liquid +22 -0
  36. package/dist/create-site/template/theme/blocks/feature-card.liquid +23 -0
  37. package/dist/create-site/template/theme/blocks/logo-mark.liquid +13 -0
  38. package/dist/create-site/template/theme/blocks/pricing-tier.liquid +42 -0
  39. package/dist/create-site/template/theme/blocks/step-card.liquid +19 -0
  40. package/dist/create-site/template/theme/blocks/testimonial-card.liquid +25 -0
  41. package/dist/create-site/template/theme/layouts/theme.liquid +99 -0
  42. package/dist/create-site/template/theme/sections/comparison.liquid +41 -0
  43. package/dist/create-site/template/theme/sections/cta-banner.liquid +27 -0
  44. package/dist/create-site/template/theme/sections/faq.liquid +27 -0
  45. package/dist/create-site/template/theme/sections/feature-grid.liquid +27 -0
  46. package/dist/create-site/template/theme/sections/hero.liquid +36 -0
  47. package/dist/create-site/template/theme/sections/how-it-works.liquid +27 -0
  48. package/dist/create-site/template/theme/sections/pricing.liquid +27 -0
  49. package/dist/create-site/template/theme/sections/social-proof.liquid +21 -0
  50. package/dist/create-site/template/theme/sections/testimonials.liquid +27 -0
  51. package/dist/create-site/template/theme/snippets/icon.liquid +24 -0
  52. package/dist/create-site/template/theme/snippets/logo.liquid +7 -0
  53. package/dist/create-site/template/theme/snippets/site-name.liquid +1 -0
  54. package/dist/create-site/template/vhost/Dockerfile +33 -0
  55. package/dist/create-site/template/vhost/docker-entrypoint.sh +30 -0
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.js +4 -0
  58. package/dist/media/drivers/driver.d.ts +11 -0
  59. package/dist/media/drivers/driver.js +1 -0
  60. package/dist/media/drivers/local-fs-driver.d.ts +3 -0
  61. package/dist/media/drivers/local-fs-driver.js +81 -0
  62. package/dist/media/filename.d.ts +1 -0
  63. package/dist/media/filename.js +31 -0
  64. package/dist/media/manage-media.d.ts +19 -0
  65. package/dist/media/manage-media.js +45 -0
  66. package/dist/migrations/index.d.ts +3 -0
  67. package/dist/migrations/index.js +49 -0
  68. package/dist/renderer/engine.d.ts +2 -0
  69. package/dist/renderer/engine.js +48 -0
  70. package/dist/renderer/render-page.d.ts +28 -0
  71. package/dist/renderer/render-page.js +143 -0
  72. package/dist/renderer/theme-templates.d.ts +7 -0
  73. package/dist/renderer/theme-templates.js +82 -0
  74. package/dist/routes/assets.d.ts +6 -0
  75. package/dist/routes/assets.js +30 -0
  76. package/dist/routes/batch.d.ts +10 -0
  77. package/dist/routes/batch.js +148 -0
  78. package/dist/routes/capabilities.d.ts +5 -0
  79. package/dist/routes/capabilities.js +27 -0
  80. package/dist/routes/content.d.ts +8 -0
  81. package/dist/routes/content.js +166 -0
  82. package/dist/routes/drafts.d.ts +10 -0
  83. package/dist/routes/drafts.js +73 -0
  84. package/dist/routes/git.d.ts +8 -0
  85. package/dist/routes/git.js +158 -0
  86. package/dist/routes/index.d.ts +19 -0
  87. package/dist/routes/index.js +84 -0
  88. package/dist/routes/media-public.d.ts +5 -0
  89. package/dist/routes/media-public.js +48 -0
  90. package/dist/routes/media.d.ts +9 -0
  91. package/dist/routes/media.js +84 -0
  92. package/dist/routes/menus.d.ts +8 -0
  93. package/dist/routes/menus.js +77 -0
  94. package/dist/routes/preview-revision.d.ts +13 -0
  95. package/dist/routes/preview-revision.js +109 -0
  96. package/dist/routes/preview.d.ts +13 -0
  97. package/dist/routes/preview.js +52 -0
  98. package/dist/routes/public.d.ts +11 -0
  99. package/dist/routes/public.js +117 -0
  100. package/dist/routes/publish.d.ts +10 -0
  101. package/dist/routes/publish.js +105 -0
  102. package/dist/routes/redirects.d.ts +8 -0
  103. package/dist/routes/redirects.js +132 -0
  104. package/dist/routes/search.d.ts +8 -0
  105. package/dist/routes/search.js +11 -0
  106. package/dist/routes/sitemap.d.ts +6 -0
  107. package/dist/routes/sitemap.js +79 -0
  108. package/dist/routes/theme.d.ts +10 -0
  109. package/dist/routes/theme.js +17 -0
  110. package/dist/schemas/instance.schema.json +17 -0
  111. package/dist/schemas/menu.schema.json +23 -0
  112. package/dist/schemas/page.schema.json +20 -0
  113. package/dist/schemas/post.schema.json +25 -0
  114. package/dist/schemas/redirects.schema.json +24 -0
  115. package/dist/search/drivers/driver.d.ts +9 -0
  116. package/dist/search/drivers/driver.js +1 -0
  117. package/dist/search/drivers/node-sqlite-driver.d.ts +3 -0
  118. package/dist/search/drivers/node-sqlite-driver.js +19 -0
  119. package/dist/search/query-index.d.ts +5 -0
  120. package/dist/search/query-index.js +21 -0
  121. package/dist/search/rebuild-index.d.ts +2 -0
  122. package/dist/search/rebuild-index.js +111 -0
  123. package/dist/server-config.d.ts +22 -0
  124. package/dist/server-config.js +196 -0
  125. package/dist/server.d.ts +13 -0
  126. package/dist/server.js +163 -0
  127. package/dist/services/batch.d.ts +35 -0
  128. package/dist/services/batch.js +136 -0
  129. package/dist/services/checkpoint.d.ts +4 -0
  130. package/dist/services/checkpoint.js +45 -0
  131. package/dist/services/content-read.d.ts +27 -0
  132. package/dist/services/content-read.js +156 -0
  133. package/dist/services/delete-content.d.ts +12 -0
  134. package/dist/services/delete-content.js +174 -0
  135. package/dist/services/dev-tunnel.d.ts +5 -0
  136. package/dist/services/dev-tunnel.js +5 -0
  137. package/dist/services/drafts.d.ts +15 -0
  138. package/dist/services/drafts.js +155 -0
  139. package/dist/services/etag.d.ts +2 -0
  140. package/dist/services/etag.js +30 -0
  141. package/dist/services/fs-walk.d.ts +1 -0
  142. package/dist/services/fs-walk.js +26 -0
  143. package/dist/services/git-commit.d.ts +3 -0
  144. package/dist/services/git-commit.js +12 -0
  145. package/dist/services/git-history.d.ts +28 -0
  146. package/dist/services/git-history.js +131 -0
  147. package/dist/services/git-revert.d.ts +12 -0
  148. package/dist/services/git-revert.js +140 -0
  149. package/dist/services/git.d.ts +16 -0
  150. package/dist/services/git.js +127 -0
  151. package/dist/services/interval-job.d.ts +4 -0
  152. package/dist/services/interval-job.js +9 -0
  153. package/dist/services/ip-allowlist.d.ts +7 -0
  154. package/dist/services/ip-allowlist.js +31 -0
  155. package/dist/services/manage-menus.d.ts +10 -0
  156. package/dist/services/manage-menus.js +84 -0
  157. package/dist/services/manage-redirects.d.ts +25 -0
  158. package/dist/services/manage-redirects.js +166 -0
  159. package/dist/services/menus.d.ts +8 -0
  160. package/dist/services/menus.js +42 -0
  161. package/dist/services/migration-runner.d.ts +13 -0
  162. package/dist/services/migration-runner.js +123 -0
  163. package/dist/services/mime-types.d.ts +3 -0
  164. package/dist/services/mime-types.js +36 -0
  165. package/dist/services/move.d.ts +16 -0
  166. package/dist/services/move.js +175 -0
  167. package/dist/services/path-safety.d.ts +6 -0
  168. package/dist/services/path-safety.js +112 -0
  169. package/dist/services/post-urls.d.ts +3 -0
  170. package/dist/services/post-urls.js +27 -0
  171. package/dist/services/prepared-operation.d.ts +4 -0
  172. package/dist/services/prepared-operation.js +1 -0
  173. package/dist/services/publish.d.ts +14 -0
  174. package/dist/services/publish.js +254 -0
  175. package/dist/services/rate-limit-config.d.ts +9 -0
  176. package/dist/services/rate-limit-config.js +20 -0
  177. package/dist/services/redirects.d.ts +27 -0
  178. package/dist/services/redirects.js +175 -0
  179. package/dist/services/resolve-blog-url.d.ts +11 -0
  180. package/dist/services/resolve-blog-url.js +31 -0
  181. package/dist/services/resolve-url.d.ts +11 -0
  182. package/dist/services/resolve-url.js +20 -0
  183. package/dist/services/startup-checks.d.ts +10 -0
  184. package/dist/services/startup-checks.js +81 -0
  185. package/dist/services/static-file.d.ts +7 -0
  186. package/dist/services/static-file.js +37 -0
  187. package/dist/services/theme-component-file.d.ts +5 -0
  188. package/dist/services/theme-component-file.js +32 -0
  189. package/dist/services/theme-page-templates.d.ts +7 -0
  190. package/dist/services/theme-page-templates.js +53 -0
  191. package/dist/services/theme-schemas.d.ts +2 -0
  192. package/dist/services/theme-schemas.js +63 -0
  193. package/dist/services/token-auth.d.ts +10 -0
  194. package/dist/services/token-auth.js +64 -0
  195. package/dist/services/urls.d.ts +2 -0
  196. package/dist/services/urls.js +20 -0
  197. package/dist/services/validation.d.ts +24 -0
  198. package/dist/services/validation.js +158 -0
  199. package/dist/services/write-queue.d.ts +1 -0
  200. package/dist/services/write-queue.js +14 -0
  201. package/package.json +53 -0
@@ -0,0 +1,21 @@
1
+ {% if block.settings.style == 'secondary' %}
2
+ <a class="btn btn--secondary" data-block-id="{{ block.id }}" href="{{ block.settings.url }}">{{ block.settings.label }}</a>
3
+ {% elsif block.settings.style == 'on-dark' %}
4
+ <a class="btn btn--on-dark" data-block-id="{{ block.id }}" href="{{ block.settings.url }}">{{ block.settings.label }}</a>
5
+ {% else %}
6
+ <a class="btn btn--primary" data-block-id="{{ block.id }}" href="{{ block.settings.url }}">{{ block.settings.label }}</a>
7
+ {% endif %}
8
+ {% schema %}
9
+ {
10
+ "$schema": "http://json-schema.org/draft-07/schema#",
11
+ "title": "Button",
12
+ "type": "object",
13
+ "additionalProperties": false,
14
+ "required": ["label", "url"],
15
+ "properties": {
16
+ "label": { "type": "string", "minLength": 1, "default": "Learn more" },
17
+ "url": { "type": "string", "minLength": 1, "default": "#" },
18
+ "style": { "type": "string", "enum": ["primary", "secondary", "on-dark"] }
19
+ }
20
+ }
21
+ {% endschema %}
@@ -0,0 +1,19 @@
1
+ <tr data-block-id="{{ block.id }}">
2
+ <td>{{ block.settings.criteria }}</td>
3
+ <td class="col-granite">{{ block.settings.granite }}</td>
4
+ <td>{{ block.settings.traditional }}</td>
5
+ </tr>
6
+ {% schema %}
7
+ {
8
+ "$schema": "http://json-schema.org/draft-07/schema#",
9
+ "title": "Comparison Row",
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "required": ["criteria", "granite", "traditional"],
13
+ "properties": {
14
+ "criteria": { "type": "string", "minLength": 1, "default": "Criteria" },
15
+ "granite": { "type": "string", "minLength": 1, "default": "Granite" },
16
+ "traditional": { "type": "string", "minLength": 1, "default": "Traditional" }
17
+ }
18
+ }
19
+ {% endschema %}
@@ -0,0 +1,22 @@
1
+ <details class="faq-item" data-block-id="{{ block.id }}">
2
+ <summary>
3
+ <span>{{ block.settings.question }}</span>
4
+ <span class="faq-item__icon">{% render 'icon', name: 'plus' %}</span>
5
+ </summary>
6
+ <div class="faq-item__body">
7
+ <p>{{ block.settings.answer }}</p>
8
+ </div>
9
+ </details>
10
+ {% schema %}
11
+ {
12
+ "$schema": "http://json-schema.org/draft-07/schema#",
13
+ "title": "FAQ Item",
14
+ "type": "object",
15
+ "additionalProperties": false,
16
+ "required": ["question", "answer"],
17
+ "properties": {
18
+ "question": { "type": "string", "minLength": 1, "default": "Question" },
19
+ "answer": { "type": "string", "minLength": 1, "default": "Answer" }
20
+ }
21
+ }
22
+ {% endschema %}
@@ -0,0 +1,23 @@
1
+ <div class="feature-card reveal" data-block-id="{{ block.id }}">
2
+ <div class="feature-card__icon">{% render 'icon', name: block.settings.icon %}</div>
3
+ <h3>{{ block.settings.heading }}</h3>
4
+ <p>{{ block.settings.description }}</p>
5
+ </div>
6
+ {% schema %}
7
+ {
8
+ "$schema": "http://json-schema.org/draft-07/schema#",
9
+ "title": "Feature Card",
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "required": ["icon", "heading", "description"],
13
+ "properties": {
14
+ "icon": {
15
+ "type": "string",
16
+ "enum": ["structure", "git", "bolt", "block", "admin", "portable"],
17
+ "default": "bolt"
18
+ },
19
+ "heading": { "type": "string", "minLength": 1, "default": "Feature" },
20
+ "description": { "type": "string", "minLength": 1, "default": "Description" }
21
+ }
22
+ }
23
+ {% endschema %}
@@ -0,0 +1,13 @@
1
+ <span class="logo-mark" data-block-id="{{ block.id }}">{{ block.settings.name }}</span>
2
+ {% schema %}
3
+ {
4
+ "$schema": "http://json-schema.org/draft-07/schema#",
5
+ "title": "Logo Mark",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["name"],
9
+ "properties": {
10
+ "name": { "type": "string", "minLength": 1, "default": "Company" }
11
+ }
12
+ }
13
+ {% endschema %}
@@ -0,0 +1,42 @@
1
+ <div class="pricing-tier reveal{% if block.settings.highlighted %} pricing-tier--highlight{% endif %}" data-block-id="{{ block.id }}">
2
+ <div class="pricing-tier__name">{{ block.settings.name }}</div>
3
+ <div class="pricing-tier__price">
4
+ <strong>{{ block.settings.price }}</strong>
5
+ <span>{{ block.settings.period }}</span>
6
+ </div>
7
+ <p class="pricing-tier__desc">{{ block.settings.description }}</p>
8
+ <ul class="pricing-tier__features">
9
+ {% for feature in block.settings.features %}
10
+ <li>{% render 'icon', name: 'check' %}<span>{{ feature }}</span></li>
11
+ {% endfor %}
12
+ </ul>
13
+ {% if block.settings.highlighted %}
14
+ <a class="btn btn--primary" href="{{ block.settings.ctaUrl }}">{{ block.settings.ctaLabel }}</a>
15
+ {% else %}
16
+ <a class="btn btn--secondary" href="{{ block.settings.ctaUrl }}">{{ block.settings.ctaLabel }}</a>
17
+ {% endif %}
18
+ </div>
19
+ {% schema %}
20
+ {
21
+ "$schema": "http://json-schema.org/draft-07/schema#",
22
+ "title": "Pricing Tier",
23
+ "type": "object",
24
+ "additionalProperties": false,
25
+ "required": ["name", "price", "period", "description", "features", "ctaLabel", "ctaUrl"],
26
+ "properties": {
27
+ "name": { "type": "string", "minLength": 1, "default": "Plan" },
28
+ "price": { "type": "string", "minLength": 1, "default": "$0" },
29
+ "period": { "type": "string", "minLength": 1, "default": "/month" },
30
+ "description": { "type": "string", "minLength": 1, "default": "Description" },
31
+ "features": {
32
+ "type": "array",
33
+ "minItems": 1,
34
+ "items": { "type": "string", "minLength": 1 },
35
+ "default": ["Feature one"]
36
+ },
37
+ "ctaLabel": { "type": "string", "minLength": 1, "default": "Get started" },
38
+ "ctaUrl": { "type": "string", "minLength": 1, "default": "#" },
39
+ "highlighted": { "type": "boolean" }
40
+ }
41
+ }
42
+ {% endschema %}
@@ -0,0 +1,19 @@
1
+ <div class="step-card reveal" data-block-id="{{ block.id }}">
2
+ <div class="step-card__number">{{ block.settings.number }}</div>
3
+ <h3>{{ block.settings.heading }}</h3>
4
+ <p>{{ block.settings.description }}</p>
5
+ </div>
6
+ {% schema %}
7
+ {
8
+ "$schema": "http://json-schema.org/draft-07/schema#",
9
+ "title": "Step Card",
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "required": ["number", "heading", "description"],
13
+ "properties": {
14
+ "number": { "type": "string", "minLength": 1, "default": "1" },
15
+ "heading": { "type": "string", "minLength": 1, "default": "Step" },
16
+ "description": { "type": "string", "minLength": 1, "default": "Description" }
17
+ }
18
+ }
19
+ {% endschema %}
@@ -0,0 +1,25 @@
1
+ <div class="testimonial-card reveal" data-block-id="{{ block.id }}">
2
+ <blockquote>{{ block.settings.quote }}</blockquote>
3
+ <div class="testimonial-card__author">
4
+ <span class="avatar" aria-hidden="true">{{ block.settings.initials }}</span>
5
+ <div>
6
+ <strong>{{ block.settings.name }}</strong>
7
+ <span>{{ block.settings.role }}</span>
8
+ </div>
9
+ </div>
10
+ </div>
11
+ {% schema %}
12
+ {
13
+ "$schema": "http://json-schema.org/draft-07/schema#",
14
+ "title": "Testimonial Card",
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "required": ["quote", "name", "role", "initials"],
18
+ "properties": {
19
+ "quote": { "type": "string", "minLength": 1, "default": "Quote" },
20
+ "name": { "type": "string", "minLength": 1, "default": "Name" },
21
+ "role": { "type": "string", "minLength": 1, "default": "Role" },
22
+ "initials": { "type": "string", "minLength": 1, "maxLength": 3, "default": "AB" }
23
+ }
24
+ }
25
+ {% endschema %}
@@ -0,0 +1,99 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>{{ page.title }}</title>
7
+ <meta name="description" content="Granite CMS is a lightweight, self-hosted content management system built for agencies and freelance developers who manage client sites on git-based workflows.">
8
+ <link rel="stylesheet" href="/assets/style.css">
9
+ </head>
10
+ <body>
11
+ <a class="skip-link" href="#main">Skip to content</a>
12
+
13
+ <header class="site-header" data-site-header>
14
+ <div class="container site-header__inner">
15
+ <a href="/" class="logo">{% render 'logo' %}</a>
16
+
17
+ <nav class="site-nav" aria-label="Primary">
18
+ <ul>
19
+ {% for item in menus.main.items %}
20
+ <li><a href="{{ item.url }}">{{ item.label }}</a></li>
21
+ {% endfor %}
22
+ </ul>
23
+ </nav>
24
+
25
+ <div class="site-header__actions">
26
+ <a class="btn btn--primary btn--sm" href="#pricing">Get started</a>
27
+ <button class="nav-toggle" type="button" data-nav-toggle aria-expanded="false" aria-controls="mobile-nav" aria-label="Toggle menu">
28
+ <span></span><span></span><span></span>
29
+ </button>
30
+ </div>
31
+ </div>
32
+
33
+ <div class="mobile-nav" id="mobile-nav" data-mobile-nav>
34
+ <ul>
35
+ {% for item in menus.main.items %}
36
+ <li><a href="{{ item.url }}">{{ item.label }}</a></li>
37
+ {% endfor %}
38
+ </ul>
39
+ <a class="btn btn--primary btn--block" href="#pricing">Get started</a>
40
+ </div>
41
+ </header>
42
+
43
+ <main id="main">
44
+ {{ content_for_layout | raw }}
45
+ </main>
46
+
47
+ <footer class="site-footer">
48
+ <div class="container">
49
+ <div class="footer__grid">
50
+ <div class="footer__brand">
51
+ {% render 'logo' %}
52
+ <p>Solid foundations for client sites. A lightweight, self-hosted CMS built for developers.</p>
53
+ <div class="footer__social">
54
+ <a href="https://github.com/granite-cms/granite" aria-label="GitHub">{% render 'icon', name: 'github' %}</a>
55
+ <a href="https://twitter.com/granitecms" aria-label="X (Twitter)">{% render 'icon', name: 'twitter' %}</a>
56
+ <a href="https://discord.gg/granitecms" aria-label="Discord">{% render 'icon', name: 'discord' %}</a>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="footer__cols">
61
+ <nav class="footer__col" aria-label="Product">
62
+ <h3>Product</h3>
63
+ <ul>
64
+ {% for item in menus.footerProduct.items %}
65
+ <li><a href="{{ item.url }}">{{ item.label }}</a></li>
66
+ {% endfor %}
67
+ </ul>
68
+ </nav>
69
+
70
+ <nav class="footer__col" aria-label="Resources">
71
+ <h3>Resources</h3>
72
+ <ul>
73
+ {% for item in menus.footerResources.items %}
74
+ <li><a href="{{ item.url }}">{{ item.label }}</a></li>
75
+ {% endfor %}
76
+ </ul>
77
+ </nav>
78
+
79
+ <nav class="footer__col" aria-label="Company">
80
+ <h3>Company</h3>
81
+ <ul>
82
+ {% for item in menus.footerCompany.items %}
83
+ <li><a href="{{ item.url }}">{{ item.label }}</a></li>
84
+ {% endfor %}
85
+ </ul>
86
+ </nav>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="footer__bottom">
91
+ <span>&copy; 2026 Granite CMS. All rights reserved.</span>
92
+ <span>Built and self-hosted, everywhere.</span>
93
+ </div>
94
+ </div>
95
+ </footer>
96
+
97
+ <script src="/assets/site.js"></script>
98
+ </body>
99
+ </html>
@@ -0,0 +1,41 @@
1
+ <section class="section-alt" id="comparison" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="section-head center reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ </div>
8
+ <div class="comparison-wrap reveal">
9
+ <table class="comparison-table">
10
+ <thead>
11
+ <tr>
12
+ <th>{{ section.settings.criteriaLabel }}</th>
13
+ <th class="col-granite">{{ section.settings.graniteLabel }}</th>
14
+ <th>{{ section.settings.traditionalLabel }}</th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
19
+ </tbody>
20
+ </table>
21
+ </div>
22
+ </div>
23
+ </section>
24
+ {% schema %}
25
+ {
26
+ "$schema": "http://json-schema.org/draft-07/schema#",
27
+ "title": "Comparison",
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "required": ["heading", "criteriaLabel", "graniteLabel", "traditionalLabel"],
31
+ "properties": {
32
+ "eyebrow": { "type": "string" },
33
+ "heading": { "type": "string", "minLength": 1, "default": "How Granite compares" },
34
+ "subheading": { "type": "string" },
35
+ "criteriaLabel": { "type": "string", "minLength": 1, "default": "Criteria" },
36
+ "graniteLabel": { "type": "string", "minLength": 1, "default": "Granite" },
37
+ "traditionalLabel": { "type": "string", "minLength": 1, "default": "Traditional CMS" }
38
+ },
39
+ "allowedBlocks": ["comparison-row"]
40
+ }
41
+ {% endschema %}
@@ -0,0 +1,27 @@
1
+ <section data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="cta-banner reveal">
4
+ <div class="cta-banner__inner">
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ <div class="cta-banner__actions">
8
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ </section>
14
+ {% schema %}
15
+ {
16
+ "$schema": "http://json-schema.org/draft-07/schema#",
17
+ "title": "CTA Banner",
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "required": ["heading"],
21
+ "properties": {
22
+ "heading": { "type": "string", "minLength": 1, "default": "Ready to get started?" },
23
+ "subheading": { "type": "string" }
24
+ },
25
+ "allowedBlocks": ["button"]
26
+ }
27
+ {% endschema %}
@@ -0,0 +1,27 @@
1
+ <section id="faq" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="section-head reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ </div>
8
+ <div class="faq-list reveal">
9
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
10
+ </div>
11
+ </div>
12
+ </section>
13
+ {% schema %}
14
+ {
15
+ "$schema": "http://json-schema.org/draft-07/schema#",
16
+ "title": "FAQ",
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["heading"],
20
+ "properties": {
21
+ "eyebrow": { "type": "string" },
22
+ "heading": { "type": "string", "minLength": 1, "default": "Frequently asked questions" },
23
+ "subheading": { "type": "string" }
24
+ },
25
+ "allowedBlocks": ["faq-item"]
26
+ }
27
+ {% endschema %}
@@ -0,0 +1,27 @@
1
+ <section class="section-alt" id="features" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="section-head reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ </div>
8
+ <div class="feature-grid">
9
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
10
+ </div>
11
+ </div>
12
+ </section>
13
+ {% schema %}
14
+ {
15
+ "$schema": "http://json-schema.org/draft-07/schema#",
16
+ "title": "Feature Grid",
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["heading"],
20
+ "properties": {
21
+ "eyebrow": { "type": "string" },
22
+ "heading": { "type": "string", "minLength": 1, "default": "Why teams choose Granite" },
23
+ "subheading": { "type": "string" }
24
+ },
25
+ "allowedBlocks": ["feature-card"]
26
+ }
27
+ {% endschema %}
@@ -0,0 +1,36 @@
1
+ <section class="hero" data-section-id="{{ section.id }}">
2
+ <div class="container hero__inner">
3
+ <div class="hero__content reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h1>{{ section.settings.heading }}</h1>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ <div class="hero__actions">{% for html in blocksHtml %}{{ html | raw }}{% endfor %}</div>
8
+ </div>
9
+ {% if section.settings.codeSnippet %}
10
+ <div class="code-panel reveal">
11
+ <div class="code-panel__bar">
12
+ <span></span><span></span><span></span>
13
+ {% if section.settings.codeTitle %}<span class="code-panel__title">{{ section.settings.codeTitle }}</span>{% endif %}
14
+ </div>
15
+ <pre><code>{{ section.settings.codeSnippet }}</code></pre>
16
+ </div>
17
+ {% endif %}
18
+ </div>
19
+ </section>
20
+ {% schema %}
21
+ {
22
+ "$schema": "http://json-schema.org/draft-07/schema#",
23
+ "title": "Hero",
24
+ "type": "object",
25
+ "additionalProperties": false,
26
+ "required": ["heading"],
27
+ "properties": {
28
+ "eyebrow": { "type": "string" },
29
+ "heading": { "type": "string", "minLength": 1, "default": "New Section" },
30
+ "subheading": { "type": "string" },
31
+ "codeTitle": { "type": "string" },
32
+ "codeSnippet": { "type": "string" }
33
+ },
34
+ "allowedBlocks": ["button"]
35
+ }
36
+ {% endschema %}
@@ -0,0 +1,27 @@
1
+ <section id="how-it-works" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="section-head reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ </div>
8
+ <div class="steps">
9
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
10
+ </div>
11
+ </div>
12
+ </section>
13
+ {% schema %}
14
+ {
15
+ "$schema": "http://json-schema.org/draft-07/schema#",
16
+ "title": "How It Works",
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["heading"],
20
+ "properties": {
21
+ "eyebrow": { "type": "string" },
22
+ "heading": { "type": "string", "minLength": 1, "default": "How it works" },
23
+ "subheading": { "type": "string" }
24
+ },
25
+ "allowedBlocks": ["step-card"]
26
+ }
27
+ {% endschema %}
@@ -0,0 +1,27 @@
1
+ <section class="section-alt" id="pricing" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="section-head center reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ </div>
8
+ <div class="pricing-grid">
9
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
10
+ </div>
11
+ </div>
12
+ </section>
13
+ {% schema %}
14
+ {
15
+ "$schema": "http://json-schema.org/draft-07/schema#",
16
+ "title": "Pricing",
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["heading"],
20
+ "properties": {
21
+ "eyebrow": { "type": "string" },
22
+ "heading": { "type": "string", "minLength": 1, "default": "Pricing" },
23
+ "subheading": { "type": "string" }
24
+ },
25
+ "allowedBlocks": ["pricing-tier"]
26
+ }
27
+ {% endschema %}
@@ -0,0 +1,21 @@
1
+ <section class="social-proof" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <p class="social-proof__label reveal">{{ section.settings.label }}</p>
4
+ <div class="social-proof__row reveal">
5
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
6
+ </div>
7
+ </div>
8
+ </section>
9
+ {% schema %}
10
+ {
11
+ "$schema": "http://json-schema.org/draft-07/schema#",
12
+ "title": "Social Proof",
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["label"],
16
+ "properties": {
17
+ "label": { "type": "string", "minLength": 1, "default": "Trusted by teams at" }
18
+ },
19
+ "allowedBlocks": ["logo-mark"]
20
+ }
21
+ {% endschema %}
@@ -0,0 +1,27 @@
1
+ <section id="testimonials" data-section-id="{{ section.id }}">
2
+ <div class="container">
3
+ <div class="section-head reveal">
4
+ {% if section.settings.eyebrow %}<span class="eyebrow">{{ section.settings.eyebrow }}</span>{% endif %}
5
+ <h2>{{ section.settings.heading }}</h2>
6
+ {% if section.settings.subheading %}<p>{{ section.settings.subheading }}</p>{% endif %}
7
+ </div>
8
+ <div class="testimonial-grid">
9
+ {% for html in blocksHtml %}{{ html | raw }}{% endfor %}
10
+ </div>
11
+ </div>
12
+ </section>
13
+ {% schema %}
14
+ {
15
+ "$schema": "http://json-schema.org/draft-07/schema#",
16
+ "title": "Testimonials",
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["heading"],
20
+ "properties": {
21
+ "eyebrow": { "type": "string" },
22
+ "heading": { "type": "string", "minLength": 1, "default": "What our customers say" },
23
+ "subheading": { "type": "string" }
24
+ },
25
+ "allowedBlocks": ["testimonial-card"]
26
+ }
27
+ {% endschema %}
@@ -0,0 +1,24 @@
1
+ {% case name %}
2
+ {% when 'github' %}
3
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 2C6.48 2 2 6.58 2 12.2c0 4.5 2.87 8.32 6.84 9.67.5.1.68-.22.68-.49 0-.24-.01-1.04-.01-1.88-2.78.61-3.37-1.21-3.37-1.21-.45-1.18-1.11-1.49-1.11-1.49-.9-.63.07-.62.07-.62 1 .07 1.53 1.05 1.53 1.05.9 1.55 2.36 1.11 2.94.84.09-.66.35-1.11.63-1.36-2.22-.26-4.56-1.14-4.56-5.06 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.31.1-2.72 0 0 .84-.28 2.75 1.05a9.3 9.3 0 0 1 5 0c1.9-1.33 2.75-1.05 2.75-1.05.55 1.41.2 2.46.1 2.72.64.72 1.03 1.63 1.03 2.75 0 3.93-2.34 4.79-4.57 5.05.36.32.68.94.68 1.9 0 1.37-.01 2.47-.01 2.81 0 .27.18.6.69.49A10.02 10.02 0 0 0 22 12.2C22 6.58 17.52 2 12 2Z" fill="currentColor"/></svg>
4
+ {% when 'twitter' %}
5
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M17.53 3h3.19l-6.98 7.98L22 21h-6.43l-5.04-6.6L4.75 21H1.56l7.46-8.53L1 3h6.6l4.56 6.03L17.53 3Zm-1.12 16.17h1.77L7.68 4.73H5.78l10.63 14.44Z" fill="currentColor"/></svg>
6
+ {% when 'discord' %}
7
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M19.27 5.33A17.8 17.8 0 0 0 14.9 4a12.5 12.5 0 0 0-.56 1.15 16.4 16.4 0 0 0-4.68 0A12.5 12.5 0 0 0 9.1 4a17.7 17.7 0 0 0-4.38 1.34C2.1 9.05 1.4 12.65 1.75 16.2a17.9 17.9 0 0 0 5.44 2.76c.44-.6.83-1.24 1.17-1.92-.64-.24-1.25-.54-1.83-.9.15-.11.3-.23.44-.35a12.7 12.7 0 0 0 10.06 0c.15.12.3.24.44.35-.58.36-1.19.66-1.83.9.34.68.73 1.32 1.17 1.92a17.85 17.85 0 0 0 5.44-2.76c.42-4.12-.66-7.68-2.98-10.87ZM8.68 14.1c-1 0-1.8-.92-1.8-2.06 0-1.13.79-2.06 1.8-2.06 1 0 1.82.94 1.8 2.06 0 1.14-.8 2.06-1.8 2.06Zm6.65 0c-1 0-1.8-.92-1.8-2.06 0-1.13.79-2.06 1.8-2.06 1 0 1.82.94 1.8 2.06 0 1.14-.79 2.06-1.8 2.06Z" fill="currentColor"/></svg>
8
+ {% when 'structure' %}
9
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1.4" stroke="currentColor" stroke-width="1.7"/><rect x="14" y="3" width="7" height="7" rx="1.4" stroke="currentColor" stroke-width="1.7"/><rect x="3" y="14" width="7" height="7" rx="1.4" stroke="currentColor" stroke-width="1.7"/><rect x="14" y="14" width="7" height="7" rx="1.4" stroke="currentColor" stroke-width="1.7"/></svg>
10
+ {% when 'git' %}
11
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="6" cy="6" r="2.4" stroke="currentColor" stroke-width="1.7"/><circle cx="6" cy="18" r="2.4" stroke="currentColor" stroke-width="1.7"/><circle cx="18" cy="15" r="2.4" stroke="currentColor" stroke-width="1.7"/><path d="M6 8.4V15.6M6 10c0 3 3 3 8 5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
12
+ {% when 'bolt' %}
13
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M13 2 4 14h6l-1 8 9-12h-6l1-8Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg>
14
+ {% when 'block' %}
15
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.7"/><path d="M6 6l12 12" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
16
+ {% when 'admin' %}
17
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="12" cy="8" r="3.4" stroke="currentColor" stroke-width="1.7"/><path d="M4.8 20c1.2-3.6 4-5.4 7.2-5.4s6 1.8 7.2 5.4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
18
+ {% when 'portable' %}
19
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 3 20 7.5v9L12 21 4 16.5v-9L12 3Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M4 7.5 12 12l8-4.5M12 12v9" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg>
20
+ {% when 'check' %}
21
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M4 12.5 9.5 18 20 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
22
+ {% when 'plus' %}
23
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
24
+ {% endcase %}
@@ -0,0 +1,7 @@
1
+ <span class="logo">
2
+ <svg class="logo__mark" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
3
+ <path d="M16 2 L29 9 V23 L16 30 L3 23 V9 Z" fill="currentColor" fill-opacity="0.08" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/>
4
+ <path d="M16 2 L16 30 M3 9 L16 15 L29 9 M3 23 L16 15" stroke="var(--accent)" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/>
5
+ </svg>
6
+ <span>Granite<span style="color:var(--accent)">CMS</span></span>
7
+ </span>
@@ -0,0 +1 @@
1
+ <span class="site-name">{{ name }}</span>