@unbox-plus/cli 0.20.4

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 (358) hide show
  1. package/LICENSE.md +46 -0
  2. package/README.md +175 -0
  3. package/bin/cli.js +439 -0
  4. package/package.json +43 -0
  5. package/src/colors.js +100 -0
  6. package/src/presets.js +241 -0
  7. package/src/theme.js +253 -0
  8. package/template/.claude/agents/assets-marca.md +40 -0
  9. package/template/.claude/agents/avaliador-visual.md +48 -0
  10. package/template/.claude/agents/branding-briefing.md +401 -0
  11. package/template/.claude/agents/conteudo-secao.md +46 -0
  12. package/template/.claude/agents/paginas-legais.md +44 -0
  13. package/template/.claude/settings.json +3 -0
  14. package/template/.claude/skills/frontend-design/ATTRIBUTION.md +3 -0
  15. package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
  16. package/template/.claude/skills/frontend-design/SKILL.md +55 -0
  17. package/template/.claude/skills/web-design-guidelines/ATTRIBUTION.md +3 -0
  18. package/template/.claude/skills/web-design-guidelines/SKILL.md +50 -0
  19. package/template/.claude/skills/web-design-guidelines/guidelines-snapshot.md +180 -0
  20. package/template/.env.example +158 -0
  21. package/template/.mcp.json.example +14 -0
  22. package/template/.nvmrc +1 -0
  23. package/template/CLAUDE.md +333 -0
  24. package/template/COVERAGE.md +80 -0
  25. package/template/DEPLOY.md +179 -0
  26. package/template/QA.md +108 -0
  27. package/template/README.md +152 -0
  28. package/template/agents/CONSTRUCAO.md +207 -0
  29. package/template/agents/MANAGER.md +192 -0
  30. package/template/agents/PADROES.md +255 -0
  31. package/template/agents/definitions/00-scaffold.md +56 -0
  32. package/template/agents/definitions/01-layout.md +47 -0
  33. package/template/agents/definitions/02-homepage.md +70 -0
  34. package/template/agents/definitions/03-catalog.md +86 -0
  35. package/template/agents/definitions/04-pdp.md +97 -0
  36. package/template/agents/definitions/05-cart.md +116 -0
  37. package/template/agents/definitions/06-checkout.md +146 -0
  38. package/template/agents/definitions/07-auth.md +130 -0
  39. package/template/agents/definitions/08-customer.md +55 -0
  40. package/template/agents/definitions/09-promotions.md +62 -0
  41. package/template/agents/definitions/10-feedback.md +128 -0
  42. package/template/agents/definitions/11-seo-infra.md +159 -0
  43. package/template/agents/definitions/12-deploy.md +173 -0
  44. package/template/agents/definitions/13-cro.md +275 -0
  45. package/template/agents/definitions/14-seo.md +388 -0
  46. package/template/agents/definitions/15-branding.md +26 -0
  47. package/template/agents/definitions/16-qa-visual.md +138 -0
  48. package/template/agents/definitions/17-aeo.md +257 -0
  49. package/template/app/(loja)/busca/page.tsx +72 -0
  50. package/template/app/(loja)/carrinho/oferta/page.tsx +57 -0
  51. package/template/app/(loja)/carrinho/page.tsx +160 -0
  52. package/template/app/(loja)/categoria/[tagSlug]/layout.tsx +31 -0
  53. package/template/app/(loja)/categoria/[tagSlug]/loading.tsx +17 -0
  54. package/template/app/(loja)/categoria/[tagSlug]/page.tsx +49 -0
  55. package/template/app/(loja)/checkout/page.tsx +80 -0
  56. package/template/app/(loja)/checkout/pix/[ref]/page.tsx +133 -0
  57. package/template/app/(loja)/conta/assinaturas/[referenceId]/page.tsx +112 -0
  58. package/template/app/(loja)/conta/assinaturas/page.tsx +51 -0
  59. package/template/app/(loja)/conta/enderecos/page.tsx +20 -0
  60. package/template/app/(loja)/conta/entrar/page.tsx +119 -0
  61. package/template/app/(loja)/conta/page.tsx +45 -0
  62. package/template/app/(loja)/conta/pedidos/[referenceId]/page.tsx +49 -0
  63. package/template/app/(loja)/conta/pedidos/page.tsx +104 -0
  64. package/template/app/(loja)/conta/preferencias/page.tsx +24 -0
  65. package/template/app/(loja)/devolucoes/page.tsx +36 -0
  66. package/template/app/(loja)/layout.tsx +22 -0
  67. package/template/app/(loja)/oferta/page.tsx +57 -0
  68. package/template/app/(loja)/page.tsx +86 -0
  69. package/template/app/(loja)/pedido/[referenceId]/page.tsx +78 -0
  70. package/template/app/(loja)/privacidade/page.tsx +113 -0
  71. package/template/app/(loja)/produto/[productSlug]/layout.tsx +23 -0
  72. package/template/app/(loja)/produto/[productSlug]/loading.tsx +20 -0
  73. package/template/app/(loja)/produto/[productSlug]/page.tsx +372 -0
  74. package/template/app/(loja)/produtos/loading.tsx +16 -0
  75. package/template/app/(loja)/produtos/page.tsx +33 -0
  76. package/template/app/(loja)/termos/page.tsx +104 -0
  77. package/template/app/acesso/page.tsx +129 -0
  78. package/template/app/api/account/addresses/route.ts +39 -0
  79. package/template/app/api/account/exists/route.ts +21 -0
  80. package/template/app/api/account/me/route.ts +25 -0
  81. package/template/app/api/account/otp/route.ts +26 -0
  82. package/template/app/api/account/preferences/route.ts +19 -0
  83. package/template/app/api/account/signin/route.ts +27 -0
  84. package/template/app/api/account/signout/route.ts +9 -0
  85. package/template/app/api/acesso/route.ts +171 -0
  86. package/template/app/api/capi/route.ts +34 -0
  87. package/template/app/api/cart/coupon/route.ts +49 -0
  88. package/template/app/api/cart/items/route.ts +45 -0
  89. package/template/app/api/cart/link/route.ts +69 -0
  90. package/template/app/api/cart/route.ts +113 -0
  91. package/template/app/api/cart/share/route.ts +21 -0
  92. package/template/app/api/cep/[code]/route.ts +18 -0
  93. package/template/app/api/checkout/address/route.ts +24 -0
  94. package/template/app/api/checkout/email/route.ts +41 -0
  95. package/template/app/api/checkout/installments/route.ts +16 -0
  96. package/template/app/api/checkout/route.ts +110 -0
  97. package/template/app/api/checkout/shipping/route.ts +65 -0
  98. package/template/app/api/checkout-destination/route.ts +31 -0
  99. package/template/app/api/order/[ref]/route.ts +17 -0
  100. package/template/app/api/payment-link/route.ts +28 -0
  101. package/template/app/api/revalidate/route.ts +52 -0
  102. package/template/app/api/shipping/quote/route.ts +29 -0
  103. package/template/app/api/subscriptions/[id]/route.ts +52 -0
  104. package/template/app/api/track/route.ts +28 -0
  105. package/template/app/api/unbox/catalogo/route.ts +136 -0
  106. package/template/app/api/unbox/paginas/route.ts +167 -0
  107. package/template/app/api/unbox/vitrine/route.ts +84 -0
  108. package/template/app/api/webhooks/unbox/route.ts +97 -0
  109. package/template/app/apple-icon.svg +5 -0
  110. package/template/app/error.tsx +15 -0
  111. package/template/app/globals.css +457 -0
  112. package/template/app/icon.svg +4 -0
  113. package/template/app/layout.tsx +90 -0
  114. package/template/app/llms.txt/route.ts +43 -0
  115. package/template/app/manifest.ts +21 -0
  116. package/template/app/not-found.tsx +18 -0
  117. package/template/app/opengraph-image.tsx +35 -0
  118. package/template/app/robots.ts +31 -0
  119. package/template/app/sitemap.ts +42 -0
  120. package/template/bootstrap.sh +87 -0
  121. package/template/components/account/account-shell.tsx +47 -0
  122. package/template/components/account/address-book.tsx +147 -0
  123. package/template/components/account/preferences-form.tsx +77 -0
  124. package/template/components/account/reorder-button.tsx +47 -0
  125. package/template/components/account/signout-button.tsx +20 -0
  126. package/template/components/account/subscription-actions.tsx +204 -0
  127. package/template/components/account-nav.tsx +41 -0
  128. package/template/components/address-fields.tsx +102 -0
  129. package/template/components/analytics/data-layer-ready.tsx +16 -0
  130. package/template/components/analytics/purchase-tracker.tsx +32 -0
  131. package/template/components/brand-search.tsx +34 -0
  132. package/template/components/cart/cart-provider.tsx +340 -0
  133. package/template/components/cart/mini-cart.tsx +228 -0
  134. package/template/components/cart-button.tsx +35 -0
  135. package/template/components/catalog/catalog-client.tsx +523 -0
  136. package/template/components/catalog/grid-skeleton.tsx +19 -0
  137. package/template/components/catalog/pager.tsx +66 -0
  138. package/template/components/catalog/product-grid-card.tsx +52 -0
  139. package/template/components/catalog/product-grid.tsx +27 -0
  140. package/template/components/checkout/checkout-client.tsx +1333 -0
  141. package/template/components/chrome/announce-bar.tsx +57 -0
  142. package/template/components/chrome/chrome-recipe.ts +13 -0
  143. package/template/components/chrome/footers/captura-botao.tsx +28 -0
  144. package/template/components/chrome/footers/colunas.tsx +110 -0
  145. package/template/components/chrome/footers/conversao.tsx +90 -0
  146. package/template/components/chrome/footers/editorial.tsx +98 -0
  147. package/template/components/chrome/footers/minimal.tsx +55 -0
  148. package/template/components/chrome/header-bar-mobile.tsx +64 -0
  149. package/template/components/chrome/headers/centralizado.tsx +71 -0
  150. package/template/components/chrome/headers/classico.tsx +64 -0
  151. package/template/components/chrome/headers/compacto.tsx +66 -0
  152. package/template/components/chrome/headers/equilibrado.tsx +73 -0
  153. package/template/components/chrome/headers/imersivo.tsx +62 -0
  154. package/template/components/chrome/registry.ts +70 -0
  155. package/template/components/chrome/solid-on-scroll.tsx +39 -0
  156. package/template/components/empty-state.tsx +22 -0
  157. package/template/components/home/combo-card-compact.tsx +67 -0
  158. package/template/components/home/combos-home.tsx +64 -0
  159. package/template/components/home/combos-section.tsx +177 -0
  160. package/template/components/home/home-recipe.ts +41 -0
  161. package/template/components/home/sections/attributes-marquee.tsx +178 -0
  162. package/template/components/home/sections/benefits.tsx +80 -0
  163. package/template/components/home/sections/bloco-html.tsx +47 -0
  164. package/template/components/home/sections/catalogo.ts +165 -0
  165. package/template/components/home/sections/category-pills.tsx +32 -0
  166. package/template/components/home/sections/combos-carousel.tsx +141 -0
  167. package/template/components/home/sections/comparison.tsx +73 -0
  168. package/template/components/home/sections/founder-story.tsx +40 -0
  169. package/template/components/home/sections/hero.tsx +187 -0
  170. package/template/components/home/sections/kits.tsx +11 -0
  171. package/template/components/home/sections/media-cards.tsx +70 -0
  172. package/template/components/home/sections/newsletter.tsx +62 -0
  173. package/template/components/home/sections/product-showcase.tsx +175 -0
  174. package/template/components/home/sections/purchase-hero.tsx +187 -0
  175. package/template/components/home/sections/quote-banner.tsx +50 -0
  176. package/template/components/home/sections/registry.ts +186 -0
  177. package/template/components/home/sections/reviews-carousel.tsx +66 -0
  178. package/template/components/home/sections/reviews.tsx +68 -0
  179. package/template/components/home/sections/ritual.tsx +96 -0
  180. package/template/components/home/sections/savings.tsx +47 -0
  181. package/template/components/home/sections/social-row.tsx +65 -0
  182. package/template/components/home/sections/spec-table.tsx +117 -0
  183. package/template/components/home/sections/stars.tsx +12 -0
  184. package/template/components/home/sections/stats-grid.tsx +54 -0
  185. package/template/components/home/sections/trust-bar.tsx +56 -0
  186. package/template/components/home/sections/trust-strip.tsx +40 -0
  187. package/template/components/home/sections/video-wall.tsx +59 -0
  188. package/template/components/landing/landing-recipe.ts +20 -0
  189. package/template/components/landing/oferta-sections.tsx +40 -0
  190. package/template/components/landing/product-picker.tsx +257 -0
  191. package/template/components/mobile-nav.tsx +71 -0
  192. package/template/components/order-status.tsx +163 -0
  193. package/template/components/powered-by-unbox.tsx +27 -0
  194. package/template/components/product/pdp/buy-box.tsx +604 -0
  195. package/template/components/product/pdp/catalog-grid.tsx +113 -0
  196. package/template/components/product/pdp/faq-modelo.ts +86 -0
  197. package/template/components/product/pdp/gallery.tsx +87 -0
  198. package/template/components/product/pdp/interactive.tsx +200 -0
  199. package/template/components/product/pdp/newsletter.tsx +64 -0
  200. package/template/components/product/pdp/payment-chips.tsx +22 -0
  201. package/template/components/product/pdp/pdp-view.tsx +227 -0
  202. package/template/components/product/pdp/recommendations.tsx +144 -0
  203. package/template/components/product/pdp/sections.tsx +173 -0
  204. package/template/components/quantity-stepper.tsx +36 -0
  205. package/template/components/search-box.tsx +37 -0
  206. package/template/components/site-footer.tsx +59 -0
  207. package/template/components/site-header.tsx +64 -0
  208. package/template/components/ui/accordion.tsx +72 -0
  209. package/template/components/ui/alert-dialog.tsx +187 -0
  210. package/template/components/ui/alert.tsx +76 -0
  211. package/template/components/ui/aspect-ratio.tsx +22 -0
  212. package/template/components/ui/avatar.tsx +109 -0
  213. package/template/components/ui/badge.tsx +52 -0
  214. package/template/components/ui/breadcrumb.tsx +125 -0
  215. package/template/components/ui/button.tsx +58 -0
  216. package/template/components/ui/card.tsx +103 -0
  217. package/template/components/ui/carousel.tsx +242 -0
  218. package/template/components/ui/command.tsx +196 -0
  219. package/template/components/ui/dialog.tsx +160 -0
  220. package/template/components/ui/dropdown-menu.tsx +268 -0
  221. package/template/components/ui/foto.tsx +54 -0
  222. package/template/components/ui/input-group.tsx +158 -0
  223. package/template/components/ui/input-otp.tsx +87 -0
  224. package/template/components/ui/input.tsx +20 -0
  225. package/template/components/ui/label.tsx +20 -0
  226. package/template/components/ui/navigation-menu.tsx +168 -0
  227. package/template/components/ui/pagination.tsx +132 -0
  228. package/template/components/ui/popover.tsx +90 -0
  229. package/template/components/ui/progress.tsx +83 -0
  230. package/template/components/ui/radio-group.tsx +38 -0
  231. package/template/components/ui/scroll-area.tsx +55 -0
  232. package/template/components/ui/select.tsx +201 -0
  233. package/template/components/ui/separator.tsx +25 -0
  234. package/template/components/ui/sheet.tsx +138 -0
  235. package/template/components/ui/skeleton.tsx +13 -0
  236. package/template/components/ui/sonner.tsx +44 -0
  237. package/template/components/ui/switch.tsx +32 -0
  238. package/template/components/ui/table.tsx +116 -0
  239. package/template/components/ui/tabs.tsx +82 -0
  240. package/template/components/ui/textarea.tsx +18 -0
  241. package/template/components/ui/toggle-group.tsx +89 -0
  242. package/template/components/ui/toggle.tsx +45 -0
  243. package/template/components/ui/tooltip.tsx +66 -0
  244. package/template/components.json +25 -0
  245. package/template/eslint.config.mjs +36 -0
  246. package/template/gitignore +21 -0
  247. package/template/lib/analytics.ts +369 -0
  248. package/template/lib/api.ts +60 -0
  249. package/template/lib/capi.ts +87 -0
  250. package/template/lib/cart-link.ts +51 -0
  251. package/template/lib/cart-normalize.ts +142 -0
  252. package/template/lib/cart-recovery.ts +23 -0
  253. package/template/lib/cart-response.ts +48 -0
  254. package/template/lib/catalog-map.ts +54 -0
  255. package/template/lib/catalog.ts +3 -0
  256. package/template/lib/checkout-lock.ts +21 -0
  257. package/template/lib/checkout-nav.ts +22 -0
  258. package/template/lib/config.ts +51 -0
  259. package/template/lib/crm.ts +40 -0
  260. package/template/lib/customer-session.ts +21 -0
  261. package/template/lib/dataloader.ts +23 -0
  262. package/template/lib/editable/config.ts +11 -0
  263. package/template/lib/editable/document.ts +1762 -0
  264. package/template/lib/editable/index.ts +3 -0
  265. package/template/lib/editable/primitives.tsx +821 -0
  266. package/template/lib/editable/provider.tsx +861 -0
  267. package/template/lib/editable/rastreio-navegacao.tsx +65 -0
  268. package/template/lib/editable/rastreio.tsx +168 -0
  269. package/template/lib/editable/server.ts +189 -0
  270. package/template/lib/editable/tokens.ts +22 -0
  271. package/template/lib/editable/verify.ts +24 -0
  272. package/template/lib/enrichment/combos.ts +184 -0
  273. package/template/lib/enrichment/index.ts +188 -0
  274. package/template/lib/enrichment/products.json +1 -0
  275. package/template/lib/env-check.ts +47 -0
  276. package/template/lib/format.ts +130 -0
  277. package/template/lib/icons.ts +67 -0
  278. package/template/lib/json-ld.ts +9 -0
  279. package/template/lib/llms-txt.ts +75 -0
  280. package/template/lib/mockup.ts +24 -0
  281. package/template/lib/newsletter.ts +12 -0
  282. package/template/lib/orders.ts +160 -0
  283. package/template/lib/queries.ts +66 -0
  284. package/template/lib/ratelimit.ts +47 -0
  285. package/template/lib/rotas-editaveis.ts +192 -0
  286. package/template/lib/sanitize.ts +36 -0
  287. package/template/lib/schemas.ts +78 -0
  288. package/template/lib/session.ts +98 -0
  289. package/template/lib/store-config.ts +67 -0
  290. package/template/lib/unbox/client.ts +829 -0
  291. package/template/lib/unbox/customer.ts +224 -0
  292. package/template/lib/unbox/errors.ts +97 -0
  293. package/template/lib/unbox/index.ts +10 -0
  294. package/template/lib/unbox/store.ts +113 -0
  295. package/template/lib/unbox/types.ts +195 -0
  296. package/template/lib/unbox/webhooks.ts +66 -0
  297. package/template/lib/utils.ts +6 -0
  298. package/template/lib/vitrine.ts +242 -0
  299. package/template/lib/webhook-store.ts +22 -0
  300. package/template/middleware.ts +178 -0
  301. package/template/next.config.ts +63 -0
  302. package/template/package.json +62 -0
  303. package/template/postcss.config.mjs +5 -0
  304. package/template/public/brand/coll/default.png +0 -0
  305. package/template/public/brand/hero-desktop.svg +12 -0
  306. package/template/public/brand/hero-mobile.svg +12 -0
  307. package/template/public/brand/heros/boutique-desktop.svg +11 -0
  308. package/template/public/brand/heros/boutique-mobile.svg +11 -0
  309. package/template/public/brand/heros/editorial-desktop.svg +11 -0
  310. package/template/public/brand/heros/editorial-mobile.svg +11 -0
  311. package/template/public/brand/heros/essencial-desktop.svg +12 -0
  312. package/template/public/brand/heros/essencial-mobile.svg +12 -0
  313. package/template/public/brand/heros/promocional-desktop.svg +12 -0
  314. package/template/public/brand/heros/promocional-mobile.svg +12 -0
  315. package/template/public/brand/logo-chrome.svg +6 -0
  316. package/template/public/brand/logo-white.svg +6 -0
  317. package/template/public/brand/logo.svg +7 -0
  318. package/template/public/brand/pay-amex.webp +0 -0
  319. package/template/public/brand/pay-elo.webp +0 -0
  320. package/template/public/brand/pay-mastercard.webp +0 -0
  321. package/template/public/brand/pay-pix.webp +0 -0
  322. package/template/public/brand/pay-visa.webp +0 -0
  323. package/template/public/brand/ph/avatar-a.svg +5 -0
  324. package/template/public/brand/ph/avatar-b.svg +5 -0
  325. package/template/public/brand/ph/avatar-c.svg +5 -0
  326. package/template/public/brand/ph/avatar-d.svg +5 -0
  327. package/template/public/brand/ph/photo-a.svg +7 -0
  328. package/template/public/brand/ph/photo-b.svg +6 -0
  329. package/template/public/brand/ph/photo-c.svg +6 -0
  330. package/template/public/brand/ph/poster-a.svg +6 -0
  331. package/template/public/brand/ph/poster-b.svg +6 -0
  332. package/template/public/brand/ph/poster-c.svg +6 -0
  333. package/template/public/unbox/powered-by-fundo-preto.png +0 -0
  334. package/template/public/unbox/powered-by-transparente.png +0 -0
  335. package/template/public/unbox/powered-by.png +0 -0
  336. package/template/scripts/abandoned-cart.ts +82 -0
  337. package/template/scripts/check-editable.mjs +467 -0
  338. package/template/scripts/check-honestidade.mjs +111 -0
  339. package/template/scripts/check-placeholder.mjs +200 -0
  340. package/template/scripts/check-recipe.mjs +76 -0
  341. package/template/scripts/check-unbox-brand.mjs +298 -0
  342. package/template/scripts/contraste.js +177 -0
  343. package/template/scripts/dump-catalog.ts +95 -0
  344. package/template/scripts/load-env.ts +13 -0
  345. package/template/scripts/medir-sistema.mjs +56 -0
  346. package/template/scripts/place-order-pix.ts +61 -0
  347. package/template/scripts/qa-screenshots.mjs +150 -0
  348. package/template/scripts/sistema.py +231 -0
  349. package/template/scripts/subscribe-webhook.ts +36 -0
  350. package/template/scripts/test-live.ts +186 -0
  351. package/template/scripts/vocabulario.py +264 -0
  352. package/template/tsconfig.json +27 -0
  353. package/tools/LEIA-ME.md +39 -0
  354. package/tools/check-template-neutro.mjs +111 -0
  355. package/tools/notion-doc.mjs +191 -0
  356. package/tools/tokenize-neutrals.mjs +0 -0
  357. package/tools/tokenize-radius.mjs +52 -0
  358. package/tools/workflow-storefront.legado.js +1207 -0
