xertica-ui 2.8.0 → 2.9.7

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 (286) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +1 -1
  3. package/bin/cli.ts +327 -87
  4. package/bin/language-config.ts +58 -18
  5. package/components/blocks/auth/AuthPageShell.tsx +63 -18
  6. package/components/blocks/auth/SocialLoginButtons.tsx +3 -3
  7. package/components/blocks/index.ts +1 -0
  8. package/components/blocks/marketing/CTASection.tsx +73 -0
  9. package/components/blocks/marketing/ComplianceBadgeRow.tsx +49 -0
  10. package/components/blocks/marketing/EvidenceTimeline.tsx +91 -0
  11. package/components/blocks/marketing/FeatureRow.tsx +78 -0
  12. package/components/blocks/marketing/Hero.tsx +125 -0
  13. package/components/blocks/marketing/HowItWorksSteps.tsx +90 -0
  14. package/components/blocks/marketing/LogoCloud.tsx +47 -0
  15. package/components/blocks/marketing/MarketingFooter.tsx +109 -0
  16. package/components/blocks/marketing/MarketingNavbar.tsx +192 -0
  17. package/components/blocks/marketing/PillBadge.tsx +96 -0
  18. package/components/blocks/marketing/SectionHeading.tsx +82 -0
  19. package/components/blocks/marketing/ServiceCardGrid.tsx +238 -0
  20. package/components/blocks/marketing/StatBand.tsx +99 -0
  21. package/components/blocks/marketing/TopAccentCards.tsx +91 -0
  22. package/components/blocks/marketing/index.ts +41 -0
  23. package/components/brand/index.ts +1 -0
  24. package/components/brand/isotype/Isotype.tsx +64 -0
  25. package/components/brand/isotype/IsotypeDiagonal.tsx +51 -0
  26. package/components/brand/isotype/IsotypeFrames.tsx +82 -0
  27. package/components/brand/isotype/IsotypeMini.tsx +65 -0
  28. package/components/brand/isotype/IsotypeTwist.tsx +68 -0
  29. package/components/brand/isotype/frame-variants.ts +20 -0
  30. package/components/brand/isotype/index.ts +9 -0
  31. package/components/brand/isotype/isotype-diagonal.mdx +52 -0
  32. package/components/brand/isotype/isotype-diagonal.stories.tsx +44 -0
  33. package/components/brand/isotype/isotype-diagonal.test.tsx +180 -0
  34. package/components/brand/isotype/isotype-frames.mdx +50 -0
  35. package/components/brand/isotype/isotype-frames.stories.tsx +63 -0
  36. package/components/brand/isotype/isotype-frames.test.tsx +92 -0
  37. package/components/brand/isotype/isotype-mini.mdx +61 -0
  38. package/components/brand/isotype/isotype-mini.stories.tsx +75 -0
  39. package/components/brand/isotype/isotype-mini.test.tsx +45 -0
  40. package/components/brand/isotype/isotype-twist.test.tsx +71 -0
  41. package/components/brand/isotype/isotype.mdx +61 -0
  42. package/components/brand/isotype/isotype.stories.tsx +74 -0
  43. package/components/brand/isotype/isotype.test.tsx +41 -0
  44. package/components/brand/isotype/mini-patterns.ts +50 -0
  45. package/components/brand/isotype/patterns.ts +83 -0
  46. package/components/brand/isotype/twist-variants.ts +20 -0
  47. package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +3 -6
  48. package/components/pages/index.ts +1 -0
  49. package/components/pages/landing-page/LandingPage.tsx +360 -0
  50. package/components/pages/landing-page/index.ts +1 -0
  51. package/components/pages/login-page/LoginPage.tsx +3 -6
  52. package/components/pages/reset-password-page/ResetPasswordPage.tsx +1 -4
  53. package/components/pages/verify-email-page/VerifyEmailPage.tsx +2 -5
  54. package/components/ui/button/button.test.tsx +1 -1
  55. package/components/ui/button/button.tsx +7 -6
  56. package/components/ui/card/card.test.tsx +12 -0
  57. package/components/ui/card/card.tsx +5 -3
  58. package/components/ui/input-otp/input-otp.test.tsx +14 -1
  59. package/contexts/BrandColorsContext.tsx +9 -5
  60. package/contexts/theme-data.ts +32 -5
  61. package/dist/AssistantChart-BU48lxL2.js +3383 -0
  62. package/dist/AssistantChart-CoN6ga6Z.js +3383 -0
  63. package/dist/AssistantChart-DIRYJ7WY.js +3383 -0
  64. package/dist/AssistantChart-DOnY1ZlD.cjs +3551 -0
  65. package/dist/AssistantChart-DjM3qnp5.cjs +3551 -0
  66. package/dist/AssistantChart-hxPyE1S9.cjs +3551 -0
  67. package/dist/AudioPlayer-CpihsbaD.js +1278 -0
  68. package/dist/AudioPlayer-CvGajzMC.cjs +1282 -0
  69. package/dist/AudioPlayer-CvHpFqDG.cjs +1282 -0
  70. package/dist/AudioPlayer-D4S7GQou.js +1278 -0
  71. package/dist/BrandColorsContext-BNKLUVcU.js +829 -0
  72. package/dist/BrandColorsContext-DNZGHItQ.cjs +836 -0
  73. package/dist/BrandColorsContext-Ug_TZ8yn.cjs +846 -0
  74. package/dist/BrandColorsContext-z0O2LlZz.js +839 -0
  75. package/dist/CodeBlock-BkaZf-R8.cjs +223 -0
  76. package/dist/CodeBlock-CKu8LgbP.cjs +223 -0
  77. package/dist/CodeBlock-DiLS5LLI.js +221 -0
  78. package/dist/CodeBlock-zdylBGdZ.js +221 -0
  79. package/dist/CustomTooltipContent-BAR5qCQg.js +33 -0
  80. package/dist/CustomTooltipContent-CBLHBxX8.cjs +54 -0
  81. package/dist/CustomTooltipContent-CjWm3jjV.js +33 -0
  82. package/dist/CustomTooltipContent-DTV9MN9E.cjs +54 -0
  83. package/dist/EvidenceTimeline-DrRk4yqb.cjs +560 -0
  84. package/dist/EvidenceTimeline-l4MaV4C7.js +546 -0
  85. package/dist/FeatureCard-CIbBHqeY.cjs +497 -0
  86. package/dist/FeatureCard-DtoKPSfW.cjs +497 -0
  87. package/dist/FeatureCard-P4iEpBmB.js +485 -0
  88. package/dist/FeatureCard-e3f8bnF0.js +485 -0
  89. package/dist/ImageWithFallback-C1ww9Hg3.cjs +122 -0
  90. package/dist/ImageWithFallback-CBrkAEo3.cjs +122 -0
  91. package/dist/ImageWithFallback-CGk5zPH-.js +98 -0
  92. package/dist/ImageWithFallback-CfnoFFSP.js +98 -0
  93. package/dist/Isotype--fRZWwS6.js +85 -0
  94. package/dist/Isotype-0dpLqetB.js +85 -0
  95. package/dist/Isotype-B8XLEXSB.cjs +92 -0
  96. package/dist/Isotype-ChKwN7e-.cjs +91 -0
  97. package/dist/IsotypeDiagonal-CuPYPP4f.js +84 -0
  98. package/dist/IsotypeDiagonal-eNcOXRAc.cjs +91 -0
  99. package/dist/IsotypeDiagonal-oiwpXZhE.js +104 -0
  100. package/dist/IsotypeDiagonal-xMFoFXkb.cjs +109 -0
  101. package/dist/IsotypeFrames-BcWtyAWp.cjs +83 -0
  102. package/dist/IsotypeFrames-BnvSJKiH.cjs +73 -0
  103. package/dist/IsotypeFrames-iDRTRuIu.js +79 -0
  104. package/dist/IsotypeFrames-qoXOvOJt.js +69 -0
  105. package/dist/IsotypeMini-CQtWVo3h.js +126 -0
  106. package/dist/IsotypeMini-Cu_sW5yN.cjs +134 -0
  107. package/dist/IsotypeTwist-BTdNL4iV.js +106 -0
  108. package/dist/IsotypeTwist-DvsUgZs_.cjs +115 -0
  109. package/dist/LanguageContext-B1aOXO-K.js +1987 -0
  110. package/dist/LanguageContext-BP0k7AzI.js +1993 -0
  111. package/dist/LanguageContext-CChcIHIG.cjs +1998 -0
  112. package/dist/LanguageContext-ubC7TSs_.cjs +1992 -0
  113. package/dist/LanguageSelector-BLFPXGo8.cjs +48 -0
  114. package/dist/LanguageSelector-CYRzWkRZ.cjs +48 -0
  115. package/dist/LanguageSelector-CyHjZgC4.cjs +48 -0
  116. package/dist/LanguageSelector-DE8lIySO.js +46 -0
  117. package/dist/LanguageSelector-DUr1hjBW.js +46 -0
  118. package/dist/LanguageSelector-DjbWIKWC.js +46 -0
  119. package/dist/LogoCloud-C_uMKVNg.cjs +513 -0
  120. package/dist/LogoCloud-DJsMPNPh.js +500 -0
  121. package/dist/MarketingFooter-B_ycEdTJ.cjs +727 -0
  122. package/dist/MarketingFooter-Bhx-2Vlr.cjs +727 -0
  123. package/dist/MarketingFooter-Br1MGK9e.js +699 -0
  124. package/dist/MarketingFooter-BwE1vmeX.cjs +700 -0
  125. package/dist/MarketingFooter-CsZ1_xL9.js +709 -0
  126. package/dist/MarketingFooter-DCwf78Q4.cjs +716 -0
  127. package/dist/MarketingFooter-KH2r-OOL.js +709 -0
  128. package/dist/MarketingFooter-nB7G7gQ5.js +682 -0
  129. package/dist/SectionHeading-DEeOKcAd.cjs +857 -0
  130. package/dist/SectionHeading-DMNObcwm.js +838 -0
  131. package/dist/ThemeToggle--CGJjQJv.cjs +219 -0
  132. package/dist/ThemeToggle-Bn_7Pmfe.js +215 -0
  133. package/dist/ThemeToggle-CoL8Ady7.js +215 -0
  134. package/dist/ThemeToggle-D2Wzm5bn.cjs +219 -0
  135. package/dist/VerifyEmailPage-90lK6pP4.js +3258 -0
  136. package/dist/VerifyEmailPage-BJX1Rter.cjs +3320 -0
  137. package/dist/VerifyEmailPage-BSWvOgry.js +3310 -0
  138. package/dist/VerifyEmailPage-CDqIkhx-.js +3224 -0
  139. package/dist/VerifyEmailPage-Crxje3ZB.js +3323 -0
  140. package/dist/VerifyEmailPage-D2ShbuEN.cjs +3333 -0
  141. package/dist/VerifyEmailPage-DEBZXQ7c.cjs +3310 -0
  142. package/dist/VerifyEmailPage-DGaX-jZv.cjs +3268 -0
  143. package/dist/VerifyEmailPage-ZtOPkgOA.cjs +3234 -0
  144. package/dist/VerifyEmailPage-s927GSiR.js +3300 -0
  145. package/dist/XerticaOrbe-BHex09xs.js +1925 -0
  146. package/dist/XerticaOrbe-BKW3LJEz.js +1925 -0
  147. package/dist/XerticaOrbe-Br2i99U_.cjs +1927 -0
  148. package/dist/XerticaOrbe-kjFmuyx-.cjs +1927 -0
  149. package/dist/XerticaProvider-Bi5lKC9N.js +44 -0
  150. package/dist/XerticaProvider-BrPMVhiv.js +44 -0
  151. package/dist/XerticaProvider-CnX5kt5W.cjs +46 -0
  152. package/dist/XerticaProvider-DEKNJTlN.js +44 -0
  153. package/dist/XerticaProvider-X_wavDYA.cjs +46 -0
  154. package/dist/XerticaProvider-b3n_oL8w.cjs +46 -0
  155. package/dist/alert-dialog--qdlmB5i.cjs +872 -0
  156. package/dist/alert-dialog-C48TOdYU.cjs +872 -0
  157. package/dist/alert-dialog-CEPpnpXt.js +819 -0
  158. package/dist/alert-dialog-NjflitDa.js +819 -0
  159. package/dist/assistant.cjs.js +2 -2
  160. package/dist/assistant.es.js +2 -2
  161. package/dist/avatar-CtEKgg7s.cjs +77 -0
  162. package/dist/avatar-D03MNQxs.js +54 -0
  163. package/dist/avatar-Df7KgSvs.js +54 -0
  164. package/dist/avatar-Gy0_Xu9i.cjs +77 -0
  165. package/dist/blocks.cjs.js +33 -18
  166. package/dist/blocks.es.js +2 -2
  167. package/dist/brand.cjs.js +110 -4
  168. package/dist/brand.es.js +98 -4
  169. package/dist/breadcrumb-BGO5PZPG.js +90 -0
  170. package/dist/breadcrumb-BvGAY4ne.cjs +98 -0
  171. package/dist/breadcrumb-CU-punra.js +90 -0
  172. package/dist/breadcrumb-DjV9wfX1.cjs +98 -0
  173. package/dist/button-B05czY_8.js +62 -0
  174. package/dist/button-B3SE1liJ.js +62 -0
  175. package/dist/button-BMDlWZR8.cjs +85 -0
  176. package/dist/button-DVhloDtr.cjs +85 -0
  177. package/dist/cli.js +280 -62
  178. package/dist/components/blocks/auth/AuthPageShell.d.ts +21 -5
  179. package/dist/components/blocks/index.d.ts +1 -0
  180. package/dist/components/blocks/marketing/CTASection.d.ts +28 -0
  181. package/dist/components/blocks/marketing/ComplianceBadgeRow.d.ts +23 -0
  182. package/dist/components/blocks/marketing/EvidenceTimeline.d.ts +26 -0
  183. package/dist/components/blocks/marketing/FeatureRow.d.ts +25 -0
  184. package/dist/components/blocks/marketing/Hero.d.ts +35 -0
  185. package/dist/components/blocks/marketing/HowItWorksSteps.d.ts +29 -0
  186. package/dist/components/blocks/marketing/LogoCloud.d.ts +22 -0
  187. package/dist/components/blocks/marketing/MarketingFooter.d.ts +42 -0
  188. package/dist/components/blocks/marketing/MarketingNavbar.d.ts +47 -0
  189. package/dist/components/blocks/marketing/PillBadge.d.ts +21 -0
  190. package/dist/components/blocks/marketing/SectionHeading.d.ts +23 -0
  191. package/dist/components/blocks/marketing/ServiceCardGrid.d.ts +65 -0
  192. package/dist/components/blocks/marketing/StatBand.d.ts +25 -0
  193. package/dist/components/blocks/marketing/TopAccentCards.d.ts +29 -0
  194. package/dist/components/blocks/marketing/index.d.ts +28 -0
  195. package/dist/components/brand/index.d.ts +1 -0
  196. package/dist/components/brand/isotype/Isotype.d.ts +27 -0
  197. package/dist/components/brand/isotype/IsotypeDiagonal.d.ts +19 -0
  198. package/dist/components/brand/isotype/IsotypeFrames.d.ts +21 -0
  199. package/dist/components/brand/isotype/IsotypeMini.d.ts +26 -0
  200. package/dist/components/brand/isotype/IsotypeTwist.d.ts +22 -0
  201. package/dist/components/brand/isotype/frame-variants.d.ts +11 -0
  202. package/dist/components/brand/isotype/index.d.ts +9 -0
  203. package/dist/components/brand/isotype/mini-patterns.d.ts +25 -0
  204. package/dist/components/brand/isotype/patterns.d.ts +42 -0
  205. package/dist/components/brand/isotype/twist-variants.d.ts +11 -0
  206. package/dist/components/pages/forgot-password-page/ForgotPasswordPage.d.ts +2 -2
  207. package/dist/components/pages/index.d.ts +1 -0
  208. package/dist/components/pages/landing-page/LandingPage.d.ts +18 -0
  209. package/dist/components/pages/landing-page/index.d.ts +1 -0
  210. package/dist/components/pages/login-page/LoginPage.d.ts +2 -2
  211. package/dist/components/pages/verify-email-page/VerifyEmailPage.d.ts +1 -1
  212. package/dist/components/ui/card/card.d.ts +3 -1
  213. package/dist/contexts/theme-data.d.ts +6 -0
  214. package/dist/dropdown-menu-ChQa9zW3.cjs +225 -0
  215. package/dist/dropdown-menu-DRvnsQAt.cjs +225 -0
  216. package/dist/dropdown-menu-DUk81i5Y.js +190 -0
  217. package/dist/dropdown-menu-NlUHhVmK.js +190 -0
  218. package/dist/hooks.cjs.js +2 -2
  219. package/dist/hooks.es.js +2 -2
  220. package/dist/index.cjs.js +57 -41
  221. package/dist/index.es.js +25 -25
  222. package/dist/input-Csp_Metb.cjs +91 -0
  223. package/dist/input-DVRwGVLx.js +65 -0
  224. package/dist/input-Dqyj7Hdw.cjs +91 -0
  225. package/dist/input-dOgrFQ-3.js +65 -0
  226. package/dist/layout.cjs.js +1 -1
  227. package/dist/layout.es.js +1 -1
  228. package/dist/media.cjs.js +1 -1
  229. package/dist/media.es.js +1 -1
  230. package/dist/pages.cjs.js +2 -1
  231. package/dist/pages.es.js +1 -1
  232. package/dist/rich-text-editor-7K7UjLJN.cjs +2968 -0
  233. package/dist/rich-text-editor-B3jLP2bm.cjs +2968 -0
  234. package/dist/rich-text-editor-BntMVQsB.js +2894 -0
  235. package/dist/rich-text-editor-Diz_onyB.js +2894 -0
  236. package/dist/rich-text-editor-RWvK4hd3.cjs +2968 -0
  237. package/dist/rich-text-editor-gauEkzPM.js +2894 -0
  238. package/dist/select-B6PygS38.cjs +161 -0
  239. package/dist/select-BD763J5Y.cjs +161 -0
  240. package/dist/select-QJDKZITN.js +130 -0
  241. package/dist/select-q7aDfuPn.js +130 -0
  242. package/dist/sidebar-Bkk1YAsD.cjs +1154 -0
  243. package/dist/sidebar-CuzYeOSz.cjs +1154 -0
  244. package/dist/sidebar-CwlW3LXE.js +1151 -0
  245. package/dist/sidebar-DNkf-Ug6.js +1151 -0
  246. package/dist/sidebar-DaS3fplw.cjs +1154 -0
  247. package/dist/sidebar-FDCY42oP.js +1151 -0
  248. package/dist/skeleton-BtBwJ39W.js +79 -0
  249. package/dist/skeleton-CJyg0eJ1.cjs +88 -0
  250. package/dist/skeleton-D8WaauK0.cjs +88 -0
  251. package/dist/skeleton-DBYtjaZW.cjs +88 -0
  252. package/dist/skeleton-DY_lSFWf.js +79 -0
  253. package/dist/skeleton-Df65Ox10.js +79 -0
  254. package/dist/slider-BKV_vvsh.cjs +78 -0
  255. package/dist/slider-B_lUjFNa.cjs +78 -0
  256. package/dist/slider-CtBpIaU4.js +56 -0
  257. package/dist/slider-KgWmSJbx.js +56 -0
  258. package/dist/tooltip-BPttuPxU.js +48 -0
  259. package/dist/tooltip-CIKRzQqE.cjs +72 -0
  260. package/dist/tooltip-CoDByjE_.cjs +72 -0
  261. package/dist/tooltip-_MAwsiOL.js +48 -0
  262. package/dist/ui.cjs.js +13 -13
  263. package/dist/ui.es.js +13 -13
  264. package/dist/xertica-assistant-B1xAl2Vn.js +2173 -0
  265. package/dist/xertica-assistant-BBUMaUXw.js +2173 -0
  266. package/dist/xertica-assistant-CWuBwfzt.cjs +2180 -0
  267. package/dist/xertica-assistant-D2hlwiEP.js +2173 -0
  268. package/dist/xertica-assistant-DN2J_sQQ.cjs +2180 -0
  269. package/dist/xertica-assistant-DdYS6WXT.cjs +2180 -0
  270. package/dist/xertica-ui.css +1 -1
  271. package/llms-compact.txt +1 -1
  272. package/llms.txt +1 -1
  273. package/package.json +4 -2
  274. package/styles/xertica/base.css +8 -0
  275. package/styles/xertica/tokens.css +1 -0
  276. package/templates/package.json +1 -1
  277. package/templates/src/app/components/AuthGuard.tsx +6 -0
  278. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -4
  279. package/templates/src/features/auth/ui/LoginContent.tsx +1 -4
  280. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +1 -4
  281. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +1 -4
  282. package/templates/src/locales/en/pages/landing.json +175 -0
  283. package/templates/src/locales/es/pages/landing.json +175 -0
  284. package/templates/src/locales/pt-BR/pages/landing.json +175 -0
  285. package/templates/src/pages/LandingPage.tsx +349 -0
  286. package/templates/src/styles/xertica/tokens.css +1 -0
