@cimplify/cli 0.6.1 → 0.6.3

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 (162) hide show
  1. package/dist/{add-5EMD74VD.mjs → add-XETP2LS5.mjs} +1 -1
  2. package/dist/{chunk-O6RONGAI.mjs → chunk-2IKULCJK.mjs} +1 -1
  3. package/dist/{chunk-OILDQUAD.mjs → chunk-M5ZENIII.mjs} +25 -25
  4. package/dist/{chunk-DAHD6YWI.mjs → chunk-SVW5FLGR.mjs} +1 -1
  5. package/dist/dispatcher.mjs +11 -10
  6. package/dist/{doctor-YPO2MQZF.mjs → doctor-JK2GOUKZ.mjs} +2 -2
  7. package/dist/{domains-AHH56CL7.mjs → domains-6GJASWJU.mjs} +20 -2
  8. package/dist/{explain-L5ZDNMHC.mjs → explain-J6SJKV7A.mjs} +1 -1
  9. package/dist/{introspect-PQ476BL7.mjs → introspect-L53LUMAR.mjs} +2 -2
  10. package/dist/{list-5ORRWEYK.mjs → list-AADEEPYT.mjs} +1 -1
  11. package/dist/{update-4AZUFFQ3.mjs → update-GB2FEYPY.mjs} +1 -1
  12. package/package.json +1 -1
  13. package/templates/storefront-auto/app/about/page.tsx +1 -1
  14. package/templates/storefront-auto/app/faq/page.tsx +1 -1
  15. package/templates/storefront-auto/app/layout.tsx +15 -31
  16. package/templates/storefront-auto/app/orders/[id]/page.tsx +1 -1
  17. package/templates/storefront-auto/app/privacy/page.tsx +1 -1
  18. package/templates/storefront-auto/app/terms/page.tsx +1 -1
  19. package/templates/storefront-auto/components/cart-pill.tsx +2 -2
  20. package/templates/storefront-auto/components/category-grid.tsx +1 -1
  21. package/templates/storefront-auto/components/collection-strip.tsx +1 -1
  22. package/templates/storefront-auto/components/header.tsx +13 -7
  23. package/templates/storefront-auto/components/hero.tsx +1 -1
  24. package/templates/storefront-auto/components/json-ld.tsx +34 -0
  25. package/templates/storefront-auto/components/mobile-nav.tsx +113 -0
  26. package/templates/storefront-auto/components/policy-page.tsx +1 -1
  27. package/templates/storefront-auto/components/section-heading.tsx +2 -2
  28. package/templates/storefront-auto/package.json +1 -1
  29. package/templates/storefront-bakery/app/about/page.tsx +1 -1
  30. package/templates/storefront-bakery/app/categories/[slug]/page.tsx +2 -2
  31. package/templates/storefront-bakery/app/collections/[slug]/page.tsx +2 -2
  32. package/templates/storefront-bakery/app/error.tsx +1 -1
  33. package/templates/storefront-bakery/app/faq/page.tsx +1 -1
  34. package/templates/storefront-bakery/app/layout.tsx +5 -26
  35. package/templates/storefront-bakery/app/not-found.tsx +1 -1
  36. package/templates/storefront-bakery/app/orders/[id]/page.tsx +1 -1
  37. package/templates/storefront-bakery/app/page.tsx +2 -2
  38. package/templates/storefront-bakery/app/privacy/page.tsx +1 -1
  39. package/templates/storefront-bakery/app/terms/page.tsx +1 -1
  40. package/templates/storefront-bakery/components/cart-pill.tsx +2 -2
  41. package/templates/storefront-bakery/components/category-grid.tsx +1 -1
  42. package/templates/storefront-bakery/components/collection-strip.tsx +1 -1
  43. package/templates/storefront-bakery/components/footer.tsx +1 -1
  44. package/templates/storefront-bakery/components/header.tsx +15 -9
  45. package/templates/storefront-bakery/components/hero.tsx +1 -1
  46. package/templates/storefront-bakery/components/json-ld.tsx +34 -0
  47. package/templates/storefront-bakery/components/mobile-nav.tsx +113 -0
  48. package/templates/storefront-bakery/components/policy-page.tsx +1 -1
  49. package/templates/storefront-bakery/package.json +1 -1
  50. package/templates/storefront-fashion/app/about/page.tsx +1 -1
  51. package/templates/storefront-fashion/app/faq/page.tsx +1 -1
  52. package/templates/storefront-fashion/app/layout.tsx +15 -31
  53. package/templates/storefront-fashion/app/orders/[id]/page.tsx +1 -1
  54. package/templates/storefront-fashion/app/privacy/page.tsx +1 -1
  55. package/templates/storefront-fashion/app/terms/page.tsx +1 -1
  56. package/templates/storefront-fashion/components/cart-pill.tsx +2 -2
  57. package/templates/storefront-fashion/components/category-grid.tsx +1 -1
  58. package/templates/storefront-fashion/components/collection-strip.tsx +1 -1
  59. package/templates/storefront-fashion/components/feature-hero.tsx +1 -1
  60. package/templates/storefront-fashion/components/header.tsx +13 -7
  61. package/templates/storefront-fashion/components/hero.tsx +1 -1
  62. package/templates/storefront-fashion/components/json-ld.tsx +34 -0
  63. package/templates/storefront-fashion/components/mobile-nav.tsx +113 -0
  64. package/templates/storefront-fashion/components/policy-page.tsx +1 -1
  65. package/templates/storefront-fashion/components/section-heading.tsx +2 -2
  66. package/templates/storefront-fashion/package.json +1 -1
  67. package/templates/storefront-grocery/app/about/page.tsx +1 -1
  68. package/templates/storefront-grocery/app/categories/[slug]/page.tsx +2 -2
  69. package/templates/storefront-grocery/app/collections/[slug]/page.tsx +2 -2
  70. package/templates/storefront-grocery/app/error.tsx +1 -1
  71. package/templates/storefront-grocery/app/faq/page.tsx +1 -1
  72. package/templates/storefront-grocery/app/layout.tsx +10 -32
  73. package/templates/storefront-grocery/app/not-found.tsx +1 -1
  74. package/templates/storefront-grocery/app/orders/[id]/page.tsx +1 -1
  75. package/templates/storefront-grocery/app/page.tsx +2 -2
  76. package/templates/storefront-grocery/app/privacy/page.tsx +1 -1
  77. package/templates/storefront-grocery/app/terms/page.tsx +1 -1
  78. package/templates/storefront-grocery/components/cart-pill.tsx +2 -2
  79. package/templates/storefront-grocery/components/category-grid.tsx +1 -1
  80. package/templates/storefront-grocery/components/collection-strip.tsx +1 -1
  81. package/templates/storefront-grocery/components/footer.tsx +1 -1
  82. package/templates/storefront-grocery/components/header.tsx +15 -9
  83. package/templates/storefront-grocery/components/hero.tsx +1 -1
  84. package/templates/storefront-grocery/components/json-ld.tsx +34 -0
  85. package/templates/storefront-grocery/components/mobile-nav.tsx +113 -0
  86. package/templates/storefront-grocery/components/policy-page.tsx +1 -1
  87. package/templates/storefront-grocery/package.json +1 -1
  88. package/templates/storefront-pharmacy/app/about/page.tsx +1 -1
  89. package/templates/storefront-pharmacy/app/faq/page.tsx +1 -1
  90. package/templates/storefront-pharmacy/app/layout.tsx +15 -31
  91. package/templates/storefront-pharmacy/app/orders/[id]/page.tsx +1 -1
  92. package/templates/storefront-pharmacy/app/privacy/page.tsx +1 -1
  93. package/templates/storefront-pharmacy/app/terms/page.tsx +1 -1
  94. package/templates/storefront-pharmacy/components/cart-pill.tsx +2 -2
  95. package/templates/storefront-pharmacy/components/category-grid.tsx +1 -1
  96. package/templates/storefront-pharmacy/components/collection-strip.tsx +1 -1
  97. package/templates/storefront-pharmacy/components/header.tsx +13 -7
  98. package/templates/storefront-pharmacy/components/hero.tsx +1 -1
  99. package/templates/storefront-pharmacy/components/json-ld.tsx +34 -0
  100. package/templates/storefront-pharmacy/components/mobile-nav.tsx +113 -0
  101. package/templates/storefront-pharmacy/components/policy-page.tsx +1 -1
  102. package/templates/storefront-pharmacy/components/section-heading.tsx +2 -2
  103. package/templates/storefront-pharmacy/package.json +1 -1
  104. package/templates/storefront-restaurant/app/about/page.tsx +1 -1
  105. package/templates/storefront-restaurant/app/categories/[slug]/page.tsx +2 -2
  106. package/templates/storefront-restaurant/app/collections/[slug]/page.tsx +2 -2
  107. package/templates/storefront-restaurant/app/error.tsx +1 -1
  108. package/templates/storefront-restaurant/app/faq/page.tsx +1 -1
  109. package/templates/storefront-restaurant/app/layout.tsx +10 -31
  110. package/templates/storefront-restaurant/app/not-found.tsx +1 -1
  111. package/templates/storefront-restaurant/app/orders/[id]/page.tsx +1 -1
  112. package/templates/storefront-restaurant/app/page.tsx +2 -2
  113. package/templates/storefront-restaurant/app/privacy/page.tsx +1 -1
  114. package/templates/storefront-restaurant/app/terms/page.tsx +1 -1
  115. package/templates/storefront-restaurant/components/cart-pill.tsx +2 -2
  116. package/templates/storefront-restaurant/components/category-grid.tsx +1 -1
  117. package/templates/storefront-restaurant/components/collection-strip.tsx +1 -1
  118. package/templates/storefront-restaurant/components/footer.tsx +1 -1
  119. package/templates/storefront-restaurant/components/header.tsx +15 -9
  120. package/templates/storefront-restaurant/components/hero.tsx +1 -1
  121. package/templates/storefront-restaurant/components/json-ld.tsx +34 -0
  122. package/templates/storefront-restaurant/components/mobile-nav.tsx +113 -0
  123. package/templates/storefront-restaurant/components/policy-page.tsx +1 -1
  124. package/templates/storefront-restaurant/package.json +1 -1
  125. package/templates/storefront-retail/app/about/page.tsx +1 -1
  126. package/templates/storefront-retail/app/faq/page.tsx +1 -1
  127. package/templates/storefront-retail/app/layout.tsx +15 -31
  128. package/templates/storefront-retail/app/orders/[id]/page.tsx +1 -1
  129. package/templates/storefront-retail/app/privacy/page.tsx +1 -1
  130. package/templates/storefront-retail/app/terms/page.tsx +1 -1
  131. package/templates/storefront-retail/components/cart-pill.tsx +2 -2
  132. package/templates/storefront-retail/components/category-grid.tsx +1 -1
  133. package/templates/storefront-retail/components/collection-strip.tsx +1 -1
  134. package/templates/storefront-retail/components/header.tsx +13 -7
  135. package/templates/storefront-retail/components/hero.tsx +1 -1
  136. package/templates/storefront-retail/components/json-ld.tsx +34 -0
  137. package/templates/storefront-retail/components/mobile-nav.tsx +113 -0
  138. package/templates/storefront-retail/components/policy-page.tsx +1 -1
  139. package/templates/storefront-retail/components/section-heading.tsx +2 -2
  140. package/templates/storefront-retail/package.json +1 -1
  141. package/templates/storefront-services/app/about/page.tsx +1 -1
  142. package/templates/storefront-services/app/book/book-client.tsx +2 -2
  143. package/templates/storefront-services/app/categories/[slug]/page.tsx +2 -2
  144. package/templates/storefront-services/app/collections/[slug]/page.tsx +2 -2
  145. package/templates/storefront-services/app/error.tsx +1 -1
  146. package/templates/storefront-services/app/faq/page.tsx +1 -1
  147. package/templates/storefront-services/app/layout.tsx +10 -31
  148. package/templates/storefront-services/app/not-found.tsx +1 -1
  149. package/templates/storefront-services/app/orders/[id]/page.tsx +1 -1
  150. package/templates/storefront-services/app/page.tsx +2 -2
  151. package/templates/storefront-services/app/privacy/page.tsx +1 -1
  152. package/templates/storefront-services/app/terms/page.tsx +1 -1
  153. package/templates/storefront-services/components/cart-pill.tsx +2 -2
  154. package/templates/storefront-services/components/category-grid.tsx +1 -1
  155. package/templates/storefront-services/components/collection-strip.tsx +1 -1
  156. package/templates/storefront-services/components/footer.tsx +1 -1
  157. package/templates/storefront-services/components/header.tsx +15 -9
  158. package/templates/storefront-services/components/hero.tsx +1 -1
  159. package/templates/storefront-services/components/json-ld.tsx +34 -0
  160. package/templates/storefront-services/components/mobile-nav.tsx +113 -0
  161. package/templates/storefront-services/components/policy-page.tsx +1 -1
  162. package/templates/storefront-services/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { gitDetectRoot, gitCurrentBranch, gitCurrentSha, gitStatusPorcelain } from './chunk-K5464A3L.mjs';