@@ -0,0 +1,457 @@
1
+ /* ═══════════════════════════════════════════════════════════════════════════════════════
2
+ MAPA DESTE ARQUIVO — ~420 linhas, e quase toda edição de marca mora numa só delas.
3
+
4
+ Na ordem em que aparecem (busque pelo marcador, não conte linhas — elas andam):
5
+
6
+ @import Tailwind v4, tw-animate-css, shadcn
7
+ @theme inline ponte tokens --store-* → utilitários do Tailwind/shadcn.
8
+ Derivação, não decisão: você raramente mexe aqui.
9
+ :root ◀ AS DECISÕES. Cor, fonte, raio, ritmo vertical, largura de
10
+ container e eixos de composição (--card-*). A marca entra AQUI.
11
+ @layer base reset e defaults de elemento
12
+ "PDP layout" página de produto e cards
13
+ "Marquee"/"Chama" animações pontuais
14
+ "Checkout" chrome próprio, full-bleed, nav inferior mobile
15
+ "Header SOBREPOSTO" variante "imersivo" do chrome
16
+ "HTML rico" descrição do produto vinda da API, sanitizada
17
+ "── MOTION" token --motion e animações de scroll (valor 0 desliga tudo)
18
+
19
+ Pra trocar a paleta da marca, só o :root importa. Não leia o resto do arquivo.
20
+ ═══════════════════════════════════════════════════════════════════════════════════════ */
21
+ @import "tailwindcss";
22
+ @import "tw-animate-css";
23
+ @import "shadcn/tailwind.css";
24
+
25
+ @custom-variant dark (&:is(.dark *));
26
+
27
+ @theme inline {
28
+ /* Fontes — as famílias reais são declaradas no bloco UNBOX-FONTS de app/layout.tsx
29
+ (reescrito pelo create-unbox-store conforme o estilo/preset escolhido). */
30
+ --font-sans: var(--font-sans);
31
+ --font-mono: var(--font-geist-mono);
32
+ --font-heading: var(--font-display);
33
+
34
+ /* Cores semânticas → tokens derivados */
35
+ --color-background: var(--background);
36
+ --color-foreground: var(--foreground);
37
+ --color-card: var(--card);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-popover: var(--popover);
40
+ --color-popover-foreground: var(--popover-foreground);
41
+ --color-primary: var(--primary);
42
+ --color-primary-foreground: var(--primary-foreground);
43
+ --color-secondary: var(--secondary);
44
+ --color-secondary-foreground: var(--secondary-foreground);
45
+ --color-muted: var(--muted);
46
+ --color-muted-foreground: var(--muted-foreground);
47
+ --color-accent: var(--accent);
48
+ --color-accent-foreground: var(--accent-foreground);
49
+ --color-destructive: var(--destructive);
50
+ --color-border: var(--border);
51
+ --color-input: var(--input);
52
+ --color-ring: var(--ring);
53
+
54
+ /* Semânticos de e-commerce */
55
+ --color-price: var(--price);
56
+ --color-success: var(--success);
57
+ --color-rating: var(--rating);
58
+ --color-warning: var(--warning);
59
+
60
+ /* Brand (compat) */
61
+ --color-brand: var(--brand);
62
+ --color-brand-soft: var(--brand-soft);
63
+
64
+ /* Charts */
65
+ --color-chart-1: var(--chart-1);
66
+ --color-chart-2: var(--chart-2);
67
+ --color-chart-3: var(--chart-3);
68
+ --color-chart-4: var(--chart-4);
69
+ --color-chart-5: var(--chart-5);
70
+
71
+ /* Sidebar */
72
+ --color-sidebar: var(--sidebar);
73
+ --color-sidebar-foreground: var(--sidebar-foreground);
74
+ --color-sidebar-primary: var(--sidebar-primary);
75
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
76
+ --color-sidebar-accent: var(--sidebar-accent);
77
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
78
+ --color-sidebar-border: var(--sidebar-border);
79
+ --color-sidebar-ring: var(--sidebar-ring);
80
+
81
+ /* Radius — escala inteira deriva de --radius (escrito pelo CLI conforme o estilo).
82
+ Multiplicadores calibrados pros raios usados no design (lg=14px, xl≈18px, 2xl≈22px
83
+ com o default 0.875rem). */
84
+ --radius-sm: calc(var(--radius) * 0.6);
85
+ --radius-md: calc(var(--radius) * 0.8);
86
+ --radius-lg: var(--radius);
87
+ --radius-xl: calc(var(--radius) * 1.3);
88
+ --radius-2xl: calc(var(--radius) * 1.6);
89
+ --radius-3xl: calc(var(--radius) * 2.2);
90
+ --radius-4xl: calc(var(--radius) * 2.6);
91
+ }
92
+
93
+ :root {
94
+ /* ═══════════════════════════════════════════════════════════════
95
+ BRAND TOKENS — editar aqui ao criar uma nova loja.
96
+ Escritos pelo create-unbox-store (cores da marca + estilo/preset).
97
+
98
+ ⚠️ O cinza neutro abaixo é CANVAS PROVISÓRIA, não escolha estética: é a tela mais
99
+ neutra possível pra qualquer cor de marca cair bem em cima. Aplicar as cores REAIS
100
+ da marca é o primeiro ato do briefing — loja entregue com esta paleta é loja sem
101
+ identidade (o `npm run build` avisa enquanto ela estiver aqui).
102
+ ═══════════════════════════════════════════════════════════════
103
+
104
+ --store-primary botões, preços, links ativos, ícones de ação
105
+ --store-primary-dark hover e pressed dos botões primários
106
+ --store-primary-soft fundo suave: badges de verificado, avisos verdes
107
+ --store-cta banners de oferta, newsletter, badge de desconto, "comprar agora"
108
+ --store-cta-fg texto sobre a cor CTA
109
+ --store-cta-dark hover/pressed dos elementos com fundo CTA
110
+ --store-cta-soft fundo suave da família CTA (chips, avisos)
111
+ --store-chrome-bg header e footer (barra da marca)
112
+ --store-chrome-text texto sobre o chrome
113
+ --store-chrome-muted texto secundário/ícones sobre o chrome
114
+ --store-chrome-line divisores dentro do chrome (rodapé)
115
+ ─────────────────────────────────────────────────────────────── */
116
+ --store-primary: #18181B;
117
+ --store-primary-dark: #09090B;
118
+ --store-primary-soft: #F1F1F3;
119
+ --store-cta: #D97706;
120
+ --store-cta-fg: #1C1207;
121
+ --store-cta-dark: #B45309;
122
+ --store-cta-soft: #FDF0DC;
123
+ --store-chrome-bg: #18181B;
124
+ --store-chrome-text: #FFFFFF;
125
+ --store-chrome-muted: #A1A1AA;
126
+ --store-chrome-line: #34343A;
127
+
128
+ /* ── Neutros do ESTILO (mudam por preset, não pela cor da marca) ──
129
+ --store-ink texto principal (quase preto do estilo)
130
+ --store-ink-2 texto de apoio forte, ícones escuros
131
+ --store-muted texto secundário
132
+ --store-faint texto terciário, placeholders, bordas de foco suaves
133
+ --store-bg fundo global das páginas
134
+ --store-surface superfícies elevadas claras (cards suaves)
135
+ --store-surface-2 superfícies de destaque (faixas, blocos de seção)
136
+ --store-line bordas e divisores padrão
137
+ --store-line-2 bordas mais marcadas (cards, inputs)
138
+ --store-sale preço riscado/urgência/erro comercial (vermelho do estilo)
139
+ --store-sale-soft fundo suave do vermelho comercial */
140
+ --store-ink: #18181B;
141
+ --store-ink-2: #3F3F46;
142
+ --store-muted: #71717A;
143
+ --store-faint: #A1A1AA;
144
+
145
+ /* ── SOMBRAS ─────────────────────────────────────────────────────────────────────────
146
+ NUNCA escreva cor literal em box-shadow. Foi exatamente assim que o verde da
147
+ foundation original sobreviveu a uma varredura que trocou 304 cores literais: ela
148
+ olhou classes de cor, e estes estavam dentro de `shadow-[...]` e de `boxShadow`
149
+ inline. Numa loja cobalto ou âmbar, o brilho continuava verde.
150
+ A sombra de CTA acompanha a marca via color-mix; as de card são neutras de
151
+ propósito (sombra é luz, não identidade). */
152
+ --store-shadow-cta: 0 6px 16px color-mix(in srgb, var(--store-primary, #18181B) 28%, transparent);
153
+ --store-shadow-cta-sm: 0 4px 10px color-mix(in srgb, var(--store-primary, #18181B) 30%, transparent);
154
+ --store-shadow-glow: 0 4px 12px color-mix(in srgb, var(--store-primary, #18181B) 32%, transparent);
155
+ --store-shadow-card: 0 10px 30px rgba(24, 24, 27, .10);
156
+ --store-shadow-card-lg: 0 12px 34px rgba(24, 24, 27, .18);
157
+ --store-shadow-soft: 0 4px 14px rgba(24, 24, 27, .07);
158
+ --store-shadow-hover: 0 8px 24px rgba(24, 24, 27, .08);
159
+ --store-bg: #FAFAFA;
160
+ --store-surface: #FFFFFF;
161
+ --store-surface-2: #F4F4F5;
162
+ --store-line: #E4E4E7;
163
+ --store-line-2: #D4D4D8;
164
+ --store-sale: #DC2626;
165
+ --store-sale-soft: #FEE9E9;
166
+
167
+ /* ── Eixos de COMPOSIÇÃO (linguagem visual, mudam por estilo) ──
168
+ --section-gap ritmo vertical entre seções da home/landing (denso ↔ arejado)
169
+ --container-max largura do conteúdo das seções (1240px padrão; menor = editorial)
170
+ --card-bg/border/shadow chrome dos cards de seção (.store-card):
171
+ bordado (default) · sombra flutuante · preenchido flat */
172
+ --section-gap: 52px;
173
+ --container-max: 1240px;
174
+ --card-bg: #ffffff;
175
+ --card-border: 1px solid var(--store-line);
176
+ --card-shadow: none;
177
+ /* ═══════════════════════════════════════════════════════════════ */
178
+
179
+ /* ── Tokens derivados dos brand tokens acima ────────────────── */
180
+
181
+ /* Shadcn/Tailwind — primary aponta diretamente para o brand token */
182
+ --primary: var(--store-primary);
183
+ --primary-foreground: #ffffff;
184
+
185
+ /* CTA / accent */
186
+ --accent: var(--store-cta);
187
+ --accent-foreground: var(--store-cta-fg);
188
+
189
+ /* Semânticos derivados */
190
+ --price: var(--store-primary);
191
+ --success: var(--store-primary);
192
+ --rating: var(--store-cta);
193
+ --warning: var(--store-cta);
194
+
195
+ /* Compat — código existente que referencia --brand-* ou --brand */
196
+ --brand: var(--store-primary);
197
+ --brand-soft: var(--store-primary-soft);
198
+
199
+ /* ── Superfícies e neutros derivados (a base vem dos tokens de ESTILO acima) ── */
200
+ --background: var(--store-bg);
201
+ --foreground: var(--store-ink);
202
+
203
+ --card: oklch(1 0 0);
204
+ --card-foreground: var(--store-ink);
205
+ --popover: oklch(1 0 0);
206
+ --popover-foreground: var(--store-ink);
207
+
208
+ --secondary: var(--store-surface-2);
209
+ --secondary-foreground: var(--store-ink-2);
210
+
211
+ --muted: var(--store-surface-2);
212
+ --muted-foreground: var(--store-muted);
213
+
214
+ --destructive: oklch(0.561 0.218 26.977);
215
+
216
+ --border: var(--store-line);
217
+ --input: var(--store-line);
218
+ --ring: var(--store-primary);
219
+
220
+ /* Radius */
221
+ --radius: 0.875rem;
222
+
223
+ /* Sidebar / mobile bottom nav */
224
+ --sidebar: oklch(1 0 0);
225
+ --sidebar-foreground: var(--store-ink);
226
+ --sidebar-primary: var(--store-primary);
227
+ --sidebar-primary-foreground: #ffffff;
228
+ --sidebar-accent: var(--store-primary-soft);
229
+ --sidebar-accent-foreground: var(--store-ink-2);
230
+ --sidebar-border: var(--store-line);
231
+ --sidebar-ring: var(--store-primary);
232
+
233
+ /* Charts */
234
+ --chart-1: var(--store-primary);
235
+ --chart-2: var(--store-cta);
236
+ --chart-3: oklch(0.561 0.218 26.977);
237
+ --chart-4: oklch(0.603 0.129 118.456);
238
+ --chart-5: oklch(0.685 0.147 71.678);
239
+ }
240
+
241
+ .dark {
242
+ /* Brand tokens herdados do :root — não precisam mudar no dark mode.
243
+ Apenas as superfícies e neutros são reajustados. */
244
+
245
+ --background: oklch(0.18 0.02 260); /* neutro escuro genérico */
246
+ --foreground: oklch(0.95 0.005 90);
247
+
248
+ --card: oklch(0.22 0.02 260);
249
+ --card-foreground: oklch(0.95 0.005 90);
250
+ --popover: oklch(0.22 0.02 260);
251
+ --popover-foreground: oklch(0.95 0.005 90);
252
+
253
+ /* primary/accent continuam apontando para --store-* via :root */
254
+
255
+ --secondary: oklch(0.28 0.02 260);
256
+ --secondary-foreground: oklch(0.95 0.005 90);
257
+
258
+ --muted: oklch(0.28 0.02 260);
259
+ --muted-foreground: oklch(0.65 0.01 90);
260
+
261
+ --destructive: oklch(0.62 0.23 27);
262
+
263
+ --border: oklch(1 0 0 / 10%);
264
+ --input: oklch(1 0 0 / 14%);
265
+
266
+ --sidebar: oklch(0.22 0.02 260);
267
+ --sidebar-foreground: oklch(0.95 0.005 90);
268
+ --sidebar-border: oklch(1 0 0 / 10%);
269
+
270
+ --chart-3: oklch(0.62 0.23 27);
271
+ }
272
+
273
+ @layer base {
274
+ * {
275
+ @apply border-border outline-ring/50;
276
+ }
277
+ body {
278
+ @apply bg-background text-foreground;
279
+ /* A família vive AQUI, não no <html>: as variáveis do next/font (--font-sans etc.)
280
+ são declaradas na className do <body>, então uma regra em html não as enxerga e
281
+ o corpo cai no serif do navegador. Só aparecia no build de produção, e só fora
282
+ do .store-layout (ex.: /carrinho). */
283
+ font-family: var(--font-sans), system-ui, -apple-system, sans-serif;
284
+ }
285
+ html {
286
+ /* Mata a barra de rolagem HORIZONTAL acidental: o hack .full-bleed usa 100vw, e 100vw
287
+ INCLUI a largura da barra de rolagem vertical (Windows/Linux e Mac com scrollbar
288
+ fixa) — sobram ~15px além da tela. `clip` corta o excedente SEM criar um scroll
289
+ container novo (não quebra position: sticky nem scrollIntoView). */
290
+ overflow-x: clip;
291
+ }
292
+ @supports not (overflow: clip) {
293
+ html { overflow-x: hidden; }
294
+ }
295
+ }
296
+
297
+ /* ===== PDP layout de produto ===== */
298
+ /* Tipografia: corpo em --font-sans, títulos/display em --font-display — ambos definidos
299
+ no bloco UNBOX-FONTS de app/layout.tsx (trocados por estilo/preset pelo CLI). */
300
+ .store-layout { font-family: var(--font-sans), system-ui, sans-serif; }
301
+ .font-display { font-family: var(--font-display), var(--font-sans), sans-serif; }
302
+
303
+ /* Títulos — usam a fonte de display do estilo */
304
+ h1, h2, h3, h4 {
305
+ font-family: var(--font-heading), var(--font-sans), system-ui, sans-serif;
306
+ }
307
+
308
+ /* Quebra do container restrito do <main> p/ seções full-bleed da PDP */
309
+ .full-bleed {
310
+ width: 100vw;
311
+ position: relative;
312
+ left: 50%;
313
+ right: 50%;
314
+ margin-left: -50vw;
315
+ margin-right: -50vw;
316
+ }
317
+
318
+ @keyframes storePulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
319
+ .animate-store-pulse { animation: storePulse 1.4s infinite; }
320
+
321
+ /* Card de seção — chrome controlado pelos eixos de composição (--card-*). O raio continua
322
+ vindo das classes rounded-* (escala do --radius). Use junto: "store-card rounded-2xl". */
323
+ .store-card {
324
+ background: var(--card-bg, #ffffff);
325
+ border: var(--card-border, 1px solid var(--store-line));
326
+ box-shadow: var(--card-shadow, none);
327
+ }
328
+
329
+ /* Marquee infinito (seção attributes-marquee) — N sets idênticos (N par), -50% fecha o loop.
330
+ A seção define a duração inline proporcional ao número de cópias. */
331
+ @keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
332
+ .animate-marquee { animation: marquee 22s linear infinite; }
333
+ @media (prefers-reduced-motion: reduce) { .animate-marquee { animation: none; } }
334
+
335
+ /* Chama "tremeluzindo" — usada nos ícones de foguinho (urgência/vendas). */
336
+ @keyframes flameFlicker {
337
+ 0%, 100% { transform: scale(1) rotate(0deg); opacity: 1; }
338
+ 25% { transform: scale(1.12) rotate(-4deg); opacity: .85; }
339
+ 50% { transform: scale(.96) rotate(3deg); opacity: 1; }
340
+ 75% { transform: scale(1.08) rotate(-2deg); opacity: .9; }
341
+ }
342
+ .flame { animation: flameFlicker 1.1s ease-in-out infinite; transform-origin: center bottom; }
343
+ @media (prefers-reduced-motion: reduce) { .flame { animation: none; } }
344
+
345
+ /* Checkout: chrome próprio (header simplificado, sem footer) ocupando a tela inteira.
346
+ SSR-safe via :has() — sem JS, sem mismatch de hidratação, sem flash. A página do
347
+ checkout marca seu container raiz com .checkout-root. */
348
+ body:has(.checkout-root) .site-chrome { display: none; }
349
+ body:has(.checkout-root) .site-main { max-width: none; padding: 0; min-height: 0; }
350
+
351
+ /* Páginas full-bleed (home, catálogo, PDP) começam coladas no header E no footer — remove o
352
+ padding do <main> e a margem própria do footer, senão sobra uma faixa da cor de fundo padrão
353
+ entre a seção full-bleed e o header/footer. */
354
+ .site-main:has(.full-bleed) { padding-top: 0; padding-bottom: 0; }
355
+ body:has(.site-main .full-bleed) footer.site-chrome { margin-top: 0; }
356
+
357
+ /* ── Header SOBREPOSTO (variante "imersivo" do chrome) ───────────────────────────────────
358
+ SÓLIDO é o default: a transparência é o ramo OPT-IN do :has() e exige acordo dos DOIS
359
+ lados — o hero marca `.hero-imersivo` E a receita escolheu o header imersivo
360
+ (data-chrome). Assim um header claro nunca fica transparente sobre foto por engano, e
361
+ qualquer página sem esse hero (catálogo, PDP, conta, busca) sai sólida.
362
+ Mesma mecânica do checkout acima: SSR-safe, sem JS, sem flash. Browser sem :has() →
363
+ header sólido, que é o modo seguro. */
364
+ header.site-chrome[data-chrome="imersivo"] { --chrome-h: 60px; }
365
+ @media (min-width: 768px) { header.site-chrome[data-chrome="imersivo"] { --chrome-h: 76px; } }
366
+
367
+ /* 1. layout: o header sai do fluxo (sem position:fixed, que o `overflow-x: clip` do html
368
+ pode clipar) e o hero sobe por baixo dele. */
369
+ body:has(.hero-imersivo) header.site-chrome[data-chrome="imersivo"] {
370
+ margin-bottom: calc(-1 * var(--chrome-h));
371
+ }
372
+ /* 2. pintura: véu na PRÓPRIA cor de chrome — logo e ícones legíveis pelo mesmo motivo que
373
+ já valem sobre o chrome sólido, sem asset novo. .chrome-bar ganha data-solid ao rolar. */
374
+ body:has(.hero-imersivo) header.site-chrome[data-chrome="imersivo"] .chrome-bar:not([data-solid]) {
375
+ background: linear-gradient(to bottom, color-mix(in srgb, var(--store-chrome-bg) 62%, transparent), transparent);
376
+ box-shadow: none;
377
+ }
378
+ @media (prefers-reduced-motion: reduce) { .chrome-bar { transition: none; } }
379
+
380
+ /* Âncoras (#destaques, #comprar) não somem sob o header sticky — vale para as 4 variantes. */
381
+ html { scroll-padding-top: var(--chrome-h, 88px); }
382
+
383
+ /* HTML rico sanitizado (descrição/infos do produto) */
384
+ .richtext {
385
+ @apply text-sm leading-relaxed text-foreground/90;
386
+ }
387
+ .richtext p { @apply my-2; }
388
+ .richtext ul { @apply my-2 list-disc pl-5; }
389
+ .richtext ol { @apply my-2 list-decimal pl-5; }
390
+ .richtext h1, .richtext h2, .richtext h3, .richtext h4 { @apply mt-4 mb-2 font-semibold; }
391
+ .richtext a { @apply text-primary underline underline-offset-2; }
392
+ .richtext img { @apply my-2 max-w-full rounded-md; }
393
+ .richtext table { @apply my-3 w-full border-collapse text-sm; }
394
+ .richtext th, .richtext td { @apply border border-border px-2 py-1 text-left; }
395
+
396
+ /* ── MOTION ──────────────────────────────────────────────────────────────────────────────
397
+ CSS PURO, zero JavaScript: as animações usam timeline de scroll (`animation-timeline:
398
+ view()`), que roda no compositor. Não entra um byte no bundle, não há hidratação e não
399
+ existe estado "invisível esperando script" — por isso não afeta o tempo da primeira tela.
400
+
401
+ Navegador sem suporte (Firefox hoje) simplesmente NÃO anima: o conteúdo aparece normal.
402
+ A degradação é sempre pro lado seguro, então o default de tudo é VISÍVEL.
403
+
404
+ ⚠️ REGRA DURA: nada da primeira dobra anima. O hero nunca leva .reveal — conteúdo que
405
+ entra animado no topo atrasa a percepção de carregamento e mexe no LCP.
406
+
407
+ Intensidade vem do dial MOTION_INTENSITY do briefing, via --motion (0 = desligado). */
408
+ :root {
409
+ /* Declaração numa linha só, indentada: é o formato que o create-unbox-store procura
410
+ (regex ancorada em início de linha) pra escrever o valor do dial. Não colapse. */
411
+ --motion: 1;
412
+ }
413
+
414
+ @supports (animation-timeline: view()) {
415
+ @media (prefers-reduced-motion: no-preference) {
416
+ /* Aparição ao entrar na viewport. Só abaixo da dobra. */
417
+ .reveal {
418
+ animation: unbox-reveal linear both;
419
+ animation-timeline: view();
420
+ animation-range: entry 0% entry 45%;
421
+ }
422
+ /* Deslocamento lento da imagem dentro do próprio frame (parallax contido).
423
+ O pai precisa de overflow-hidden; a imagem usa scale pra não mostrar borda. */
424
+ .parallax-slow {
425
+ animation: unbox-parallax linear both;
426
+ animation-timeline: view();
427
+ animation-range: cover 0% cover 100%;
428
+ will-change: transform;
429
+ }
430
+ }
431
+ }
432
+
433
+ @keyframes unbox-reveal {
434
+ from { opacity: 0; transform: translateY(calc(18px * var(--motion))); }
435
+ to { opacity: 1; transform: none; }
436
+ }
437
+
438
+ @keyframes unbox-parallax {
439
+ from { transform: scale(1.08) translateY(calc(-2.5% * var(--motion))); }
440
+ to { transform: scale(1.08) translateY(calc(2.5% * var(--motion))); }
441
+ }
442
+
443
+ /* ── ACESSIBILIDADE: piso de 16px em campo de formulário no mobile ───────────────────────
444
+ O iOS dá zoom automático ao focar um input com fonte menor que 16px, e a "solução"
445
+ antiga era desligar o pinch-zoom no viewport — que quebra a WCAG 1.4.4 para todo mundo
446
+ para resolver um detalhe de uma plataforma. Esta é a correção certa.
447
+
448
+ !important é deliberado: utilitário do Tailwind (text-[.98rem] e afins) vence seletor de
449
+ elemento, e este é um piso de acessibilidade que não deve ser sobrescrito por engano. Só
450
+ vale abaixo de 768px, onde o problema existe. */
451
+ @media (max-width: 767px) {
452
+ input:not([type="checkbox"]):not([type="radio"]),
453
+ select,
454
+ textarea {
455
+ font-size: 16px !important;
456
+ }
457
+ }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
2
+ <!-- unbox-placeholder: troque este ícone pelo da marca (remova este comentário ao trocar) -->
3
+ <rect width="32" height="32" rx="7" fill="#D4D4D8"/>
4
+ </svg>
@@ -0,0 +1,90 @@
1
+ import type { Metadata, Viewport } from "next";
2
+ import "./globals.css";
3
+ import { Toaster } from "@/components/ui/sonner";
4
+ // EDITOR DE LOJA: o conteúdo publicado pelo lojista entra aqui e os primitivos o aplicam por cima do
5
+ // que está escrito no código. Sem EDITOR_URL no ambiente, `conteudo` é null e a loja renderiza
6
+ // exatamente o código: o editor fica desligado, sem efeito nenhum.
7
+ import { EditableProvider } from "@/lib/editable";
8
+ import { getPublishedContent, presencaNoAmbiente } from "@/lib/editable/server";
9
+ // RASTREIO E MARKETING: a foundation renderiza tudo (GTM da Unbox e do lojista, GA4, Meta Pixel, TikTok,
10
+ // Pinterest, page_view por rota, botão de WhatsApp) numa linha, `<Rastreio>`; este layout não conhece
11
+ // provedor nenhum, e o próximo entra por versão da foundation. Ela lê as MESMAS variáveis de sempre
12
+ // (NEXT_PUBLIC_GTM_ID, NEXT_PUBLIC_GA_ID, NEXT_PUBLIC_META_PIXEL_ID) com a mesma régua (placeholder e
13
+ // formato errado são ausência; Pixel só sem GTM próprio), então nenhuma loja perde rastreio.
14
+ import { Rastreio } from "@/lib/editable/rastreio";
15
+ import { EDITABLE_TOKENS } from "@/lib/editable/tokens";
16
+ import { EDITOR_ORIGIN, STORE_SLUG } from "@/lib/editable/config";
17
+
18
+ // UNBOX-FONTS-BEGIN (bloco reescrito pelo create-unbox-store conforme o estilo escolhido — não renomear os markers)
19
+ import { Geist_Mono, Poppins, Plus_Jakarta_Sans } from "next/font/google";
20
+ const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"] });
21
+ const sans = Plus_Jakarta_Sans({ variable: "--font-sans", subsets: ["latin"], weight: ["400", "500", "600", "700"], display: "swap" });
22
+ const displayFont = Poppins({ variable: "--font-display", subsets: ["latin"], weight: ["500", "600", "700", "800"], display: "swap" });
23
+ // UNBOX-FONTS-END
24
+
25
+ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000";
26
+
27
+ // ═══════════════════════════════════════════════════════════════════════════
28
+ // GTM CENTRAL DA UNBOX — OBRIGATÓRIO EM TODA LOJA. NÃO REMOVA NEM TROQUE O ID.
29
+ // É o container central da Unbox para captura de dados da plataforma (parte do
30
+ // contrato da loja). Tags próprias da marca NÃO entram aqui: o lojista configura
31
+ // os IDs dele (GTM próprio, GA4, Meta Pixel, TikTok, Pinterest, WhatsApp) na aba
32
+ // Apps do painel do editor, ou o cadastro da loja os traz do ambiente
33
+ // (NEXT_PUBLIC_GTM_ID / NEXT_PUBLIC_GA_ID / NEXT_PUBLIC_META_PIXEL_ID …), e a
34
+ // foundation injeta (`<Rastreio>`, lib/editable/rastreio.tsx), nunca
35
+ // editando/substituindo este ID. Este literal vai para o `<Rastreio>` como o
36
+ // contêiner contratual, e o prebuild (scripts/check-unbox-brand.mjs) cobra que
37
+ // ele continue aqui.
38
+ // ═══════════════════════════════════════════════════════════════════════════
39
+ const UNBOX_GTM_ID = "GTM-PZLT336";
40
+
41
+ export const metadata: Metadata = {
42
+ metadataBase: new URL(siteUrl),
43
+ title: {
44
+ default: "Minha Loja · Compre Online",
45
+ template: "%s · Minha Loja",
46
+ },
47
+ // ⚠ Descrição PROVISÓRIA: não diz o que a loja vende. O briefing troca por uma frase com o
48
+ // produto e o público — é o que busca e resposta de IA extraem. O build avisa enquanto for esta.
49
+ // Sem description de fábrica, de propósito: "Produtos de qualidade entregues na sua casa"
50
+ // PARECE preenchido e ninguém revisa. Vazio o build avisa e o Google gera a partir do
51
+ // conteúdo, que é melhor que uma frase que não diz o que a loja vende. O briefing escreve.
52
+ description: process.env.NEXT_PUBLIC_SITE_DESCRIPTION || undefined,
53
+ // openGraph.images vem de app/opengraph-image.tsx (gerada com o nome da loja nas cores da
54
+ // marca): todo compartilhamento sai com imagem, mesmo antes de a marca mandar uma foto.
55
+ openGraph: { type: "website", locale: "pt_BR", siteName: "Minha Loja" },
56
+ twitter: { card: "summary_large_image" },
57
+ // Favicon: NÃO declarar `icons` aqui — app/icon.svg e app/apple-icon.svg (convenção do
58
+ // App Router) já geram os <link> corretos. Apontar pro logo horizontal deixa o favicon
59
+ // ilegível/invisível na aba. Personalize substituindo esses dois arquivos.
60
+ };
61
+
62
+ // NÃO adicione maximumScale/userScalable aqui. Bloquear o pinch-zoom viola a WCAG 1.4.4
63
+ // (Resize Text) e prejudica de verdade quem depende de ampliar a tela — e toda loja gerada
64
+ // herdava isso. O motivo original era o zoom automático do iOS ao focar um input, que
65
+ // acontece quando o campo tem fonte MENOR que 16px; a correção certa é o piso de 16px nos
66
+ // controles de formulário no mobile, que está em app/globals.css.
67
+ export const viewport: Viewport = {
68
+ width: "device-width",
69
+ initialScale: 1,
70
+ };
71
+
72
+ export default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
73
+ const conteudo = await getPublishedContent();
74
+ return (
75
+ <html lang="pt-BR">
76
+ <body className={`${sans.variable} ${geistMono.variable} ${displayFont.variable} antialiased`}>
77
+ {/* Header/rodapé/nav da loja NÃO ficam aqui: moram em app/(loja)/layout.tsx (route
78
+ group). Página criada fora de (loja) — acesso, erro, landing — nasce sem chrome. */}
79
+ <EditableProvider doc={conteudo} shop={STORE_SLUG} tokens={EDITABLE_TOKENS} editorOrigin={EDITOR_ORIGIN || undefined} apps={presencaNoAmbiente(process.env, { unboxGtmId: UNBOX_GTM_ID })}>
80
+ {children}
81
+ </EditableProvider>
82
+ <Toaster position="top-center" />
83
+ {/* rastreio e marketing: o que o lojista publicou na aba Apps vence o ambiente, provedor a provedor,
84
+ e cada provedor dispara UMA vez; o contêiner da Unbox entra sempre. Só muda quando ele PUBLICA.
85
+ Nada de script de GA/Pixel/GTM escrito à mão neste arquivo: seria o mesmo provedor duas vezes. */}
86
+ <Rastreio doc={conteudo} ambiente={process.env} unboxGtmId={UNBOX_GTM_ID} />
87
+ </body>
88
+ </html>
89
+ );
90
+ }
@@ -0,0 +1,43 @@
1
+ // /llms.txt — apresenta a loja a modelos de IA e agentes.
2
+ //
3
+ // É uma ROTA, não um arquivo em public/, pelo mesmo motivo do resto do template: o que a loja
4
+ // afirma vem do catálogo real. Arquivo estático nasce desatualizado no dia em que um produto sai
5
+ // de linha, e o formato estático já foi ao ar com "TODO: preencher" numa loja de produção,
6
+ // servindo isso justamente aos crawlers que o robots.txt convida.
7
+ //
8
+ // O formato mora em lib/llms-txt.ts (função pura, testável). Aqui só buscamos os dados.
9
+ import { loadAllCatalogItems } from "@/lib/dataloader";
10
+ import { getTopTags } from "@/lib/queries";
11
+ import { buildTagMap, mapCatalogItems } from "@/lib/catalog-map";
12
+ import { montarLlmsTxt } from "@/lib/llms-txt";
13
+ import { FREE_SHIPPING_THRESHOLD, PIX_DISCOUNT_PCT } from "@/lib/store-config";
14
+
15
+ export const revalidate = 3600;
16
+
17
+ export async function GET() {
18
+ // Falha de API não pode derrubar o arquivo: sem catálogo ele sai com as rotas e as políticas,
19
+ // que continuam verdadeiras.
20
+ const [itens, tags] = await Promise.all([
21
+ loadAllCatalogItems().catch(() => [] as any[]),
22
+ getTopTags().catch(() => [] as any[]),
23
+ ]);
24
+ const produtos = mapCatalogItems(itens as any[], buildTagMap(tags as any[]));
25
+
26
+ const texto = montarLlmsTxt(produtos, {
27
+ siteName: process.env.NEXT_PUBLIC_SITE_NAME || "Minha Loja",
28
+ siteUrl: process.env.NEXT_PUBLIC_SITE_URL,
29
+ descricao: process.env.NEXT_PUBLIC_SITE_DESCRIPTION,
30
+ categorias: (tags as any[])
31
+ .filter((t) => t?.slug && t.isVisible !== false)
32
+ .map((t) => ({ nome: t.displayTitle || t.name, slug: t.slug })),
33
+ pixDescontoPct: PIX_DISCOUNT_PCT,
34
+ freteGratisAcimaDe: FREE_SHIPPING_THRESHOLD,
35
+ });
36
+
37
+ return new Response(texto, {
38
+ headers: {
39
+ "content-type": "text/plain; charset=utf-8",
40
+ "cache-control": "public, max-age=3600, s-maxage=3600, stale-while-revalidate=86400",
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,21 @@
1
+ import type { MetadataRoute } from "next";
2
+
3
+ export default function manifest(): MetadataRoute.Manifest {
4
+ return {
5
+ name: process.env.NEXT_PUBLIC_SITE_NAME ?? "Minha Loja",
6
+ short_name: process.env.NEXT_PUBLIC_SITE_NAME ?? "Loja",
7
+ description: "Loja oficial. Compre com segurança e receba em casa.",
8
+ start_url: "/",
9
+ display: "standalone",
10
+ background_color: "#ffffff",
11
+ // theme_color: usar o --store-chrome-bg da marca (preencher ao configurar cores)
12
+ theme_color: "#18181B",
13
+ orientation: "portrait",
14
+ icons: [
15
+ // SVG funciona na maioria dos browsers modernos
16
+ { src: "/brand/logo.svg", sizes: "any", type: "image/svg+xml" },
17
+ // TODO: criar /public/icon-192.png e /public/icon-512.png com a marca real
18
+ // (PNG é necessário para Android e iOS antigos; usar squoosh.app ou similar)
19
+ ],
20
+ };
21
+ }
@@ -0,0 +1,18 @@
1
+ import Link from "next/link";
2
+ import { Button } from "@/components/ui/button";
3
+
4
+ export default function NotFound() {
5
+ return (
6
+ <div className="flex flex-col items-center justify-center py-24 text-center">
7
+ <p className="text-5xl font-bold text-primary">404</p>
8
+ <h1 className="mt-3 text-xl font-semibold">Página não encontrada</h1>
9
+ <p className="mt-1 max-w-sm text-sm text-muted-foreground">
10
+ O produto pode ter saído do catálogo ou o endereço está incorreto.
11
+ </p>
12
+ <div className="mt-6 flex gap-2">
13
+ <Button nativeButton={false} render={<Link href="/" />}>Voltar ao início</Button>
14
+ <Button variant="outline" nativeButton={false} render={<Link href="/produtos" />}>Ver produtos</Button>
15
+ </div>
16
+ </div>
17
+ );
18
+ }