@@ -0,0 +1,360 @@
1
+ import * as React from 'react';
2
+ import {
3
+ BookOpen,
4
+ Braces,
5
+ Code2,
6
+ Component,
7
+ Figma,
8
+ FileCode2,
9
+ Github,
10
+ Layers,
11
+ Package,
12
+ ShieldCheck,
13
+ Sparkles,
14
+ SquareTerminal,
15
+ SwatchBook,
16
+ } from 'lucide-react';
17
+ import { useTranslation } from 'react-i18next';
18
+ import { IsotypeDiagonal, IsotypeFrames } from '../../brand/isotype';
19
+ import { XerticaLogo } from '../../brand/xertica-logo';
20
+ import {
21
+ ComplianceBadgeRow,
22
+ CTASection,
23
+ FeatureRow,
24
+ Hero,
25
+ HowItWorksSteps,
26
+ MarketingFooter,
27
+ MarketingNavbar,
28
+ SectionHeading,
29
+ ServiceCardGrid,
30
+ StatBand,
31
+ TopAccentCards,
32
+ } from '../../blocks/marketing';
33
+
34
+ /**
35
+ * Public marketing landing page for the Xertica-ui design system.
36
+ *
37
+ * @description
38
+ * A full composition of the `blocks/marketing` primitives — navbar, hero,
39
+ * framing cards, service grids, alternating feature rows, a "how it works"
40
+ * steps block, compliance badges, a closing CTA, and a footer.
41
+ * Renders in its own scrollable root, independent of the authenticated
42
+ * app shell's fixed-height layout.
43
+ *
44
+ * @ai-rules
45
+ * 1. This page is presentational only — all CTAs use placeholder `href="#"` links; wire real routes/handlers when integrating.
46
+ * 2. All visuals are token-driven placeholders (brand isotypes, inline SVG logos) to avoid any network/image dependency.
47
+ * 3. Keep composing exclusively from `components/blocks/marketing` — do not hand-roll sections that a block already covers.
48
+ * 4. All copy comes from the `landing` i18n namespace (`locales/<lang>/pages/landing.json`) via `useTranslation()` — never hardcode user-facing text here.
49
+ */
50
+ export function LandingPage() {
51
+ const { t } = useTranslation();
52
+
53
+ return (
54
+ <div className="h-full w-full overflow-y-auto bg-background">
55
+ <MarketingNavbar
56
+ logo={<XerticaLogo className="h-8 w-auto text-primary dark:text-foreground" variant="theme" />}
57
+ links={[
58
+ { label: t('landing.nav.product'), href: '#' },
59
+ { label: t('landing.nav.resources'), href: '#' },
60
+ { label: t('landing.nav.pricing'), href: '#' },
61
+ { label: t('landing.nav.contact'), href: '#' },
62
+ ]}
63
+ cta={{ label: t('landing.nav.login'), href: '#' }}
64
+ openMenuLabel={t('landing.nav.openMenu')}
65
+ closeMenuLabel={t('landing.nav.closeMenu')}
66
+ />
67
+
68
+ <Hero
69
+ eyebrow={{ label: t('landing.hero.eyebrow'), icon: <Sparkles /> }}
70
+ title={t('landing.hero.title')}
71
+ description={t('landing.hero.description')}
72
+ primaryCta={{ label: t('landing.hero.primaryCta'), href: '#' }}
73
+ secondaryCta={{ label: t('landing.hero.secondaryCta'), href: '#' }}
74
+ // Isotype (plain grid mark) is out of rotation for now — only Diagonal + Frames are in active use.
75
+ // visual={<Isotype className="mx-auto w-full max-w-md" />}
76
+ visual={<IsotypeDiagonal className="mx-auto w-full max-w-md" />}
77
+ />
78
+
79
+ <section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
80
+ <TopAccentCards
81
+ items={[
82
+ {
83
+ title: t('landing.framing.purpose.title'),
84
+ body: t('landing.framing.purpose.body'),
85
+ color: 'chart-4',
86
+ },
87
+ {
88
+ title: t('landing.framing.problem.title'),
89
+ body: t('landing.framing.problem.body'),
90
+ bullets: [
91
+ t('landing.framing.problem.bullet1'),
92
+ t('landing.framing.problem.bullet2'),
93
+ t('landing.framing.problem.bullet3'),
94
+ ],
95
+ color: 'chart-5',
96
+ },
97
+ ]}
98
+ />
99
+ </section>
100
+
101
+ <section className="bg-card">
102
+ <div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
103
+ <SectionHeading
104
+ title={t('landing.ecosystem.title')}
105
+ accentColor="chart-1"
106
+ className="mb-12"
107
+ />
108
+
109
+ <ServiceCardGrid
110
+ variant="card"
111
+ columns={4}
112
+ items={[
113
+ {
114
+ icon: <Figma />,
115
+ color: 'primary',
116
+ title: t('landing.ecosystem.figma.title'),
117
+ description: t('landing.ecosystem.figma.description'),
118
+ bullets: [
119
+ t('landing.ecosystem.figma.bullet1'),
120
+ t('landing.ecosystem.figma.bullet2'),
121
+ ],
122
+ },
123
+ {
124
+ icon: <FileCode2 />,
125
+ color: 'chart-1',
126
+ title: t('landing.ecosystem.vscode.title'),
127
+ description: t('landing.ecosystem.vscode.description'),
128
+ bullets: [
129
+ t('landing.ecosystem.vscode.bullet1'),
130
+ t('landing.ecosystem.vscode.bullet2'),
131
+ ],
132
+ },
133
+ {
134
+ icon: <Component />,
135
+ color: 'chart-5',
136
+ title: t('landing.ecosystem.storybook.title'),
137
+ description: t('landing.ecosystem.storybook.description'),
138
+ bullets: [
139
+ t('landing.ecosystem.storybook.bullet1'),
140
+ t('landing.ecosystem.storybook.bullet2'),
141
+ ],
142
+ },
143
+ {
144
+ icon: <Github />,
145
+ color: 'chart-2',
146
+ title: t('landing.ecosystem.github.title'),
147
+ description: t('landing.ecosystem.github.description'),
148
+ bullets: [
149
+ t('landing.ecosystem.github.bullet1'),
150
+ t('landing.ecosystem.github.bullet2'),
151
+ ],
152
+ },
153
+ ]}
154
+ />
155
+ </div>
156
+ </section>
157
+
158
+ <FeatureRow
159
+ eyebrow={t('landing.features.theming.eyebrow')}
160
+ title={t('landing.features.theming.title')}
161
+ description={t('landing.features.theming.description')}
162
+ bullets={[
163
+ t('landing.features.theming.bullet1'),
164
+ t('landing.features.theming.bullet2'),
165
+ t('landing.features.theming.bullet3'),
166
+ ]}
167
+ visual={<IsotypeFrames variant="blue-magenta" className="mx-auto w-full max-w-md" />}
168
+ />
169
+
170
+ <FeatureRow
171
+ tone="card"
172
+ reverse
173
+ eyebrow={t('landing.features.composition.eyebrow')}
174
+ title={t('landing.features.composition.title')}
175
+ description={t('landing.features.composition.description')}
176
+ bullets={[
177
+ t('landing.features.composition.bullet1'),
178
+ t('landing.features.composition.bullet2'),
179
+ t('landing.features.composition.bullet3'),
180
+ ]}
181
+ visual={<IsotypeDiagonal className="mx-auto w-full max-w-md" />}
182
+ />
183
+
184
+ <FeatureRow
185
+ eyebrow={t('landing.features.onboarding.eyebrow')}
186
+ title={t('landing.features.onboarding.title')}
187
+ description={t('landing.features.onboarding.description')}
188
+ bullets={[
189
+ t('landing.features.onboarding.bullet1'),
190
+ t('landing.features.onboarding.bullet2'),
191
+ t('landing.features.onboarding.bullet3'),
192
+ ]}
193
+ visual={<IsotypeFrames variant="blue-yellow" className="mx-auto w-full max-w-md" />}
194
+ />
195
+
196
+ <section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
197
+ <SectionHeading
198
+ title={t('landing.useCases.title')}
199
+ accentColor="chart-4"
200
+ className="mb-12"
201
+ />
202
+
203
+ <ServiceCardGrid
204
+ variant="card"
205
+ columns={3}
206
+ items={[
207
+ {
208
+ color: 'chart-2',
209
+ title: t('landing.useCases.ecommerce.title'),
210
+ description: t('landing.useCases.ecommerce.description'),
211
+ checklist: [
212
+ t('landing.useCases.ecommerce.check1'),
213
+ t('landing.useCases.ecommerce.check2'),
214
+ ],
215
+ className: 'border',
216
+ },
217
+ {
218
+ color: 'primary',
219
+ title: t('landing.useCases.saas.title'),
220
+ description: t('landing.useCases.saas.description'),
221
+ checklist: [t('landing.useCases.saas.check1'), t('landing.useCases.saas.check2')],
222
+ className: 'border',
223
+ },
224
+ {
225
+ color: 'chart-4',
226
+ title: t('landing.useCases.fintech.title'),
227
+ description: t('landing.useCases.fintech.description'),
228
+ checklist: [
229
+ t('landing.useCases.fintech.check1'),
230
+ t('landing.useCases.fintech.check2'),
231
+ ],
232
+ className: 'border',
233
+ },
234
+ ]}
235
+ />
236
+ </section>
237
+
238
+ <section className="bg-sidebar text-sidebar-foreground">
239
+ <div className="mx-auto max-w-5xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
240
+ <SectionHeading
241
+ title={t('landing.methodology.title')}
242
+ tone="sidebar"
243
+ accentColor="primary"
244
+ className="mb-12"
245
+ />
246
+
247
+ <HowItWorksSteps
248
+ items={[
249
+ {
250
+ icon: <SwatchBook />,
251
+ title: t('landing.methodology.tokensFirst.title'),
252
+ description: t('landing.methodology.tokensFirst.description'),
253
+ color: 'chart-4',
254
+ },
255
+ {
256
+ icon: <Layers />,
257
+ title: t('landing.methodology.primitives.title'),
258
+ description: t('landing.methodology.primitives.description'),
259
+ color: 'chart-2',
260
+ },
261
+ {
262
+ icon: <Braces />,
263
+ title: t('landing.methodology.compositionBlocks.title'),
264
+ description: t('landing.methodology.compositionBlocks.description'),
265
+ color: 'chart-1',
266
+ },
267
+ {
268
+ icon: <SquareTerminal />,
269
+ title: t('landing.methodology.cliTemplates.title'),
270
+ description: t('landing.methodology.cliTemplates.description'),
271
+ color: 'warning',
272
+ },
273
+ ]}
274
+ />
275
+ </div>
276
+ </section>
277
+
278
+ <section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
279
+ <div className="rounded-2xl border border-border bg-card p-8 md:p-12">
280
+ <ComplianceBadgeRow
281
+ badges={[
282
+ {
283
+ icon: <ShieldCheck />,
284
+ label: t('landing.compliance.wcag.label'),
285
+ description: t('landing.compliance.wcag.description'),
286
+ },
287
+ {
288
+ icon: <Code2 />,
289
+ label: t('landing.compliance.typescript.label'),
290
+ description: t('landing.compliance.typescript.description'),
291
+ },
292
+ {
293
+ icon: <Package />,
294
+ label: t('landing.compliance.treeShakeable.label'),
295
+ description: t('landing.compliance.treeShakeable.description'),
296
+ },
297
+ {
298
+ icon: <BookOpen />,
299
+ label: t('landing.compliance.themeable.label'),
300
+ description: t('landing.compliance.themeable.description'),
301
+ },
302
+ ]}
303
+ />
304
+
305
+ <div className="my-10 border-t border-border" />
306
+
307
+ <StatBand
308
+ columns={3}
309
+ stats={[
310
+ { value: '99.9%', label: t('landing.stats.docsUptime'), color: 'success' },
311
+ { value: '<24h', label: t('landing.stats.avgResponseTime'), color: 'info' },
312
+ { value: '100%', label: t('landing.stats.typedComponents'), color: 'chart-5' },
313
+ ]}
314
+ />
315
+ </div>
316
+ </section>
317
+
318
+ <section className="mx-auto max-w-7xl px-4 pb-16 sm:px-6 lg:px-8 md:pb-24">
319
+ <CTASection
320
+ title={t('landing.cta.title')}
321
+ description={t('landing.cta.description')}
322
+ cta={{ label: t('landing.cta.primary'), href: '#' }}
323
+ secondaryCta={{ label: t('landing.cta.secondary'), href: '#' }}
324
+ />
325
+ </section>
326
+
327
+ <MarketingFooter
328
+ logo={<XerticaLogo className="h-8 w-auto text-primary dark:text-foreground" variant="theme" />}
329
+ tagline={t('landing.footer.tagline')}
330
+ columns={[
331
+ {
332
+ heading: t('landing.footer.product.heading'),
333
+ links: [
334
+ { label: t('landing.footer.product.components'), href: '#' },
335
+ { label: t('landing.footer.product.blocks'), href: '#' },
336
+ { label: t('landing.footer.product.themes'), href: '#' },
337
+ ],
338
+ },
339
+ {
340
+ heading: t('landing.footer.resources.heading'),
341
+ links: [
342
+ { label: t('landing.footer.resources.docs'), href: '#' },
343
+ { label: t('landing.footer.resources.cli'), href: '#' },
344
+ { label: t('landing.footer.resources.changelog'), href: '#' },
345
+ ],
346
+ },
347
+ {
348
+ heading: t('landing.footer.company.heading'),
349
+ links: [
350
+ { label: t('landing.footer.company.about'), href: '#' },
351
+ { label: t('landing.footer.company.contact'), href: '#' },
352
+ { label: t('landing.footer.company.careers'), href: '#' },
353
+ ],
354
+ },
355
+ ]}
356
+ copyright={t('landing.footer.copyright')}
357
+ />
358
+ </div>
359
+ );
360
+ }
@@ -0,0 +1 @@
1
+ export { LandingPage } from './LandingPage';
@@ -22,13 +22,13 @@ interface LoginPageProps {
22
22
  * Standard Login Page component.
23
23
  *
24
24
  * @description
25
- * A complete, responsive login screen featuring a hero image side-panel (on desktop)
25
+ * A complete, responsive login screen featuring a brand isotype side-panel (on desktop)
26
26
  * and a focused authentication form. Supports email/password login and placeholders
27
27
  * for Social/SSO providers (Google, MT Login, gov.br).
28
28
  *
29
29
  * @ai-rules
30
30
  * 1. State Management: You must provide the `onLogin` callback to handle the actual authentication logic.
31
- * 2. Visuals: Hero images use `ImageWithFallback`. You can customize the `src` if needed.
31
+ * 2. Visuals: Left panel renders `IsotypeTwist` via `AuthPageShell`'s `leftPanel="isotype"` no hero image on this page.
32
32
  * 3. Routing: Uses `react-router` for navigation to the forgot-password flow.
33
33
  */
34
34
  export function LoginPage({ onLogin }: LoginPageProps) {
@@ -62,10 +62,7 @@ export function LoginPage({ onLogin }: LoginPageProps) {
62
62
  };
63
63
 
64
64
  return (
65
- <AuthPageShell
66
- imageSrc="https://images.unsplash.com/photo-1551434678-e076c223a692?w=1200&h=800&fit=crop&auto=format"
67
- imageAlt={t('login.heroImageAlt')}
68
- >
65
+ <AuthPageShell leftPanel="isotype">
69
66
  <div className="text-center">
70
67
  <div className="flex items-center justify-center mb-4">
71
68
  <XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
@@ -62,10 +62,7 @@ export function ResetPasswordPage() {
62
62
  };
63
63
 
64
64
  return (
65
- <AuthPageShell
66
- imageSrc="https://images.unsplash.com/photo-1555949963-aa79dcee981c?w=1200&h=800&fit=crop&auto=format"
67
- imageAlt={t('resetPassword.heroImageAlt')}
68
- >
65
+ <AuthPageShell leftPanel="isotype">
69
66
  <button
70
67
  onClick={() => navigate('/login')}
71
68
  className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
@@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
12
12
  * @description
13
13
  * An informative screen shown after a password reset request. It instructs
14
14
  * the user to check their email and provides a "Resend email" feature.
15
- * Includes the same consistent layout with a hero image side-panel.
15
+ * Includes the same consistent layout with the shared brand isotype side-panel.
16
16
  *
17
17
  * @ai-rules
18
18
  * 1. Data Source: It retrieves the email from the router's `location.state`.
@@ -46,10 +46,7 @@ export function VerifyEmailPage() {
46
46
  };
47
47
 
48
48
  return (
49
- <AuthPageShell
50
- imageSrc="https://images.unsplash.com/photo-1563986768609-322da13575f3?w=1200&h=800&fit=crop&auto=format"
51
- imageAlt={t('verifyEmail.heroImageAlt')}
52
- >
49
+ <AuthPageShell leftPanel="isotype">
53
50
  <div className="text-center">
54
51
  <div className="flex items-center justify-center mb-4">
55
52
  <XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
@@ -16,7 +16,7 @@ describe('Button', () => {
16
16
  expect(screen.getByRole('button')).toHaveClass('bg-destructive');
17
17
 
18
18
  rerender(<Button variant="outline">Outline</Button>);
19
- expect(screen.getByRole('button')).toHaveClass('border-border');
19
+ expect(screen.getByRole('button')).toHaveClass('border-outline-stroke');
20
20
 
21
21
  rerender(<Button variant="ghost">Ghost</Button>);
22
22
  expect(screen.getByRole('button')).toHaveClass('hover:bg-accent');
@@ -6,22 +6,23 @@ import { Loader2 } from 'lucide-react';
6
6
  import { cn } from '../../shared/utils';
7
7
 
8
8
  const buttonVariants = cva(
9
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-button)] text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-button)] text-sm font-button-weight transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
10
10
  {
11
11
  variants: {
12
12
  variant: {
13
13
  default: 'border-button-stroke bg-button-primary text-button-primary',
14
14
  destructive:
15
- 'border-button-stroke bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
16
- outline: 'border border-border bg-background hover:bg-accent hover:text-accent-foreground',
15
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
16
+ outline:
17
+ 'border border-outline-stroke bg-background hover:bg-accent hover:text-accent-foreground',
17
18
  secondary: 'border-button-stroke bg-button-secondary text-button-secondary',
18
19
  ghost: 'hover:bg-accent hover:text-accent-foreground',
19
20
  link: 'text-primary underline-offset-4 hover:underline',
20
21
  success:
21
- 'border-button-stroke bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success/20 dark:focus-visible:ring-success/40',
22
- info: 'border-button-stroke bg-info text-info-foreground hover:bg-info/90 focus-visible:ring-info/20 dark:focus-visible:ring-info/40',
22
+ 'bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success/20 dark:focus-visible:ring-success/40',
23
+ info: 'bg-info text-info-foreground hover:bg-info/90 focus-visible:ring-info/20 dark:focus-visible:ring-info/40',
23
24
  warning:
24
- 'border-button-stroke bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40',
25
+ 'bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40',
25
26
  },
26
27
  size: {
27
28
  default: 'h-9 px-4 py-2 has-[>svg]:px-3',
@@ -52,4 +52,16 @@ describe('Card', () => {
52
52
  const { container } = render(<Card className="custom-card">Content</Card>);
53
53
  expect(container.querySelector('[data-slot="card"]')).toHaveClass('custom-card');
54
54
  });
55
+
56
+ it('defaults to the theme-reactive card background and foreground', () => {
57
+ const { container } = render(<Card>Content</Card>);
58
+ expect(container.querySelector('[data-slot="card"]')).toHaveClass('bg-card', 'text-card-foreground');
59
+ });
60
+
61
+ it('pairs variant="white" with a fixed dark text color, not the theme-reactive one', () => {
62
+ const { container } = render(<Card variant="white">Content</Card>);
63
+ const card = container.querySelector('[data-slot="card"]');
64
+ expect(card).toHaveClass('bg-white', 'text-neutral-900');
65
+ expect(card).not.toHaveClass('text-card-foreground');
66
+ });
55
67
  });
@@ -7,7 +7,9 @@ export interface CardProps extends React.ComponentProps<'div'> {
7
7
  * Background variant.
8
8
  * - `default`: uses the theme's `--card` token (e.g. Marfim on the Xertica theme).
9
9
  * - `white`: forces a solid white background regardless of the active theme,
10
- * for cards that should stand out from the theme's own card tone.
10
+ * for cards that should stand out from the theme's own card tone. Pairs
11
+ * with a fixed dark text color, since the theme's `--card-foreground` can
12
+ * be near-white in dark mode.
11
13
  */
12
14
  variant?: 'default' | 'white';
13
15
  }
@@ -30,8 +32,8 @@ function Card({ className, variant = 'default', ...props }: CardProps) {
30
32
  <div
31
33
  data-slot="card"
32
34
  className={cn(
33
- 'text-card-foreground flex flex-col gap-6 rounded-[var(--radius-card)]',
34
- variant === 'white' ? 'bg-white' : 'bg-card',
35
+ 'flex flex-col gap-6 rounded-[var(--radius-card)]',
36
+ variant === 'white' ? 'bg-white text-neutral-900' : 'bg-card text-card-foreground',
35
37
  className
36
38
  )}
37
39
  {...props}
@@ -1,10 +1,23 @@
1
1
  import { render, screen, fireEvent } from '@testing-library/react';
2
- import { describe, it, expect } from 'vitest';
2
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
3
3
  import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from './input-otp';
4
4
  import { OTPInputContext } from 'input-otp';
5
5
  import React from 'react';
6
6
 
7
7
  describe('InputOTP', () => {
8
+ // input-otp schedules internal setTimeout(0/10/50) calls on mount that it
9
+ // never cancels on unmount. Under real timers those fire after this file's
10
+ // jsdom environment is torn down, throwing `window is not defined` as an
11
+ // unhandled error in later test files. Fake timers keep them from ever
12
+ // firing for real.
13
+ beforeEach(() => {
14
+ vi.useFakeTimers();
15
+ });
16
+
17
+ afterEach(() => {
18
+ vi.useRealTimers();
19
+ });
20
+
8
21
  it('renders children correctly', () => {
9
22
  render(
10
23
  <OTPInputContext.Provider value={{ slots: [{ char: 'A' }] } as any}>
@@ -87,13 +87,13 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
87
87
  const gradientStart = isDark ? colors.gradientStartDark : colors.gradientStart;
88
88
  const gradientEnd = isDark ? colors.gradientEndDark : colors.gradientEnd;
89
89
 
90
- // --- Semantic Colors (Fixed Standard) ---
90
+ // --- Semantic Colors (Fixed Standard, overridable per theme in light mode) ---
91
91
  // Success (Green), Warning (Amber/Yellow), Info (Blue), Destructive (Red)
92
92
  const semantic = {
93
- success: isDark ? '#4ade80' : '#047857', // Green 400 / 700
94
- warning: isDark ? '#fbbf24' : '#b45309', // Amber 400 / 700
95
- info: isDark ? '#60a5fa' : '#1d4ed8', // Blue 400 / 700
96
- destructive: isDark ? '#f87171' : '#b91c1c', // Red 400 / 700
93
+ success: isDark ? '#4ade80' : colors.successColor || '#047857', // Green 400 / 700
94
+ warning: isDark ? '#fbbf24' : colors.warningColor || '#b45309', // Amber 400 / 700
95
+ info: isDark ? '#60a5fa' : colors.infoColor || '#1d4ed8', // Blue 400 / 700
96
+ destructive: isDark ? '#f87171' : colors.destructiveColor || '#b91c1c', // Red 400 / 700
97
97
  foreground: isDark ? '#050505' : '#ffffff',
98
98
  };
99
99
 
@@ -135,6 +135,7 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
135
135
  // 3b. Button shape overrides (identical in both modes; theme-specific, always emitted)
136
136
  cssVars.push(`--radius-button: ${colors.buttonRadius || '12px'}`);
137
137
  cssVars.push(`--button-stroke-width: ${colors.buttonStrokeWidth || '0px'}`);
138
+ cssVars.push(`--button-font-weight: ${colors.buttonFontWeight || '500'}`);
138
139
 
139
140
  // 4. Charts
140
141
  cssVars.push(`--chart-1: ${colors.chart1}`);
@@ -222,6 +223,9 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
222
223
  if (colors.inputBorderColor) {
223
224
  lightModeVars.push(`--input-border: ${colors.inputBorderColor}`);
224
225
  }
226
+ if (colors.outlineBorderColor) {
227
+ lightModeVars.push(`--outline-border: ${colors.outlineBorderColor}`);
228
+ }
225
229
 
226
230
  darkSurfaceVars.push(
227
231
  `--button-stroke-color: ${colors.buttonStrokeColorDark || colors.buttonStrokeColor || 'transparent'}`
@@ -49,11 +49,29 @@ export interface BrandColors {
49
49
  // always-visible stroke regardless of the surrounding background.
50
50
  inputBorderColor?: string;
51
51
 
52
+ // Optional: light-mode border color for the Button `outline` variant only
53
+ // — defaults to the shared --border token when unset. Same rationale as
54
+ // inputBorderColor: an outline button needs a stronger, always-visible
55
+ // contour when a theme makes --background and --muted identical, without
56
+ // affecting structural strokes elsewhere.
57
+ outlineBorderColor?: string;
58
+
59
+ // Optional: light-mode overrides for the semantic status colors (success,
60
+ // warning, info, destructive) used by alerts, badges, and status buttons —
61
+ // each defaults to the shared fixed value (see BrandColorsContext) when
62
+ // unset. Dark mode always uses the fixed value regardless of these, since
63
+ // no dark-mode-specific brand value has been defined yet.
64
+ successColor?: string;
65
+ warningColor?: string;
66
+ infoColor?: string;
67
+ destructiveColor?: string;
68
+
52
69
  // Optional: Button shape & stroke overrides (defaults preserve current look when unset)
53
70
  buttonRadius?: string;
54
71
  buttonStrokeWidth?: string;
55
72
  buttonStrokeColor?: string;
56
73
  buttonStrokeColorDark?: string;
74
+ buttonFontWeight?: string;
57
75
 
58
76
  // Optional: Button `secondary` variant colors (defaults to the shared --secondary token when unset)
59
77
  buttonSecondaryBg?: string;
@@ -158,10 +176,12 @@ export const colorThemes: ColorTheme[] = [
158
176
  darkMuted: '#26241f',
159
177
  darkBorder: '#38352c',
160
178
 
161
- gradientStart: '#1899AF', // Celeste
162
- gradientEnd: '#C45BAA', // Magenta
163
- gradientStartDark: '#126578',
164
- gradientEndDark: '#8F3F7D',
179
+ // Flat fill, same value on every stop — this theme uses solid ink
180
+ // colors, never gradients (Negro Xertica, matching sidebar/secondary-button).
181
+ gradientStart: '#1E1E1E',
182
+ gradientEnd: '#1E1E1E',
183
+ gradientStartDark: '#1E1E1E',
184
+ gradientEndDark: '#1E1E1E',
165
185
 
166
186
  chart1: '#3D63AC', // Azul
167
187
  chart2: '#1899AF', // Celeste
@@ -172,11 +192,18 @@ export const colorThemes: ColorTheme[] = [
172
192
  backgroundLight: '#E9E6DC', // page/shell background
173
193
  cardLight: '#FFFEF8', // Marfim — cards, header, assistant panel
174
194
  inputBorderColor: 'rgba(30, 30, 30, 0.4)', // Negro Xertica stroke — form fields only
195
+ outlineBorderColor: 'rgba(30, 30, 30, 0.4)', // Negro Xertica stroke — Button `outline` variant only
196
+
197
+ successColor: '#2E8B5A', // Verde
198
+ warningColor: '#E37744', // Naranja
199
+ infoColor: '#3D63AC', // Azul
200
+ destructiveColor: '#DE5B48', // Rojo Coral
175
201
 
176
202
  buttonRadius: '9999px',
177
- buttonStrokeWidth: '2px',
203
+ buttonStrokeWidth: '1px',
178
204
  buttonStrokeColor: '#1E1E1E',
179
205
  buttonStrokeColorDark: '#F2EDD8', // Crema
206
+ buttonFontWeight: '400',
180
207
 
181
208
  buttonSecondaryBg: '#1E1E1E',
182
209
  buttonSecondaryBgDark: '#1E1E1E',