3
3
  import { parseEnvFile } from './chunk-DBZ3UOQ2.mjs';
4
- import { package_default } from './chunk-O6RONGAI.mjs';
4
+ import { package_default } from './chunk-2IKULCJK.mjs';
5
5
  import { parseArgs } from './chunk-C4M3DXKC.mjs';
6
6
  import { readAuthOrNull, readProjectLinkOrNull, readProjectState } from './chunk-UBAI443T.mjs';
7
7
  import { bold, dim, yellow, green, info, result, red } from './chunk-E2T2SBP5.mjs';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { TEMPLATES } from './chunk-OILDQUAD.mjs';
3
- import { package_default } from './chunk-O6RONGAI.mjs';
2
+ import { TEMPLATES } from './chunk-M5ZENIII.mjs';
3
+ import { package_default } from './chunk-2IKULCJK.mjs';
4
4
 
5
5
  // src/dispatcher.ts
6
6
  var VERSION = package_default.version ?? "unknown";
@@ -61,6 +61,7 @@ Domains:
61
61
  cimplify domains ls List business domains
62
62
  cimplify domains ls --attached List domains attached to the linked project
63
63
  cimplify domains add <domain> Add a domain (prints DNS records to set)
64
+ cimplify domains records <domain> Re-fetch DNS records (e.g. after CF populates DCV)
64
65
  cimplify domains verify <dom> Trigger verification after DNS is configured
