@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,57 @@
1
+ // Barra de anúncio (desktop) — fica ACIMA do header e rola pra fora.
2
+ // Compartilhada por todas as variantes de header: o conteúdo é comercial (frete/brinde),
3
+ // não estrutura de marca, e a regra de honestidade tem que valer igual em todas.
4
+ //
5
+ // Só renderiza quando há promessa REAL configurada em lib/store-config (defaults zerados =
6
+ // a barra inteira some). A casca não a renderiza no modo "sobreposto" (header transparente).
7
+ //
8
+ // EDITOR: a faixa é uma seção do chrome (`chrome.faixa`, fixa) e cada aviso é um ITEM de lista
9
+ // aninhada, com id de PAPEL (`frete`, `brinde`), não posicional: os dois só existem quando a
10
+ // configuração comercial existe, e um id posicional mudaria de dono conforme o que está ligado.
11
+ // O lojista troca o texto, reordena, oculta e duplica pelo painel ("+ Adicionar" copia o último
12
+ // aviso visível: é assim que ele acrescenta um aviso próprio). O VALOR do frete (R$X) fica fora do
13
+ // primitivo: é configuração da loja (lib/store-config), não copy (README do editor, §8). A cópia
14
+ // do aviso no celular mora em components/chrome/header-bar-mobile.tsx, dentro do cabeçalho.
15
+ import { Truck } from "@phosphor-icons/react/dist/ssr";
16
+ import { FREE_SHIPPING_THRESHOLD, GIFT_TIERS } from "@/lib/store-config";
17
+ // Exports NOMEADOS: server component (ver components/site-header.tsx).
18
+ import { EditableIcon, EditableSection, EditableSections, EditableText } from "@/lib/editable";
19
+
20
+ export function AnnounceBar() {
21
+ if (FREE_SHIPPING_THRESHOLD == null && GIFT_TIERS.length === 0) return null;
22
+
23
+ return (
24
+ <EditableSection id="faixa" kind="faixa-de-anuncio" container="chrome" fixed label="Faixa de anúncio do topo">
25
+ <div className="store-layout site-chrome hidden items-stretch bg-[var(--store-chrome-bg,#18181B)] text-[13px] font-semibold text-[var(--store-chrome-text,#ffffff)] md:flex">
26
+ <div className="flex flex-1 items-center justify-center gap-2 px-4 py-[9px]">
27
+ {/* a className era do ícone; vai para o invólucro do Icon (regra do `Editable.Icon`) */}
28
+ <EditableIcon path="icone" label="Ícone da faixa" size={16} className="text-base text-[var(--store-cta,#D97706)]">
29
+ <Truck weight="bold" />
30
+ </EditableIcon>
31
+ {/* o invólucro de cada item é display:contents: quem continua sendo filho do flex é o <span> do aviso */}
32
+ <EditableSections nested>
33
+ {FREE_SHIPPING_THRESHOLD != null && (
34
+ <EditableSection item id="frete" label="Aviso de frete grátis">
35
+ <span className="text-[var(--store-surface-2)]">
36
+ <EditableText path="texto" fallback="Frete Grátis acima de" label="Texto do aviso de frete" />{" "}
37
+ <span className="text-[var(--store-chrome-text,#ffffff)]">R${FREE_SHIPPING_THRESHOLD}</span>
38
+ </span>
39
+ </EditableSection>
40
+ )}
41
+ {GIFT_TIERS.length > 0 && (
42
+ <EditableSection item id="brinde" label="Aviso de brinde">
43
+ <EditableText
44
+ path="texto"
45
+ fallback={`${FREE_SHIPPING_THRESHOLD != null ? "· e ganhe" : "Ganhe"} brindes exclusivos`}
46
+ label="Texto do aviso de brinde"
47
+ className="text-[var(--store-chrome-muted)]"
48
+ />
49
+ </EditableSection>
50
+ )}
51
+ </EditableSections>
52
+ </div>
53
+ {/* TODO: adicionar badge de cupom/promoção da marca */}
54
+ </div>
55
+ </EditableSection>
56
+ );
57
+ }
@@ -0,0 +1,13 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // RECEITA DO CHROME — qual header e qual rodapé esta loja usa.
3
+ // Escrita pelo create-unbox-store conforme o ESTILO escolhido no setup; editada
4
+ // pelo agente de branding no briefing (é AQUI que o chrome muda — não reescreva
5
+ // o JSX de uma variante; catálogo em agents/PADROES.md).
6
+ // `npm run typecheck` acusa header/rodapé inexistente.
7
+ // ═══════════════════════════════════════════════════════════════════════════
8
+ import type { ChromeRecipe } from "@/components/chrome/registry";
9
+
10
+ export const chromeRecipe: ChromeRecipe = {
11
+ header: "classico",
12
+ footer: "colunas",
13
+ };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ // O BOTÃO da captura de e-mail do rodapé "conversão", isolado num componente de CLIENTE.
4
+ //
5
+ // POR QUE ELE EXISTE, e por que não pode voltar para dentro do conversao.tsx:
6
+ // `EditableSlot` é client e recebe os filhos como FUNÇÃO (render-prop) para devolver o mesmo
7
+ // elemento sem invólucro. O rodapé é Server Component, e função não atravessa a fronteira
8
+ // servidor→cliente: o build quebra com "Functions cannot be passed directly to Client Components".
9
+ // Como o rodapé vive no layout de `(loja)`, o erro derrubava o prerender de TODA página, não só do
10
+ // rodapé — e só no estilo promocional, que é o único preset que usa este rodapé por padrão.
11
+ import { EditableSlot } from "@/lib/editable";
12
+
13
+ export function CapturaBotao({ label = "QUERO" }: { label?: string }) {
14
+ return (
15
+ <EditableSlot path="captura.botao" type="text" fallback={label} label="Texto do botão da captura">
16
+ {(v, attrs, ref) => (
17
+ <button
18
+ ref={ref as React.Ref<HTMLButtonElement>}
19
+ {...attrs}
20
+ type="submit"
21
+ className="font-display h-11 shrink-0 rounded-full bg-[var(--store-cta,#D97706)] px-6 text-[14px] font-extrabold tracking-[0.5px] text-[var(--store-cta-fg,#1C1207)] transition-opacity hover:opacity-90"
22
+ >
23
+ {v}
24
+ </button>
25
+ )}
26
+ </EditableSlot>
27
+ );
28
+ }
@@ -0,0 +1,110 @@
1
+ // RODAPÉ "colunas" — trust strip + 4 colunas (marca, catálogo, ajuda, pagamento).
2
+ // É o rodapé de loja completa: cobre navegação secundária e reforço de confiança.
3
+ //
4
+ // A barra inferior (© + selo Powered by Unbox) NÃO está aqui: ela é da casca
5
+ // (components/site-footer.tsx), porque é contrato da plataforma e não pode variar.
6
+ //
7
+ // EDITOR: o escopo (`chrome.footer`) vem da casca. Os selos são uma LISTA editável (`selo-N`, com
8
+ // ícone e texto: o lojista troca, reordena, oculta e duplica). Dois tipos de link nas colunas, pela
9
+ // regra §8 do README do editor. LINK DE MARCA (tem `chave`): o rótulo é uma frase da marca e o
10
+ // lojista o batiza ("Todos os produtos", "Kits & Combos"): rótulo e destino editáveis em
11
+ // `<coluna>.<chave>`. LINK DE INTERFACE (sem `chave`): nomeia uma mecânica da loja ou uma página
12
+ // legal ("Buscar", "Minha conta", "Termos"): fica no código. O título de cada coluna é editável.
13
+ import Link from "next/link";
14
+ import { Truck, ShieldCheck } from "@phosphor-icons/react/dist/ssr";
15
+ import { PaymentChips } from "@/components/product/pdp/payment-chips";
16
+ // Exports NOMEADOS: server component (ver components/site-footer.tsx).
17
+ import { EditableIcon, EditableImg, EditableLink, EditableSection, EditableSections, EditableText } from "@/lib/editable";
18
+ import type { ChromeVariantProps } from "../registry";
19
+
20
+ // Âncoras de confiança: só o que a foundation PODE afirmar (SSL e o prazo do CDC). Prazo de
21
+ // entrega, garantia e atendimento são promessas do lojista: entram pelo briefing, com lastro.
22
+ const TRUST = [
23
+ { icon: ShieldCheck, text: "Compra segura (SSL)" },
24
+ { icon: Truck, text: "Troca em 7 dias · CDC" },
25
+ ];
26
+
27
+ export function FooterColunas({ data }: ChromeVariantProps) {
28
+ const { shopName } = data;
29
+
30
+ return (
31
+ <>
32
+ {/* trust — lista editável: o invólucro do item é display:contents, então quem continua
33
+ sendo a célula do grid é o próprio <div> do selo */}
34
+ <div className="mx-auto grid max-w-[var(--container-max,1240px)] grid-cols-2 gap-x-6 border-b border-[var(--store-chrome-line,rgba(255,255,255,.1))] px-6 py-8 ">
35
+ <EditableSections nested>
36
+ {TRUST.map((t, i) => (
37
+ <EditableSection key={t.text} item id={`selo-${i + 1}`} label={`Selo de confiança ${i + 1}`}>
38
+ <div className="flex items-center gap-3.5 py-1.5">
39
+ {/* a className fica no ícone (é ela que dá o tamanho e a cor); o `size` é o
40
+ tamanho da imagem que o lojista subir no lugar */}
41
+ <EditableIcon path="icone" label="Ícone do selo" size={28} className="text-[28px] text-[var(--store-cta,#D97706)]">
42
+ <t.icon />
43
+ </EditableIcon>
44
+ <EditableText as="div" path="texto" fallback={t.text} label="Texto do selo" className="text-[13px] leading-snug text-[var(--store-chrome-muted)]" />
45
+ </div>
46
+ </EditableSection>
47
+ ))}
48
+ </EditableSections>
49
+ </div>
50
+
51
+ {/* colunas */}
52
+ <div className="mx-auto grid max-w-[var(--container-max,1240px)] gap-8 px-6 py-9 sm:grid-cols-2 lg:grid-cols-[1.4fr_1fr_1fr_1fr]">
53
+ <div>
54
+ {/* logo-chrome.svg = versão do logo legível sobre o chrome (o CLI troca conforme o
55
+ estilo: chrome escuro usa o logo branco, chrome claro usa o logo normal).
56
+ <img> puro (EditableImg), não next/image: SVG de poucos KB em toda página. */}
57
+ <EditableImg path="logo" fallback={{ src: "/brand/logo-chrome.svg", alt: shopName }} label="Logo do rodapé" className="mb-3.5 h-16 w-auto" />
58
+ {/* TODO: tagline da marca (EditableText path="frase", como a loja piloto) */}
59
+ </div>
60
+ <FooterCol path="coluna-catalogo" title="Catálogo" links={[
61
+ { chave: "todos", label: "Todos os produtos", href: "/produtos" },
62
+ { label: "Buscar", href: "/busca" },
63
+ { chave: "combos", label: "Kits & Combos", href: "/produtos" },
64
+ { chave: "ofertas", label: "Ofertas", href: "/produtos" },
65
+ ]} />
66
+ <FooterCol path="coluna-ajuda" title="Ajuda" links={[
67
+ { label: "Acompanhar pedido", href: "/conta/entrar" },
68
+ { label: "Trocas e devoluções", href: "/devolucoes" },
69
+ { label: "Termos", href: "/termos" },
70
+ { label: "Minha conta", href: "/conta" },
71
+ ]} />
72
+ <div>
73
+ <EditableText as="div" path="pagamento.titulo" fallback="Pague com" label="Título das formas de pagamento" className="font-display mb-3 text-sm font-bold" />
74
+ {/* As bandeiras são selo de terceiro (interface de pagamento), não imagem da marca: não viram
75
+ primitivo. O `data-editor-ignore` mora no próprio componente (payment-chips.tsx). */}
76
+ <PaymentChips />
77
+ </div>
78
+ </div>
79
+ </>
80
+ );
81
+ }
82
+
83
+ type LinkDoRodape = {
84
+ /** caminho do link no editor (`<coluna>.<chave>`); sem ele o link é de interface e fica no código */
85
+ chave?: string;
86
+ label: string;
87
+ href: string;
88
+ };
89
+
90
+ const CLASSE_LINK = "no-underline hover:text-[var(--store-chrome-text,#ffffff)]";
91
+
92
+ /** `path` = prefixo dos caminhos no editor; o título da coluna e os links de marca (com `chave`) são editáveis. */
93
+ function FooterCol({ path, title, links }: { path: string; title: string; links: LinkDoRodape[] }) {
94
+ return (
95
+ <div>
96
+ <EditableText as="div" path={`${path}.titulo`} fallback={title} label={`Título da coluna: ${title}`} className="font-display mb-3 text-sm font-bold" />
97
+ <div className="flex flex-col gap-2.5 text-[13px] text-[var(--store-chrome-muted)]">
98
+ {links.map((l) =>
99
+ l.chave ? (
100
+ <EditableLink key={l.label} path={`${path}.${l.chave}`} fallback={{ href: l.href, label: l.label }} label={`Link: ${l.label}`} className={CLASSE_LINK}>
101
+ {l.label}
102
+ </EditableLink>
103
+ ) : (
104
+ <Link key={l.label} href={l.href} className={CLASSE_LINK} data-editor-ignore="">{l.label}</Link>
105
+ ),
106
+ )}
107
+ </div>
108
+ </div>
109
+ );
110
+ }
@@ -0,0 +1,90 @@
1
+ // RODAPÉ "conversão" — captura de e-mail em destaque no topo, links enxutos e meios de
2
+ // pagamento com peso. O rodapé como última chance de conversão, não como mapa do site.
3
+ //
4
+ // Quando usar: loja promocional/campanha, onde a lista de e-mail vale mais que a navegação.
5
+ // Quando NÃO usar: catálogo grande que precisa de navegação secundária (veja "colunas").
6
+ //
7
+ // EDITOR: o escopo (`chrome.footer`) vem da casca. A captura tem título, texto e o texto do botão
8
+ // editáveis (`captura.*`); campo e placeholder do formulário são INTERFACE e ficam fora (README
9
+ // do editor, §8). Nas colunas, link de marca (com `chave`) é editável em `<coluna>.<chave>`; link
10
+ // de interface fica no código. O título de cada coluna é editável.
11
+ import Link from "next/link";
12
+ import { PaymentChips } from "@/components/product/pdp/payment-chips";
13
+ // Exports NOMEADOS: server component (ver components/site-footer.tsx).
14
+ import { EditableImg, EditableLink, EditableText } from "@/lib/editable";
15
+ import { CapturaBotao } from "./captura-botao";
16
+ import { NEWSLETTER_ACTION } from "@/lib/newsletter";
17
+ import type { ChromeVariantProps } from "../registry";
18
+
19
+ export function FooterConversao({ data }: ChromeVariantProps) {
20
+ const { shopName } = data;
21
+
22
+ return (
23
+ <>
24
+ {/* Captura de e-mail: só existe com destino real (NEXT_PUBLIC_NEWSLETTER_ACTION). Sem ele,
25
+ a faixa inteira some — inclusive a borda, senão fica uma tira vazia no topo do rodapé. */}
26
+ {NEWSLETTER_ACTION && (
27
+ <div className="border-b border-[var(--store-chrome-line,rgba(255,255,255,.1))]">
28
+ <div className="mx-auto flex max-w-[var(--container-max,1240px)] flex-wrap items-center gap-6 px-6 py-10">
29
+ <div className="min-w-[260px] flex-1">
30
+ <EditableText as="div" path="captura.titulo" fallback="Receba as novidades antes" label="Título da captura de e-mail" className="font-display text-[24px] font-extrabold leading-[1.15] text-[var(--store-chrome-text,#ffffff)]" />
31
+ <EditableText as="p" path="captura.texto" fallback="Ofertas e lançamentos direto no seu e-mail." label="Texto da captura de e-mail" className="mt-1.5 text-[14px] text-[var(--store-chrome-muted)]" />
32
+ </div>
33
+ <form method="post" action={NEWSLETTER_ACTION} className="flex w-full max-w-[440px] items-center gap-1.5 rounded-full bg-white p-1.5">
34
+ <input type="email" name="email" required placeholder="Seu melhor e-mail" aria-label="Seu e-mail" className="h-11 min-w-0 flex-1 rounded-full bg-transparent px-4 text-sm text-[var(--store-ink)] outline-none" />
35
+ <CapturaBotao />
36
+ </form>
37
+ </div>
38
+ </div>
39
+ )}
40
+
41
+ {/* links + pagamento */}
42
+ <div className="mx-auto grid max-w-[var(--container-max,1240px)] gap-8 px-6 py-9 sm:grid-cols-2 lg:grid-cols-[1fr_1fr_1.2fr]">
43
+ <FooterCol path="coluna-comprar" title="Comprar" links={[
44
+ { chave: "todos", label: "Todos os produtos", href: "/produtos" },
45
+ { chave: "ofertas", label: "Ofertas", href: "/produtos" },
46
+ { label: "Buscar", href: "/busca" },
47
+ ]} />
48
+ <FooterCol path="coluna-ajuda" title="Ajuda" links={[
49
+ { label: "Acompanhar pedido", href: "/conta/entrar" },
50
+ { label: "Trocas e devoluções", href: "/devolucoes" },
51
+ { label: "Termos", href: "/termos" },
52
+ ]} />
53
+ <div>
54
+ <EditableText as="div" path="pagamento.titulo" fallback="Pague com" label="Título das formas de pagamento" className="font-display mb-3 text-sm font-bold" />
55
+ <PaymentChips />
56
+ {/* <img> puro (EditableImg), não next/image: logo do chrome é SVG de poucos KB em toda página. */}
57
+ <EditableImg path="logo" fallback={{ src: "/brand/logo-chrome.svg", alt: shopName }} label="Logo do rodapé" className="mt-6 h-11 w-auto opacity-90" />
58
+ </div>
59
+ </div>
60
+ </>
61
+ );
62
+ }
63
+
64
+ type LinkDoRodape = {
65
+ /** caminho do link no editor (`<coluna>.<chave>`); sem ele o link é de interface e fica no código */
66
+ chave?: string;
67
+ label: string;
68
+ href: string;
69
+ };
70
+
71
+ const CLASSE_LINK = "no-underline hover:text-[var(--store-chrome-text,#ffffff)]";
72
+
73
+ function FooterCol({ path, title, links }: { path: string; title: string; links: LinkDoRodape[] }) {
74
+ return (
75
+ <div>
76
+ <EditableText as="div" path={`${path}.titulo`} fallback={title} label={`Título da coluna: ${title}`} className="font-display mb-3 text-sm font-bold" />
77
+ <div className="flex flex-col gap-2.5 text-[13px] text-[var(--store-chrome-muted)]">
78
+ {links.map((l) =>
79
+ l.chave ? (
80
+ <EditableLink key={l.label} path={`${path}.${l.chave}`} fallback={{ href: l.href, label: l.label }} label={`Link: ${l.label}`} className={CLASSE_LINK}>
81
+ {l.label}
82
+ </EditableLink>
83
+ ) : (
84
+ <Link key={l.label} href={l.href} className={CLASSE_LINK} data-editor-ignore="">{l.label}</Link>
85
+ ),
86
+ )}
87
+ </div>
88
+ </div>
89
+ );
90
+ }
@@ -0,0 +1,98 @@
1
+ // RODAPÉ "editorial" — um parágrafo de marca (manifesto curto) ocupando a coluna larga,
2
+ // links em colunas discretas e redes sociais. Fecha a página com voz, não com selos.
3
+ //
4
+ // Quando usar: marca com história pra contar, clube/assinatura, tom de revista.
5
+ // Quando NÃO usar: loja de conversão pura (veja "conversao").
6
+ //
7
+ // EDITOR: o escopo (`chrome.footer`) vem da casca. Logo e manifesto são caminhos da seção; as
8
+ // redes são uma LISTA editável (`redes.rede-N`, só o nome: a foundation não sabe a URL de rede
9
+ // nenhuma, e o briefing é quem a traz). Nas colunas, link de marca (com `chave`) é editável em
10
+ // `<coluna>.<chave>`; link de interface fica no código. O título de cada coluna é editável.
11
+ import Link from "next/link";
12
+ import { PaymentChips } from "@/components/product/pdp/payment-chips";
13
+ // Exports NOMEADOS: server component (ver components/site-footer.tsx).
14
+ import { EditableImg, EditableLink, EditableScope, EditableSection, EditableSections, EditableText } from "@/lib/editable";
15
+ import type { ChromeVariantProps } from "../registry";
16
+
17
+ // TODO: trocar pelas redes REAIS da marca (do briefing/rodapé do site atual).
18
+ const REDES = ["Instagram", "YouTube", "Newsletter"];
19
+
20
+ export function FooterEditorial({ data }: ChromeVariantProps) {
21
+ const { shopName } = data;
22
+
23
+ return (
24
+ <div className="mx-auto grid max-w-[var(--container-max,1240px)] gap-10 px-6 py-12 lg:grid-cols-[1.6fr_1fr_1fr]">
25
+ <div className="max-w-[420px]">
26
+ {/* <img> puro (EditableImg), não next/image: logo do chrome é SVG de poucos KB em toda página. */}
27
+ <EditableImg path="logo" fallback={{ src: "/brand/logo-chrome.svg", alt: shopName }} label="Logo do rodapé" className="h-14 w-auto" />
28
+ {/* TODO: troque pelo manifesto REAL da marca (1 parágrafo, voz do briefing). */}
29
+ <EditableText
30
+ as="p"
31
+ path="manifesto"
32
+ fallback="Uma curadoria feita com calma, pensada pra durar. Cada item aqui passou por escolha, teste e uso real antes de chegar até você."
33
+ label="Manifesto da marca"
34
+ multiline
35
+ className="mt-5 text-[14px] leading-[1.65] text-[var(--store-chrome-muted)]"
36
+ />
37
+ <div className="mt-6 flex flex-wrap gap-4 text-[13px] text-[var(--store-chrome-muted)]">
38
+ {/* lista editável: o invólucro do item é display:contents, o <span> segue sendo o filho do flex */}
39
+ <EditableScope path="redes">
40
+ <EditableSections nested>
41
+ {REDES.map((r, i) => (
42
+ <EditableSection key={r} item id={`rede-${i + 1}`} label={`Rede ${i + 1}`}>
43
+ <EditableText path="texto" fallback={r} label="Nome da rede" />
44
+ </EditableSection>
45
+ ))}
46
+ </EditableSections>
47
+ </EditableScope>
48
+ </div>
49
+ </div>
50
+
51
+ <FooterCol path="coluna-loja" title="Loja" links={[
52
+ { chave: "todos", label: "Todos os produtos", href: "/produtos" },
53
+ { label: "Buscar", href: "/busca" },
54
+ { label: "Minha conta", href: "/conta" },
55
+ ]} />
56
+
57
+ <div>
58
+ <FooterCol path="coluna-institucional" title="Institucional" links={[
59
+ { label: "Trocas e devoluções", href: "/devolucoes" },
60
+ { label: "Termos", href: "/termos" },
61
+ { label: "Privacidade", href: "/privacidade" },
62
+ ]} />
63
+ <div className="mt-7">
64
+ <EditableText as="div" path="pagamento.titulo" fallback="Pague com" label="Título das formas de pagamento" className="font-display mb-3 text-sm font-bold" />
65
+ <PaymentChips />
66
+ </div>
67
+ </div>
68
+ </div>
69
+ );
70
+ }
71
+
72
+ type LinkDoRodape = {
73
+ /** caminho do link no editor (`<coluna>.<chave>`); sem ele o link é de interface e fica no código */
74
+ chave?: string;
75
+ label: string;
76
+ href: string;
77
+ };
78
+
79
+ const CLASSE_LINK = "no-underline hover:text-[var(--store-chrome-text,#ffffff)]";
80
+
81
+ function FooterCol({ path, title, links }: { path: string; title: string; links: LinkDoRodape[] }) {
82
+ return (
83
+ <div>
84
+ <EditableText as="div" path={`${path}.titulo`} fallback={title} label={`Título da coluna: ${title}`} className="font-display mb-3 text-[12px] font-bold uppercase tracking-[1.4px]" />
85
+ <div className="flex flex-col gap-2.5 text-[13px] text-[var(--store-chrome-muted)]">
86
+ {links.map((l) =>
87
+ l.chave ? (
88
+ <EditableLink key={l.label} path={`${path}.${l.chave}`} fallback={{ href: l.href, label: l.label }} label={`Link: ${l.label}`} className={CLASSE_LINK}>
89
+ {l.label}
90
+ </EditableLink>
91
+ ) : (
92
+ <Link key={l.label} href={l.href} className={CLASSE_LINK} data-editor-ignore="">{l.label}</Link>
93
+ ),
94
+ )}
95
+ </div>
96
+ </div>
97
+ );
98
+ }
@@ -0,0 +1,55 @@
1
+ // RODAPÉ "minimal" — logo pequeno, uma linha de links e pagamento discreto. Muito ar.
2
+ // O rodapé some da atenção: quem chegou até aqui já decidiu, e a marca não quer ruído.
3
+ //
4
+ // Quando usar: catálogo curto, estética premium/minimal, poucas páginas institucionais.
5
+ // Quando NÃO usar: loja com muita navegação secundária ou muita informação legal (veja
6
+ // "colunas"). Os links legais continuam aqui — some o peso visual, nunca o acesso.
7
+ //
8
+ // EDITOR: o escopo (`chrome.footer`) vem da casca. O logo é caminho da seção; na linha de links,
9
+ // só "Todos os produtos" é link de MARCA (editável em `links.todos`); os outros nomeiam mecânica
10
+ // da loja ou página legal e ficam no código (README do editor, §8).
11
+ import Link from "next/link";
12
+ import { PaymentChips } from "@/components/product/pdp/payment-chips";
13
+ // Exports NOMEADOS: server component (ver components/site-footer.tsx).
14
+ import { EditableImg, EditableLink } from "@/lib/editable";
15
+ import type { ChromeVariantProps } from "../registry";
16
+
17
+ const LINKS: { chave?: string; label: string; href: string }[] = [
18
+ { chave: "todos", label: "Todos os produtos", href: "/produtos" },
19
+ { label: "Minha conta", href: "/conta" },
20
+ { label: "Acompanhar pedido", href: "/conta/entrar" },
21
+ { label: "Trocas e devoluções", href: "/devolucoes" },
22
+ { label: "Termos", href: "/termos" },
23
+ { label: "Privacidade", href: "/privacidade" },
24
+ ];
25
+
26
+ const CLASSE_LINK = "no-underline transition-colors hover:text-[var(--store-chrome-text,#ffffff)]";
27
+
28
+ export function FooterMinimal({ data }: ChromeVariantProps) {
29
+ const { shopName } = data;
30
+
31
+ return (
32
+ <div className="mx-auto flex max-w-[var(--container-max,1240px)] flex-col items-center gap-7 px-6 py-14 text-center">
33
+ <Link href="/" aria-label={shopName} className="flex">
34
+ {/* <img> puro (EditableImg), não next/image: logo do chrome é SVG de poucos KB em toda página. */}
35
+ <EditableImg path="logo" fallback={{ src: "/brand/logo-chrome.svg", alt: shopName }} label="Logo do rodapé" className="h-12 w-auto" />
36
+ </Link>
37
+
38
+ <nav className="flex flex-wrap items-center justify-center gap-x-7 gap-y-3 text-[13px] text-[var(--store-chrome-muted)]" aria-label="Rodapé">
39
+ {LINKS.map((l) =>
40
+ l.chave ? (
41
+ <EditableLink key={l.label} path={`links.${l.chave}`} fallback={{ href: l.href, label: l.label }} label={`Link: ${l.label}`} className={CLASSE_LINK}>
42
+ {l.label}
43
+ </EditableLink>
44
+ ) : (
45
+ <Link key={l.label} href={l.href} className={CLASSE_LINK} data-editor-ignore="">{l.label}</Link>
46
+ ),
47
+ )}
48
+ </nav>
49
+
50
+ <div className="opacity-80">
51
+ <PaymentChips />
52
+ </div>
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,64 @@
1
+ // Barra do header no MOBILE — compartilhada pelas variantes de header.
2
+ //
3
+ // Por que compartilhada: no celular o espaço é o mesmo pra todo mundo (hambúrguer · logo ·
4
+ // busca · carrinho) e a percepção de "loja diferente" vem do desktop e do conteúdo. Cada
5
+ // variante que quiser um mobile próprio pode não usar este componente — mas o default evita
6
+ // quadruplicar o trabalho e o risco de uma variante esquecer o acesso ao menu ou ao carrinho.
7
+ //
8
+ // EDITOR: o logo é o MESMO caminho do logo do desktop (`logo`, relativo à seção header): trocar
9
+ // uma vez troca os dois. Busca, menu e carrinho são INTERFACE da loja, não copy: ficam fora dos
10
+ // primitivos (README do editor, §8). O aviso de frete do celular é caminho próprio do cabeçalho
11
+ // (`frete-celular`), separado do aviso da faixa do desktop: são duas seções diferentes, e o
12
+ // painel mostra cada um com o nome do lugar onde aparece.
13
+ import Link from "next/link";
14
+ import { Truck, MagnifyingGlass } from "@phosphor-icons/react/dist/ssr";
15
+ import { FREE_SHIPPING_THRESHOLD } from "@/lib/store-config";
16
+ import { CartButton } from "@/components/cart-button";
17
+ import { MobileNav } from "@/components/mobile-nav";
18
+ // Exports NOMEADOS: server component (ver components/site-header.tsx).
19
+ import { EditableIcon, EditableImg, EditableText } from "@/lib/editable";
20
+ import type { ChromeData } from "./registry";
21
+
22
+ export function HeaderBarMobile({
23
+ data,
24
+ /** "solid" = sobre fundo claro (padrão). "over" = sobreposto a uma foto: cores de chrome,
25
+ * logo legível sobre o chrome e SEM a faixa de frete (ela quebraria a altura fixa). */
26
+ tone = "solid",
27
+ }: {
28
+ data: ChromeData;
29
+ tone?: "solid" | "over";
30
+ }) {
31
+ const { shopName, categories } = data;
32
+ const over = tone === "over";
33
+
34
+ return (
35
+ <div className="md:hidden">
36
+ <div className="flex items-center justify-between px-4 py-3">
37
+ <MobileNav
38
+ categories={categories}
39
+ shopName={shopName}
40
+ triggerClassName={over ? "text-[var(--store-chrome-text,#ffffff)]" : "text-[var(--store-ink)] md:hidden"}
41
+ />
42
+ <Link href="/" aria-label={shopName} className="flex">
43
+ {/* <img> puro (EditableImg), não next/image: logo do chrome é SVG de poucos KB, e o next/image
44
+ marcaria lazy num elemento que aparece em toda página (o preload scanner perde o recurso) e
45
+ o reencode come o traço fino do lettering. */}
46
+ <EditableImg path="logo" fallback={{ src: over ? "/brand/logo-chrome.svg" : "/brand/logo.svg", alt: shopName }} label="Logo" className="h-10 w-auto" />
47
+ </Link>
48
+ <div className="flex items-center gap-4">
49
+ <Link href="/busca" aria-label="Buscar" className={`flex ${over ? "text-[var(--store-chrome-text,#ffffff)]" : "text-[var(--store-ink)]"}`}><MagnifyingGlass className="text-[24px]" /></Link>
50
+ <CartButton tone={over ? "chrome" : "ink"} />
51
+ </div>
52
+ </div>
53
+ {!over && FREE_SHIPPING_THRESHOLD != null && (
54
+ <div className="flex items-center justify-center gap-2 bg-[var(--store-chrome-bg,#18181B)] px-4 py-2.5 text-sm font-semibold text-[var(--store-chrome-text,#ffffff)]">
55
+ <EditableIcon path="frete-celular-icone" label="Ícone do aviso de frete (celular)" size={17} className="text-[17px] text-[var(--store-cta,#D97706)]">
56
+ <Truck weight="bold" />
57
+ </EditableIcon>
58
+ {/* o valor (R$X) é configuração da loja, não copy: fica fora do primitivo */}
59
+ <span><EditableText path="frete-celular" fallback="Frete Grátis acima de" label="Aviso de frete (celular)" /> <b>R${FREE_SHIPPING_THRESHOLD}</b></span>
60
+ </div>
61
+ )}
62
+ </div>
63
+ );
64
+ }
@@ -0,0 +1,71 @@
1
+ // HEADER "centralizado" — logo grande no centro, ações nas pontas e a navegação
2
+ // centralizada abaixo. Simetria e ar: o logo vira o eixo da página, não um item de canto.
3
+ //
4
+ // Quando usar: marca editorial/de assinatura, catálogo curado, wordmark bonito.
5
+ // Quando NÃO usar: catálogo grande onde a busca é o caminho principal (veja "classico").
6
+ //
7
+ // EDITOR: o escopo (`chrome.header`) vem da casca (components/site-header.tsx). O logo é o único
8
+ // ponto de marca desta variante (`logo`, o mesmo caminho da barra do celular). Busca, conta e
9
+ // carrinho são INTERFACE; as categorias são DADO do catálogo da Unbox: nada disso vira primitivo
10
+ // (README do editor, §8), e a nav de categorias sai do gate por `data-editor-ignore`.
11
+ import Link from "next/link";
12
+ import { MagnifyingGlass } from "@phosphor-icons/react/dist/ssr";
13
+ import { CartButton } from "@/components/cart-button";
14
+ import { AccountNav } from "@/components/account-nav";
15
+ import { EditableImg } from "@/lib/editable";
16
+ import { HeaderBarMobile } from "../header-bar-mobile";
17
+ import type { ChromeVariantProps } from "../registry";
18
+
19
+ export function HeaderCentralizado({ data }: ChromeVariantProps) {
20
+ const { shopName, categories } = data;
21
+
22
+ return (
23
+ <>
24
+ <HeaderBarMobile data={data} />
25
+
26
+ {/* desktop — linha 1: ações · logo · ações */}
27
+ <div className="mx-auto hidden max-w-[var(--container-max,1240px)] grid-cols-[1fr_auto_1fr] items-center gap-6 px-6 py-5 md:grid">
28
+ <div className="flex items-center gap-4 justify-self-start">
29
+ <Link href="/busca" aria-label="Buscar" className="flex text-[var(--store-ink-2)] transition-colors hover:text-[var(--store-primary,#18181B)]">
30
+ <MagnifyingGlass weight="bold" className="text-[21px]" />
31
+ </Link>
32
+ </div>
33
+
34
+ <Link href="/" aria-label={shopName} className="flex justify-self-center">
35
+ {/* <img> puro (EditableImg), não next/image: logo do chrome é SVG de poucos KB, e o next/image
36
+ marcaria lazy num elemento que aparece em toda página (o preload scanner perde o recurso) e
37
+ o reencode come o traço fino do lettering. */}
38
+ <EditableImg path="logo" fallback={{ src: "/brand/logo.svg", alt: shopName }} label="Logo" className="h-[58px] w-auto" />
39
+ </Link>
40
+
41
+ <div className="flex items-center gap-5 justify-self-end">
42
+ <AccountNav />
43
+ <CartButton />
44
+ </div>
45
+ </div>
46
+
47
+ {/* desktop — linha 2: navegação centralizada (dado do catálogo: fora do editor) */}
48
+ <div className="hidden border-t border-[var(--store-surface-2)] md:block" data-editor-ignore="">
49
+ <nav className="mx-auto flex max-w-[var(--container-max,1240px)] items-center justify-center gap-8 overflow-x-auto px-6" aria-label="Categorias">
50
+ <NavLink href="/produtos" label="Todos" />
51
+ {categories.map((c) => (
52
+ <NavLink key={c.id} href={`/categoria/${encodeURIComponent(c.slug)}`} label={c.name} />
53
+ ))}
54
+ </nav>
55
+ </div>
56
+ </>
57
+ );
58
+ }
59
+
60
+ function NavLink({ href, label }: { href: string; label: string }) {
61
+ const isOffer = /oferta/i.test(label);
62
+ return (
63
+ <Link
64
+ href={href}
65
+ className="shrink-0 whitespace-nowrap py-3 text-[12.5px] font-semibold uppercase tracking-[1.2px] no-underline transition-colors hover:text-[var(--store-primary,#18181B)]"
66
+ style={{ color: isOffer ? "var(--store-sale)" : "var(--store-ink-2)" }}
67
+ >
68
+ {label}
69
+ </Link>
70
+ );
71
+ }