65
66
  cimplify domains rm <dom> Remove a domain
66
67
  cimplify domains attach <dom> [flags] Attach a verified domain to the linked project
@@ -132,19 +133,19 @@ var COMMANDS = {
132
133
  cancel: () => import('./cancel-HANLRA6Q.mjs'),
133
134
  rollback: () => import('./rollback-DD4RNRFM.mjs'),
134
135
  env: () => import('./env-7ISJ73YI.mjs'),
135
- domains: () => import('./domains-AHH56CL7.mjs'),
136
+ domains: () => import('./domains-6GJASWJU.mjs'),
136
137
  logs: () => import('./logs-YNN2PQ24.mjs'),
137
138
  status: () => import('./status-JSYXM5RT.mjs'),
138
139
  dev: () => import('./dev-ONW2S77K.mjs'),
139
- introspect: () => import('./introspect-PQ476BL7.mjs'),
140
- doctor: () => import('./doctor-YPO2MQZF.mjs'),
141
- explain: () => import('./explain-L5ZDNMHC.mjs'),
140
+ introspect: () => import('./introspect-L53LUMAR.mjs'),
141
+ doctor: () => import('./doctor-JK2GOUKZ.mjs'),
142
+ explain: () => import('./explain-J6SJKV7A.mjs'),
142
143
  assets: () => import('./assets-EBEMMENZ.mjs'),
143
144
  repo: () => import('./repo-WOBWKEAO.mjs'),
144
- list: () => import('./list-5ORRWEYK.mjs'),
145
- add: () => import('./add-5EMD74VD.mjs'),
146
- update: () => import('./update-4AZUFFQ3.mjs'),
147
- upgrade: () => import('./update-4AZUFFQ3.mjs'),
145
+ list: () => import('./list-AADEEPYT.mjs'),
146
+ add: () => import('./add-XETP2LS5.mjs'),
147
+ update: () => import('./update-GB2FEYPY.mjs'),
148
+ upgrade: () => import('./update-GB2FEYPY.mjs'),
148
149
  "auth-step-up": () => import('./auth-step-up-BIUYQJP6.mjs')
149
150
  };
150
151
  var COMMAND_PREFIXES = {
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { gatherIntrospection } from './chunk-DAHD6YWI.mjs';
2
+ import { gatherIntrospection } from './chunk-SVW5FLGR.mjs';
3
3
  import './chunk-K5464A3L.mjs';
4
4
  import './chunk-DBZ3UOQ2.mjs';
5
- import './chunk-O6RONGAI.mjs';
5
+ import './chunk-2IKULCJK.mjs';
6
6
  import { parseArgs, flagBool } from './chunk-C4M3DXKC.mjs';
7
7
  import { ApiClient } from './chunk-MAOO6ZZ5.mjs';
8
8
  import { readAuthOrNull } from './chunk-UBAI443T.mjs';
@@ -4,7 +4,7 @@ import { ENV_SCOPE, DOMAIN_TYPE } from './chunk-MXYUAJEW.mjs';
4
4
  import { parseArgs, flagBool, flagString } from './chunk-C4M3DXKC.mjs';
5
5
  import { ApiClient } from './chunk-MAOO6ZZ5.mjs';
6
6
  import { readAuth, readProjectLink } from './chunk-UBAI443T.mjs';
7
- import { CliError, CLI_ERROR_CODE, info, dim, result, bold, success } from './chunk-E2T2SBP5.mjs';
7
+ import { CliError, CLI_ERROR_CODE, result, info, dim, bold, success } from './chunk-E2T2SBP5.mjs';
8
8
 
9
9
  // src/commands/domains.ts
10
10
  var SUB_LS = "ls";
@@ -16,6 +16,7 @@ var SUB_REMOVE = "remove";
16
16
  var SUB_ATTACH = "attach";
17
17
  var SUB_DETACH = "detach";
18
18
  var SUB_PRIMARY = "primary";
19
+ var SUB_RECORDS = "records";
19
20
  var FLAG_TYPE = "type";
20
21
  var FLAG_YES = "yes";
21
22
  var FLAG_ENV = "env";
@@ -79,7 +80,7 @@ async function run(argv) {
79
80
  if (!sub) {
80
81
  throw new CliError(
81
82
  CLI_ERROR_CODE.INVALID_INPUT,
82
- `Usage: cimplify domains <${SUB_LS}|${SUB_ADD}|${SUB_VERIFY}|${SUB_RM}|${SUB_ATTACH}|${SUB_DETACH}|${SUB_PRIMARY}>`
83
+ `Usage: cimplify domains <${SUB_LS}|${SUB_ADD}|${SUB_VERIFY}|${SUB_RM}|${SUB_ATTACH}|${SUB_DETACH}|${SUB_PRIMARY}|${SUB_RECORDS}>`
83
84
  );
84
85
  }
85
86
  const auth = await readAuth();
@@ -116,8 +117,25 @@ async function run(argv) {
116
117
  await setPrimaryAttachment(client, auth.businessId, args);
117
118
  return;
118
119
  }
120
+ if (sub === SUB_RECORDS) {
121
+ await fetchDnsRecords(client, args);
122
+ return;
123
+ }
119
124
  throw new CliError(CLI_ERROR_CODE.INVALID_INPUT, `Unknown domains subcommand "${sub}"`);
120
125
  }
126
+ async function fetchDnsRecords(client, args) {
127
+ const domain = args.positional[1];
128
+ if (!domain) {
129
+ throw new CliError(
130
+ CLI_ERROR_CODE.INVALID_INPUT,
131
+ `Usage: cimplify domains ${SUB_RECORDS} <domain>`
132
+ );
133
+ }
134
+ const dns = await client.get(dnsRecordsEndpoint(domain));
135
+ const records = Array.isArray(dns) ? dns : dns.records;
136
+ printDnsRecords(domain, records);
137
+ result({ domain, dns_records: records });
138
+ }
121
139
  async function listDomains(client, businessId) {
122
140
  const domains = await client.get(domainsEndpoint(businessId));
123
141
  if (domains.length === 0) {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-O6RONGAI.mjs';
2
+ import { package_default } from './chunk-2IKULCJK.mjs';
3
3
  import { parseArgs } from './chunk-C4M3DXKC.mjs';
4
4
  import { bold, dim, info, result, CliError, CLI_ERROR_CODE } from './chunk-E2T2SBP5.mjs';
5
5
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-DAHD6YWI.mjs';
2
+ export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-SVW5FLGR.mjs';
3
3
  import './chunk-K5464A3L.mjs';
4
4
  import './chunk-DBZ3UOQ2.mjs';
5
- import './chunk-O6RONGAI.mjs';
5
+ import './chunk-2IKULCJK.mjs';
6
6
  import './chunk-C4M3DXKC.mjs';
7
7
  import './chunk-UBAI443T.mjs';
8
8
  import './chunk-E2T2SBP5.mjs';
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { REGISTRY_INDEX } from './chunk-OILDQUAD.mjs';
2
+ import { REGISTRY_INDEX } from './chunk-M5ZENIII.mjs';
3
3
  import { parseArgs, flagBool } from './chunk-C4M3DXKC.mjs';
4
4
  import { CliError, CLI_ERROR_CODE, info, bold, dim, green, result } from './chunk-E2T2SBP5.mjs';
5
5
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-O6RONGAI.mjs';
2
+ import { package_default } from './chunk-2IKULCJK.mjs';
3
3
  import { promptYesNo } from './chunk-ITAFAORS.mjs';
4
4
  import { parseArgs, flagBool, flagString } from './chunk-C4M3DXKC.mjs';
5
5
  import { success, bold, info, dim, result, failure, CliError, CLI_ERROR_CODE, step, isJsonMode } from './chunk-E2T2SBP5.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/cli",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Cimplify CLI — deploy, manage env vars, link projects, and scaffold storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -11,7 +11,7 @@ export default function AboutPage() {
11
11
  // Title supports a single \n for a hard line break.
12
12
  const titleParts = a.title.split("\n");
13
13
  return (
14
- <article className="max-w-3xl mx-auto px-8 py-16">
14
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16">
15
15
  <p className="text-[11px] font-mono uppercase tracking-[0.16em] text-primary mb-2">
16
16
  {a.eyebrow}
17
17
  </p>
@@ -9,7 +9,7 @@ export const metadata: Metadata = {
9
9
  export default function FaqPage() {
10
10
  const f = brand.faq;
11
11
  return (
12
- <article className="max-w-3xl mx-auto px-8 py-16">
12
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16">
13
13
  <p className="text-[11px] font-mono uppercase tracking-[0.16em] text-primary mb-2">
14
14
  {f.eyebrow}
15
15
  </p>
@@ -4,7 +4,10 @@ import "./globals.css";
4
4
  import { Providers } from "@/components/providers";
5
5
  import { Header } from "@/components/header";
6
6
  import { Footer } from "@/components/footer";
7
+ import { ProductModal } from "@/components/product-modal";
7
8
  import { CartDrawer } from "@/components/cart-drawer";
9
+ import { OrganizationJsonLd } from "@/components/json-ld";
10
+ import { Suspense } from "react";
8
11
  import { brand } from "@/lib/brand";
9
12
  import { getSiteUrl } from "@/lib/site-url";
10
13
 
@@ -25,55 +28,36 @@ export async function generateMetadata(): Promise<Metadata> {
25
28
  return {
26
29
  metadataBase: new URL(siteUrl),
27
30
  title: {
28
- default: brand.name,
29
- template: `%s — ${brand.name}`,
31
+ default: brand.name,
32
+ template: `%s — ${brand.name}`,
30
33
  },
31
34
  description: brand.description,
32
35
  openGraph: {
33
- type: "website",
34
- siteName: brand.name,
35
- locale: brand.locale,
36
+ type: "website",
37
+ siteName: brand.name,
38
+ locale: brand.locale,
36
39
  },
37
40
  twitter: { card: "summary_large_image" },
38
41
  };
39
42
  }
40
43
 
41
- async function organizationLd() {
42
- const siteUrl = await getSiteUrl();
43
- return {
44
- "@context": "https://schema.org",
45
- "@type": brand.schemaType,
46
- name: brand.name,
47
- url: siteUrl,
48
- description: brand.description,
49
- email: brand.contact.email,
50
- telephone: brand.contact.phoneTel,
51
- address: {
52
- "@type": "PostalAddress",
53
- streetAddress: brand.contact.streetAddress,
54
- addressLocality: brand.contact.city,
55
- addressCountry: brand.contact.countryCode,
56
- },
57
- sameAs: brand.socials.map((s) => s.href),
58
- };
59
- }
60
-
61
- export default async function RootLayout({ children }: { children: React.ReactNode }) {
62
- const ld = await organizationLd();
44
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
63
45
  return (
64
46
  <html lang="en" suppressHydrationWarning className={`${inter.variable} ${mono.variable}`}>
65
47
  <body
66
48
  suppressHydrationWarning
67
49
  className="min-h-screen flex flex-col bg-background text-foreground font-sans"
68
50
  >
69
- <script
70
- type="application/ld+json"
71
- dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }}
72
- />
51
+ <Suspense fallback={null}>
52
+ <OrganizationJsonLd />
53
+ </Suspense>
73
54
  <Providers>
74
55
  <Header />
75
56
  <main className="flex-1 pb-12 w-full">{children}</main>
76
57
  <Footer />
58
+ <Suspense fallback={null}>
59
+ <ProductModal />
60
+ </Suspense>
77
61
  <CartDrawer />
78
62
  </Providers>
79
63
  </body>
@@ -3,7 +3,7 @@ import Link from "next/link";
3
3
  export default async function OrderPage({ params }: { params: Promise<{ id: string }> }) {
4
4
  const { id } = await params;
5
5
  return (
6
- <section className="max-w-2xl mx-auto px-8 py-20 text-center">
6
+ <section className="max-w-2xl mx-auto px-6 sm:px-8 py-20 text-center">
7
7
  <h1 className="text-3xl mt-0 mb-3 font-bold -tracking-[0.025em]">
8
8
  Order confirmed.
9
9
  </h1>
@@ -9,7 +9,7 @@ export const metadata: Metadata = {
9
9
  export default function PrivacyPage() {
10
10
  const p = brand.privacy;
11
11
  return (
12
- <article className="max-w-3xl mx-auto px-8 py-16 prose prose-lg max-w-none">
12
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16 prose prose-lg max-w-none">
13
13
  <p className="text-[11px] font-mono uppercase tracking-[0.16em] text-primary mb-2 not-prose">
14
14
  {p.eyebrow}
15
15
  </p>
@@ -9,7 +9,7 @@ export const metadata: Metadata = {
9
9
  export default function TermsPage() {
10
10
  const t = brand.terms;
11
11
  return (
12
- <article className="max-w-3xl mx-auto px-8 py-16 prose prose-lg max-w-none">
12
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16 prose prose-lg max-w-none">
13
13
  <p className="text-[11px] font-mono uppercase tracking-[0.16em] text-primary mb-2 not-prose">
14
14
  {t.eyebrow}
15
15
  </p>
@@ -17,7 +17,7 @@ export function CartPill() {
17
17
  type="button"
18
18
  onClick={open}
19
19
  aria-label={`Open cart, ${count} ${count === 1 ? "item" : "items"}`}
20
- className="inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full bg-foreground text-background text-xs font-semibold tracking-wide transition-transform hover:scale-105 cursor-pointer"
20
+ className="inline-flex items-center gap-1.5 px-4 py-2.5 sm:py-2 rounded-full bg-foreground text-background text-xs font-semibold tracking-wide transition-transform hover:scale-105 cursor-pointer"
21
21
  >
22
22
  Cart · {count}
23
23
  </button>
@@ -28,7 +28,7 @@ export function CartPillSkeleton() {
28
28
  return (
29
29
  <span
30
30
  aria-hidden
31
- className="inline-flex items-center gap-1.5 px-3.5 py-2 rounded-full bg-foreground/80 text-background text-xs font-semibold tracking-wide"
31
+ className="inline-flex items-center gap-1.5 px-4 py-2.5 sm:py-2 rounded-full bg-foreground/80 text-background text-xs font-semibold tracking-wide"
32
32
  >
33
33
  Cart · …
34
34
  </span>
@@ -11,7 +11,7 @@ import type { Category } from "@cimplify/sdk";
11
11
  export function CategoryGrid({ categories }: { categories?: Category[] }) {
12
12
  const router = useRouter();
13
13
  return (
14
- <section className="max-w-7xl mx-auto px-8 pt-14">
14
+ <section className="max-w-7xl mx-auto px-6 sm:px-8 pt-14">
15
15
  <h2 className="font-serif text-[28px] font-semibold m-0 mb-5">Browse the menu</h2>
16
16
  <SdkCategoryGrid
17
17
  categories={categories}
@@ -16,7 +16,7 @@ interface CollectionStripProps {
16
16
  export function CollectionStrip({ collection, products, collectionHref }: CollectionStripProps) {
17
17
  if (products.length === 0) return null;
18
18
  return (
19
- <section className="max-w-7xl mx-auto px-8 pt-12">
19
+ <section className="max-w-7xl mx-auto px-6 sm:px-8 pt-12">
20
20
  <header className="grid grid-cols-[1fr_auto] items-end gap-4 mb-5">
21
21
  <h2 className="font-serif text-[28px] font-semibold m-0">{collection.name}</h2>
22
22
  {collectionHref && (
@@ -2,6 +2,7 @@ import Link from "next/link";
2
2
  import { Suspense } from "react";
3
3
  import { NavLink } from "./nav-link";
4
4
  import { CartPill, CartPillSkeleton } from "./cart-pill";
5
+ import { MobileNav } from "./mobile-nav";
5
6
  import { brand } from "@/lib/brand";
6
7
 
7
8
  /**
@@ -22,16 +23,21 @@ export function Header() {
22
23
  {brand.microTag}
23
24
  </span>
24
25
  </Link>
25
- <nav className="flex items-center gap-5 sm:gap-6">
26
- {brand.header.nav.map((link) => (
27
- <Suspense key={link.href} fallback={<NavLinkFallback>{link.label}</NavLinkFallback>}>
28
- <NavLink href={link.href}>{link.label}</NavLink>
29
- </Suspense>
30
- ))}
26
+ <div className="flex items-center gap-3 sm:gap-6">
27
+ <nav className="hidden sm:flex items-center gap-5 sm:gap-6">
28
+ {brand.header.nav.map((link) => (
29
+ <Suspense key={link.href} fallback={<NavLinkFallback>{link.label}</NavLinkFallback>}>
30
+ <NavLink href={link.href}>{link.label}</NavLink>
31
+ </Suspense>
32
+ ))}
33
+ </nav>
31
34
  <Suspense fallback={<CartPillSkeleton />}>
32
35
  <CartPill />
33
36
  </Suspense>
34
- </nav>
37
+ <div className="sm:hidden">
38
+ <MobileNav />
39
+ </div>
40
+ </div>
35
41
  </header>
36
42
  );
37
43
  }
@@ -6,7 +6,7 @@ interface HeroProps {
6
6
 
7
7
  export function Hero({ badge, title, subtitle }: HeroProps) {
8
8
  return (
9
- <section className="relative px-8 py-20 text-center overflow-hidden bg-gradient-to-br from-foreground via-foreground to-primary text-background">
9
+ <section className="relative px-6 sm:px-8 py-20 text-center overflow-hidden bg-gradient-to-br from-foreground via-foreground to-primary text-background">
10
10
  <div className="absolute inset-0 opacity-[0.06] pointer-events-none [background-image:radial-gradient(circle_at_2px_2px,white_1px,transparent_0)] [background-size:32px_32px]" />
11
11
  <div className="relative max-w-3xl mx-auto">
12
12
  {badge && (
@@ -0,0 +1,34 @@
1
+ import { brand } from "@/lib/brand";
2
+ import { getSiteUrl } from "@/lib/site-url";
3
+
4
+ /**
5
+ * Streams the organization JSON-LD script tag in async so it doesn't block
6
+ * the rest of the layout shell. `getSiteUrl` reads request headers, and
7
+ * any await on dynamic data inside `RootLayout` makes Next 16 mark the
8
+ * whole route as blocking.
9
+ */
10
+ export async function OrganizationJsonLd(): Promise<React.ReactElement> {
11
+ const siteUrl = await getSiteUrl();
12
+ const ld = {
13
+ "@context": "https://schema.org",
14
+ "@type": brand.schemaType,
15
+ name: brand.name,
16
+ url: siteUrl,
17
+ description: brand.description,
18
+ email: brand.contact.email,
19
+ telephone: brand.contact.phoneTel,
20
+ address: {
21
+ "@type": "PostalAddress",
22
+ streetAddress: brand.contact.streetAddress,
23
+ addressLocality: brand.contact.city,
24
+ addressCountry: brand.contact.countryCode,
25
+ },
26
+ sameAs: brand.socials.map((s) => s.href),
27
+ };
28
+ return (
29
+ <script
30
+ type="application/ld+json"
31
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }}
32
+ />
33
+ );
34
+ }
@@ -0,0 +1,113 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import Link from "next/link";
5
+ import { brand } from "@/lib/brand";
6
+
7
+ /**
8
+ * Hamburger button + slide-in drawer for narrow viewports. Header hides
9
+ * its inline nav links below `sm` and renders this in their place; the
10
+ * cart pill stays in the header chrome.
11
+ */
12
+ export function MobileNav() {
13
+ const [open, setOpen] = useState(false);
14
+
15
+ useEffect(() => {
16
+ if (!open) return;
17
+ const onKey = (event: KeyboardEvent) => {
18
+ if (event.key === "Escape") setOpen(false);
19
+ };
20
+ document.addEventListener("keydown", onKey);
21
+ const previousOverflow = document.body.style.overflow;
22
+ document.body.style.overflow = "hidden";
23
+ return () => {
24
+ document.removeEventListener("keydown", onKey);
25
+ document.body.style.overflow = previousOverflow;
26
+ };
27
+ }, [open]);
28
+
29
+ return (
30
+ <>
31
+ <button
32
+ type="button"
33
+ onClick={() => setOpen(true)}
34
+ aria-label="Open menu"
35
+ aria-expanded={open}
36
+ aria-controls="mobile-nav-drawer"
37
+ className="grid place-items-center w-11 h-11 -mr-2 rounded-md text-foreground hover:bg-muted transition-colors"
38
+ >
39
+ <svg
40
+ width="20"
41
+ height="20"
42
+ viewBox="0 0 24 24"
43
+ fill="none"
44
+ stroke="currentColor"
45
+ strokeWidth="2"
46
+ strokeLinecap="round"
47
+ strokeLinejoin="round"
48
+ aria-hidden="true"
49
+ >
50
+ <line x1="3" y1="6" x2="21" y2="6" />
51
+ <line x1="3" y1="12" x2="21" y2="12" />
52
+ <line x1="3" y1="18" x2="21" y2="18" />
53
+ </svg>
54
+ </button>
55
+
56
+ {open ? (
57
+ <div className="fixed inset-0 z-50 sm:hidden">
58
+ <button
59
+ type="button"
60
+ onClick={() => setOpen(false)}
61
+ aria-label="Close menu"
62
+ className="absolute inset-0 bg-background/80 backdrop-blur-sm"
63
+ />
64
+ <nav
65
+ id="mobile-nav-drawer"
66
+ aria-label="Mobile navigation"
67
+ className="absolute inset-y-0 right-0 w-[85%] max-w-sm flex flex-col bg-background border-l border-border shadow-2xl"
68
+ >
69
+ <div className="flex items-center justify-between px-6 py-4 border-b border-border">
70
+ <span className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
71
+ Menu
72
+ </span>
73
+ <button
74
+ type="button"
75
+ onClick={() => setOpen(false)}
76
+ aria-label="Close menu"
77
+ className="grid place-items-center w-11 h-11 -mr-2 rounded-md text-foreground hover:bg-muted transition-colors"
78
+ >
79
+ <svg
80
+ width="20"
81
+ height="20"
82
+ viewBox="0 0 24 24"
83
+ fill="none"
84
+ stroke="currentColor"
85
+ strokeWidth="2"
86
+ strokeLinecap="round"
87
+ strokeLinejoin="round"
88
+ aria-hidden="true"
89
+ >
90
+ <line x1="18" y1="6" x2="6" y2="18" />
91
+ <line x1="6" y1="6" x2="18" y2="18" />
92
+ </svg>
93
+ </button>
94
+ </div>
95
+ <ul className="flex flex-col gap-1 px-3 py-4">
96
+ {brand.header.nav.map((link) => (
97
+ <li key={link.href}>
98
+ <Link
99
+ href={link.href}
100
+ onClick={() => setOpen(false)}
101
+ className="block px-3 py-3 rounded-md text-base font-medium text-foreground hover:bg-muted transition-colors"
102
+ >
103
+ {link.label}
104
+ </Link>
105
+ </li>
106
+ ))}
107
+ </ul>
108
+ </nav>
109
+ </div>
110
+ ) : null}
111
+ </>
112
+ );
113
+ }
@@ -13,7 +13,7 @@ interface PolicyShape {
13
13
  */
14
14
  export function PolicyPage({ policy }: { policy: PolicyShape }) {
15
15
  return (
16
- <article className="max-w-3xl mx-auto px-8 py-16 prose prose-lg max-w-none">
16
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16 prose prose-lg max-w-none">
17
17
  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-2 not-prose">
18
18
  {policy.eyebrow}
19
19
  </p>
@@ -9,7 +9,7 @@ interface SectionHeadingProps {
9
9
 
10
10
  export function SectionHeading({ eyebrow, title, description, link }: SectionHeadingProps) {
11
11
  return (
12
- <div className="flex items-end justify-between gap-6 mb-8">
12
+ <div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-3 sm:gap-6 mb-6 sm:mb-8">
13
13
  <div className="max-w-2xl">
14
14
  <p className="text-[11px] font-mono uppercase tracking-[0.16em] text-primary mb-2">
15
15
  {eyebrow}
@@ -24,7 +24,7 @@ export function SectionHeading({ eyebrow, title, description, link }: SectionHea
24
24
  {link && (
25
25
  <Link
26
26
  href={link.href}
27
- className="text-sm font-semibold text-primary hover:underline whitespace-nowrap hidden sm:inline-flex items-center gap-1"
27
+ className="text-sm font-semibold text-primary hover:underline whitespace-nowrap inline-flex items-center gap-1 self-start sm:self-auto"
28
28
  >
29
29
  {link.label}
30
30
  <svg viewBox="0 0 12 12" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden>
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.46.1",
20
+ "@cimplify/sdk": "^0.47.0",
21
21
  "next": "^16.2.4",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -10,7 +10,7 @@ export default function AboutPage() {
10
10
  const a = brand.about;
11
11
  const titleParts = a.title.split("\n");
12
12
  return (
13
- <article className="max-w-3xl mx-auto px-8 py-16">
13
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16">
14
14
  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-2">
15
15
  {a.eyebrow}
16
16
  </p>
@@ -71,7 +71,7 @@ async function CategoryContent({
71
71
 
72
72
  const { category, products } = data;
73
73
  return (
74
- <section className="max-w-7xl mx-auto px-8 pt-12">
74
+ <section className="max-w-7xl mx-auto px-6 sm:px-8 pt-12">
75
75
  <header className="mb-8 text-center">
76
76
  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-2">
77
77
  Category
@@ -102,7 +102,7 @@ async function CategoryContent({
102
102
 
103
103
  function CategorySkeleton() {
104
104
  return (
105
- <section className="max-w-7xl mx-auto px-8 pt-12">
105
+ <section className="max-w-7xl mx-auto px-6 sm:px-8 pt-12">
106
106
  <header className="mb-8 text-center">
107
107
  <div className="mx-auto h-3 w-20 bg-muted rounded mb-2 animate-pulse" />
108
108
  <div className="mx-auto h-10 w-64 bg-muted rounded mb-2 animate-pulse" />
@@ -71,7 +71,7 @@ async function CollectionContent({
71
71
 
72
72
  const { collection, products } = data;
73
73
  return (
74
- <section className="max-w-7xl mx-auto px-8 pt-12">
74
+ <section className="max-w-7xl mx-auto px-6 sm:px-8 pt-12">
75
75
  <header className="mb-8 text-center">
76
76
  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-2">
77
77
  Collection
@@ -102,7 +102,7 @@ async function CollectionContent({
102
102
 
103
103
  function CollectionSkeleton() {
104
104
  return (
105
- <section className="max-w-7xl mx-auto px-8 pt-12">
105
+ <section className="max-w-7xl mx-auto px-6 sm:px-8 pt-12">
106
106
  <header className="mb-8 text-center">
107
107
  <div className="mx-auto h-3 w-20 bg-muted rounded mb-2 animate-pulse" />
108
108
  <div className="mx-auto h-10 w-64 bg-muted rounded mb-2 animate-pulse" />
@@ -24,7 +24,7 @@ export default function GlobalError({
24
24
  }, [error]);
25
25
 
26
26
  return (
27
- <section className="max-w-2xl mx-auto px-8 py-20 text-center">
27
+ <section className="max-w-2xl mx-auto px-6 sm:px-8 py-20 text-center">
28
28
  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-3">
29
29
  Something broke
30
30
  </p>
@@ -9,7 +9,7 @@ export const metadata: Metadata = {
9
9
  export default function FaqPage() {
10
10
  const f = brand.faq;
11
11
  return (
12
- <article className="max-w-3xl mx-auto px-8 py-16">
12
+ <article className="max-w-3xl mx-auto px-6 sm:px-8 py-16">
13
13
  <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-primary mb-2">
14
14
  {f.eyebrow}
15
15
  </p>