@fluencypassdevs/cycle 1.15.1 → 1.16.1

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 (33) hide show
  1. package/bin/mcp.mjs +94 -0
  2. package/dist/chunk-DJ3DMPKK.js +56 -0
  3. package/dist/chunk-DJ3DMPKK.js.map +1 -0
  4. package/dist/chunk-G57PPNB3.js +74 -0
  5. package/dist/chunk-G57PPNB3.js.map +1 -0
  6. package/dist/chunk-JQRGW2RF.js +82 -0
  7. package/dist/chunk-JQRGW2RF.js.map +1 -0
  8. package/dist/chunk-WCDYTWKA.js +105 -0
  9. package/dist/chunk-WCDYTWKA.js.map +1 -0
  10. package/dist/chunk-YZNOE4TO.js +48 -0
  11. package/dist/chunk-YZNOE4TO.js.map +1 -0
  12. package/dist/marketing/buttons/cta-brand.d.ts +39 -0
  13. package/dist/marketing/buttons/cta-brand.js +8 -0
  14. package/dist/marketing/buttons/cta-brand.js.map +1 -0
  15. package/dist/marketing/buttons/index.d.ts +5 -0
  16. package/dist/marketing/buttons/index.js +9 -0
  17. package/dist/marketing/buttons/index.js.map +1 -0
  18. package/dist/marketing/buttons/link-brand.d.ts +39 -0
  19. package/dist/marketing/buttons/link-brand.js +6 -0
  20. package/dist/marketing/buttons/link-brand.js.map +1 -0
  21. package/dist/marketing/effects/hand-underline.d.ts +33 -0
  22. package/dist/marketing/effects/hand-underline.js +8 -0
  23. package/dist/marketing/effects/hand-underline.js.map +1 -0
  24. package/dist/marketing/effects/index.d.ts +3 -0
  25. package/dist/marketing/effects/index.js +5 -0
  26. package/dist/marketing/effects/magnetic-button.d.ts +36 -0
  27. package/dist/marketing/effects/magnetic-button.js +7 -0
  28. package/dist/marketing/effects/magnetic-button.js.map +1 -0
  29. package/dist/marketing/effects/rotating-word.d.ts +40 -0
  30. package/dist/marketing/effects/rotating-word.js +7 -0
  31. package/dist/marketing/effects/rotating-word.js.map +1 -0
  32. package/dist/styles/tokens.css +37 -8
  33. package/package.json +9 -1
package/bin/mcp.mjs CHANGED
@@ -1142,6 +1142,100 @@ import { Home, Search } from "@fluencypassdevs/cycle/icons/lucide"
1142
1142
  </Marquee>`,
1143
1143
  keywords: ["marketing", "landing", "effects", "marquee", "scroll", "horizontal", "logo", "cloud", "partner", "band"],
1144
1144
  },
1145
+ // ─── Marketing — Camada 2b (Buttons + Motion avançado) ──────────────────────
1146
+ {
1147
+ name: "MagneticButton",
1148
+ import: `import { MagneticButton } from "@fluencypassdevs/cycle/marketing/effects/magnetic-button"`,
1149
+ description: "MARKETING — Camada 2b. Wrapper que aplica efeito magnetico no filho (puxa em direcao ao cursor). Escreve transform direto no DOM via ref + RAF throttle. IntersectionObserver gating (rootMargin 200px). Drop-shadow rose via CSS vars --shadow-brand-glow-idle/-hover (consumer override). Skip total em prefers-reduced-motion e pointer:coarse (touch).",
1150
+ props: [
1151
+ { name: "strength", type: "number", default: "0.25", note: "multiplicador do offset" },
1152
+ { name: "radius", type: "number", default: "100", note: "distancia em px na qual o efeito ativa" },
1153
+ ],
1154
+ example: `<MagneticButton>
1155
+ <a href="#vendas">
1156
+ <Button variant="default">Quero saber mais</Button>
1157
+ </a>
1158
+ </MagneticButton>
1159
+
1160
+ <MagneticButton strength={0.4} radius={150}>
1161
+ <CustomElement />
1162
+ </MagneticButton>`,
1163
+ keywords: ["marketing", "landing", "effects", "magnetic", "hover", "cursor", "interactive", "premium"],
1164
+ },
1165
+ {
1166
+ name: "RotatingWord",
1167
+ import: `import { RotatingWord } from "@fluencypassdevs/cycle/marketing/effects/rotating-word"`,
1168
+ description: "MARKETING — Camada 2b. Typewriter de palavras em loop (typing → holding → erasing). Cor herda do parent via currentColor (consumer aplica text-[color]). aria-live polite + aria-atomic. Em prefers-reduced-motion, mostra so primeira palavra estatica sem cursor.",
1169
+ props: [
1170
+ { name: "words", type: "string[]", default: "-", note: "obrigatorio, palavras a ciclar" },
1171
+ { name: "typingMs", type: "number", default: "80", note: "velocidade de digitacao ms/caractere" },
1172
+ { name: "eraseMs", type: "number", default: "45", note: "velocidade de apagar" },
1173
+ { name: "holdMs", type: "number", default: "1500", note: "tempo segurando a palavra completa" },
1174
+ ],
1175
+ example: `<h1>
1176
+ Aprenda ingles com{" "}
1177
+ <RotatingWord
1178
+ className="text-[color:var(--brand-default)]"
1179
+ words={["IA", "proposito", "confianca"]}
1180
+ />
1181
+ </h1>`,
1182
+ keywords: ["marketing", "landing", "effects", "typewriter", "rotating", "word", "hero", "typing"],
1183
+ },
1184
+ {
1185
+ name: "HandUnderline",
1186
+ import: `import { HandUnderline } from "@fluencypassdevs/cycle/marketing/effects/hand-underline"`,
1187
+ description: "MARKETING — Camada 2b. Wrapper que desenha SVG underline em curva sutil abaixo do texto, animada via stroke-dasharray quando entra no viewport (threshold 0.6). Cor herda do parent (stroke=currentColor). Em prefers-reduced-motion, desenha completo direto sem animar.",
1188
+ props: [
1189
+ { name: "delay", type: "number", default: "600", note: "ms entre intersecao e inicio do draw" },
1190
+ { name: "duration", type: "number", default: "780", note: "duracao do draw em ms" },
1191
+ ],
1192
+ example: `<h2>
1193
+ Acelere o ingles do seu{" "}
1194
+ <HandUnderline className="text-[color:var(--brand-default)]">
1195
+ time corporativo
1196
+ </HandUnderline>
1197
+ </h2>`,
1198
+ keywords: ["marketing", "landing", "effects", "underline", "highlight", "draw", "scroll-trigger"],
1199
+ },
1200
+ {
1201
+ name: "CtaBrand",
1202
+ import: `import { CtaBrand } from "@fluencypassdevs/cycle/marketing/buttons/cta-brand"`,
1203
+ description: "MARKETING — Camada 2b. CTA principal de marketing — gradient brand rose (from-brand-soft to-brand-dark) + rounded-full + drop-shadow glow + brightness hover + MagneticButton embutido (sempre). Sempre renderiza como <a> com href. Pra acoes de JS sem URL, use Button core.",
1204
+ props: [
1205
+ { name: "href", type: "string", default: "-", note: "obrigatorio, sempre renderiza como <a>" },
1206
+ { name: "external", type: "boolean", default: "false", note: "target=_blank + rel noopener noreferrer" },
1207
+ { name: "size", type: '"md" | "lg"', default: '"lg"', note: "md=h-11 px-7. lg=h-13 px-10" },
1208
+ { name: "disableMagnetic", type: "boolean", default: "false", note: "escape hatch raro (popups/drawers)" },
1209
+ ],
1210
+ example: `<CtaBrand href="#vendas">
1211
+ Solicitar demonstracao
1212
+ </CtaBrand>
1213
+
1214
+ <CtaBrand href="https://app.fluencypass.com" external size="md">
1215
+ Acessar app
1216
+ </CtaBrand>`,
1217
+ keywords: ["marketing", "landing", "buttons", "cta", "brand", "gradient", "magnetic", "hero", "conversao"],
1218
+ },
1219
+ {
1220
+ name: "LinkBrand",
1221
+ import: `import { LinkBrand } from "@fluencypassdevs/cycle/marketing/buttons/link-brand"`,
1222
+ description: "MARKETING — Camada 2b. Link de texto discreto da marca — text-brand-default com hover pra brand-dark, transition-colors, focus-visible outline. Sem underline por default (consumer adiciona hover:underline se quiser). Renderiza como <a> (default) ou <button> via prop as.",
1223
+ props: [
1224
+ { name: "as", type: '"a" | "button"', default: '"a"' },
1225
+ { name: "href", type: "string", default: "-", note: "obrigatorio quando as=a" },
1226
+ { name: "external", type: "boolean", default: "false" },
1227
+ ],
1228
+ example: `<LinkBrand href="/precos">Ver planos</LinkBrand>
1229
+
1230
+ <LinkBrand href="https://twitter.com" external>
1231
+ Siga no Twitter
1232
+ </LinkBrand>
1233
+
1234
+ <LinkBrand as="button" onClick={() => openModal()}>
1235
+ Abrir filtros
1236
+ </LinkBrand>`,
1237
+ keywords: ["marketing", "landing", "buttons", "link", "brand", "nav", "footer", "inline"],
1238
+ },
1145
1239
  ]
1146
1240
 
1147
1241
  // ─── Design Tokens ─────────────────────────────────────────────────────────────
@@ -0,0 +1,56 @@
1
+ import { MagneticButton } from './chunk-WCDYTWKA.js';
2
+ import { cn } from './chunk-QTH4LSO6.js';
3
+ import { __objRest, __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
4
+ import * as React from 'react';
5
+ import { cva } from 'class-variance-authority';
6
+ import { jsx } from 'react/jsx-runtime';
7
+
8
+ var ctaBrandVariants = cva(
9
+ [
10
+ // Base
11
+ "inline-flex items-center justify-center gap-2",
12
+ "rounded-full font-semibold whitespace-nowrap",
13
+ "text-white border-0",
14
+ // Brand gradient (consome tokens --brand-soft/--brand-dark da Camada 2a)
15
+ "bg-gradient-to-b from-[color:var(--brand-soft)] to-[color:var(--brand-dark)]",
16
+ "hover:brightness-110 transition-[filter] duration-200",
17
+ // A11y focus
18
+ "outline-none focus-visible:outline-2 focus-visible:outline-offset-2",
19
+ "focus-visible:outline-[color:var(--brand-default)]"
20
+ ].join(" "),
21
+ {
22
+ variants: {
23
+ size: {
24
+ md: "h-11 px-7 text-sm",
25
+ lg: "h-13 px-10 text-base"
26
+ }
27
+ },
28
+ defaultVariants: {
29
+ size: "lg"
30
+ }
31
+ }
32
+ );
33
+ var CtaBrand = React.forwardRef(
34
+ function CtaBrand2(_a, ref) {
35
+ var _b = _a, { href, external = false, size, disableMagnetic = false, className, children } = _b, props = __objRest(_b, ["href", "external", "size", "disableMagnetic", "className", "children"]);
36
+ const anchor = /* @__PURE__ */ jsx(
37
+ "a",
38
+ __spreadProps(__spreadValues({
39
+ ref,
40
+ href,
41
+ target: external ? "_blank" : void 0,
42
+ rel: external ? "noopener noreferrer" : void 0,
43
+ "data-slot": "cta-brand",
44
+ className: cn(ctaBrandVariants({ size }), className)
45
+ }, props), {
46
+ children
47
+ })
48
+ );
49
+ if (disableMagnetic) return anchor;
50
+ return /* @__PURE__ */ jsx(MagneticButton, { children: anchor });
51
+ }
52
+ );
53
+
54
+ export { CtaBrand, ctaBrandVariants };
55
+ //# sourceMappingURL=chunk-DJ3DMPKK.js.map
56
+ //# sourceMappingURL=chunk-DJ3DMPKK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/marketing/buttons/cta-brand.tsx"],"names":["CtaBrand"],"mappings":";;;;;;;AAQA,IAAM,gBAAA,GAAmB,GAAA;AAAA,EACvB;AAAA;AAAA,IAEE,+CAAA;AAAA,IACA,8CAAA;AAAA,IACA,qBAAA;AAAA;AAAA,IAEA,8EAAA;AAAA,IACA,uDAAA;AAAA;AAAA,IAEA,qEAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,GAAG,CAAA;AAAA,EACV;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,mBAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACN,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM;AAAA;AACR;AAEJ;AAkCA,IAAM,QAAA,GAAiB,KAAA,CAAA,UAAA;AAAA,EACrB,SAASA,SAAAA,CACP,EAAA,EACA,GAAA,EACA;AAFA,IAAA,IAAA,EAAA,GAAA,EAAA,EAAE,QAAM,QAAA,GAAW,KAAA,EAAO,IAAA,EAAM,eAAA,GAAkB,OAAO,SAAA,EAAW,QAAA,EApExE,GAoEI,EAAA,EAAiF,kBAAjF,EAAA,EAAiF,CAA/E,QAAM,UAAA,EAAkB,MAAA,EAAM,mBAAyB,WAAA,EAAW,UAAA,CAAA,CAAA;AAGpE,IAAA,MAAM,MAAA,mBACJ,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA,aAAA,CAAA,cAAA,CAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA;AAAA,QACA,MAAA,EAAQ,WAAW,QAAA,GAAW,MAAA;AAAA,QAC9B,GAAA,EAAK,WAAW,qBAAA,GAAwB,MAAA;AAAA,QACxC,WAAA,EAAU,WAAA;AAAA,QACV,WAAW,EAAA,CAAG,gBAAA,CAAiB,EAAE,IAAA,EAAM,GAAG,SAAS;AAAA,OAAA,EAC/C,KAAA,CAAA,EAPL;AAAA,QASE;AAAA,OAAA;AAAA,KACH;AAGF,IAAA,IAAI,iBAAiB,OAAO,MAAA;AAC5B,IAAA,uBAAO,GAAA,CAAC,kBAAgB,QAAA,EAAA,MAAA,EAAO,CAAA;AAAA,EACjC;AACF","file":"chunk-DJ3DMPKK.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport { MagneticButton } from \"@/components/marketing/effects/magnetic-button\"\n\nconst ctaBrandVariants = cva(\n [\n // Base\n \"inline-flex items-center justify-center gap-2\",\n \"rounded-full font-semibold whitespace-nowrap\",\n \"text-white border-0\",\n // Brand gradient (consome tokens --brand-soft/--brand-dark da Camada 2a)\n \"bg-gradient-to-b from-[color:var(--brand-soft)] to-[color:var(--brand-dark)]\",\n \"hover:brightness-110 transition-[filter] duration-200\",\n // A11y focus\n \"outline-none focus-visible:outline-2 focus-visible:outline-offset-2\",\n \"focus-visible:outline-[color:var(--brand-default)]\",\n ].join(\" \"),\n {\n variants: {\n size: {\n md: \"h-11 px-7 text-sm\",\n lg: \"h-13 px-10 text-base\",\n },\n },\n defaultVariants: {\n size: \"lg\",\n },\n }\n)\n\nexport interface CtaBrandProps\n extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, \"size\">,\n VariantProps<typeof ctaBrandVariants> {\n /** URL destino. Sempre renderiza como `<a>`. Pra acao de JS sem URL, use `<Button>` core. */\n href: string\n /** Quando true, abre em nova aba com `rel=\"noopener noreferrer\"`. Default `false`. */\n external?: boolean\n /**\n * Escape hatch — desliga o efeito magnetic wrapper. Use em contextos onde o\n * movimento incomoda (dentro de Sheet/Drawer/popover). Default `false`\n * (magnetic SEMPRE ligado — esse e o ponto do CtaBrand).\n */\n disableMagnetic?: boolean\n}\n\n/**\n * CTA principal de marketing — botao com gradient brand rose (Fluencypass),\n * rounded-full, drop-shadow glow + brightness hover, e efeito magnetic\n * embutido (puxa em direcao ao cursor).\n *\n * Pra acoes de JS sem URL (submit de form, abrir modal etc), use o\n * `<Button>` core do Cycle em vez disso.\n *\n * @example\n * <CtaBrand href=\"#vendas\">\n * Solicitar demonstracao\n * </CtaBrand>\n *\n * <CtaBrand href=\"https://app.fluencypass.com\" external size=\"md\">\n * Acessar app\n * </CtaBrand>\n */\nconst CtaBrand = React.forwardRef<HTMLAnchorElement, CtaBrandProps>(\n function CtaBrand(\n { href, external = false, size, disableMagnetic = false, className, children, ...props },\n ref\n ) {\n const anchor = (\n <a\n ref={ref}\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n data-slot=\"cta-brand\"\n className={cn(ctaBrandVariants({ size }), className)}\n {...props}\n >\n {children}\n </a>\n )\n\n if (disableMagnetic) return anchor\n return <MagneticButton>{anchor}</MagneticButton>\n }\n)\n\nexport { CtaBrand, ctaBrandVariants }\n"]}
@@ -0,0 +1,74 @@
1
+ import { useReducedMotion } from './chunk-T7EIAVSF.js';
2
+ import { useIntersection } from './chunk-OHRYR7PN.js';
3
+ import { cn } from './chunk-QTH4LSO6.js';
4
+ import { __objRest, __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
5
+ import * as React from 'react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var PATH_LENGTH = 240;
9
+ var STROKE_WIDTH = 2.2;
10
+ var SVG_PATH = "M 2 9 C 30 4, 70 12, 110 7 S 180 4, 198 8";
11
+ var SVG_VIEWBOX = "0 0 200 14";
12
+ var HandUnderline = React.forwardRef(
13
+ function HandUnderline2(_a, forwardedRef) {
14
+ var _b = _a, { delay = 600, duration = 780, className, children } = _b, props = __objRest(_b, ["delay", "duration", "className", "children"]);
15
+ const reduced = useReducedMotion();
16
+ const { ref, isVisible } = useIntersection({
17
+ threshold: 0.6,
18
+ rootMargin: "0px",
19
+ startVisible: reduced
20
+ });
21
+ const [draw, setDraw] = React.useState(reduced);
22
+ React.useImperativeHandle(forwardedRef, () => ref.current, [ref]);
23
+ React.useEffect(() => {
24
+ if (!isVisible || draw) return;
25
+ if (reduced) {
26
+ setDraw(true);
27
+ return;
28
+ }
29
+ const id = window.setTimeout(() => setDraw(true), delay);
30
+ return () => window.clearTimeout(id);
31
+ }, [isVisible, draw, delay, reduced]);
32
+ return /* @__PURE__ */ jsxs(
33
+ "span",
34
+ __spreadProps(__spreadValues({
35
+ ref,
36
+ "data-slot": "hand-underline",
37
+ className: cn("relative inline-block whitespace-nowrap", className)
38
+ }, props), {
39
+ children: [
40
+ children,
41
+ /* @__PURE__ */ jsx(
42
+ "svg",
43
+ {
44
+ "aria-hidden": "true",
45
+ viewBox: SVG_VIEWBOX,
46
+ preserveAspectRatio: "none",
47
+ className: "absolute left-0 right-0 -bottom-1 w-full pointer-events-none",
48
+ style: { height: "0.32em" },
49
+ children: /* @__PURE__ */ jsx(
50
+ "path",
51
+ {
52
+ d: SVG_PATH,
53
+ fill: "none",
54
+ stroke: "currentColor",
55
+ strokeWidth: STROKE_WIDTH,
56
+ strokeLinecap: "round",
57
+ style: {
58
+ strokeDasharray: PATH_LENGTH,
59
+ strokeDashoffset: draw ? 0 : PATH_LENGTH,
60
+ transition: reduced ? "none" : `stroke-dashoffset ${duration}ms cubic-bezier(0.22, 1, 0.36, 1)`
61
+ }
62
+ }
63
+ )
64
+ }
65
+ )
66
+ ]
67
+ })
68
+ );
69
+ }
70
+ );
71
+
72
+ export { HandUnderline };
73
+ //# sourceMappingURL=chunk-G57PPNB3.js.map
74
+ //# sourceMappingURL=chunk-G57PPNB3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/marketing/effects/hand-underline.tsx"],"names":["HandUnderline"],"mappings":";;;;;;;AAiBA,IAAM,WAAA,GAAc,GAAA;AACpB,IAAM,YAAA,GAAe,GAAA;AACrB,IAAM,QAAA,GAAW,2CAAA;AACjB,IAAM,WAAA,GAAc,YAAA;AAuBpB,IAAM,aAAA,GAAsB,KAAA,CAAA,UAAA;AAAA,EAC1B,SAASA,cAAAA,CACP,EAAA,EACA,YAAA,EACA;AAFA,IAAA,IAAA,EAAA,GAAA,EAAA,EAAE,EAAA,KAAA,GAAQ,GAAA,EAAK,QAAA,GAAW,GAAA,EAAK,SAAA,EAAW,QAAA,EA7C9C,GA6CI,EAAA,EAAuD,KAAA,GAAA,SAAA,CAAvD,EAAA,EAAuD,CAArD,OAAA,EAAa,YAAgB,WAAA,EAAW,UAAA,CAAA,CAAA;AAG1C,IAAA,MAAM,UAAU,gBAAA,EAAiB;AACjC,IAAA,MAAM,EAAE,GAAA,EAAK,SAAA,EAAU,GAAI,eAAA,CAAiC;AAAA,MAC1D,SAAA,EAAW,GAAA;AAAA,MACX,UAAA,EAAY,KAAA;AAAA,MACZ,YAAA,EAAc;AAAA,KACf,CAAA;AACD,IAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAU,eAAS,OAAO,CAAA;AAG9C,IAAM,0BAAoB,YAAA,EAAc,MAAM,IAAI,OAAA,EAA4B,CAAC,GAAG,CAAC,CAAA;AAEnF,IAAM,gBAAU,MAAM;AACpB,MAAA,IAAI,CAAC,aAAa,IAAA,EAAM;AACxB,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,QAAA;AAAA,MACF;AACA,MAAA,MAAM,KAAK,MAAA,CAAO,UAAA,CAAW,MAAM,OAAA,CAAQ,IAAI,GAAG,KAAK,CAAA;AACvD,MAAA,OAAO,MAAM,MAAA,CAAO,YAAA,CAAa,EAAE,CAAA;AAAA,IACrC,GAAG,CAAC,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,OAAO,CAAC,CAAA;AAEpC,IAAA,uBACE,IAAA;AAAA,MAAC,MAAA;AAAA,MAAA,aAAA,CAAA,cAAA,CAAA;AAAA,QACC,GAAA;AAAA,QACA,WAAA,EAAU,gBAAA;AAAA,QACV,SAAA,EAAW,EAAA,CAAG,yCAAA,EAA2C,SAAS;AAAA,OAAA,EAC9D,KAAA,CAAA,EAJL;AAAA,QAME,QAAA,EAAA;AAAA,UAAA,QAAA;AAAA,0BACD,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,aAAA,EAAY,MAAA;AAAA,cACZ,OAAA,EAAS,WAAA;AAAA,cACT,mBAAA,EAAoB,MAAA;AAAA,cACpB,SAAA,EAAU,8DAAA;AAAA,cACV,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,EAAS;AAAA,cAE1B,QAAA,kBAAA,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,CAAA,EAAG,QAAA;AAAA,kBACH,IAAA,EAAK,MAAA;AAAA,kBACL,MAAA,EAAO,cAAA;AAAA,kBACP,WAAA,EAAa,YAAA;AAAA,kBACb,aAAA,EAAc,OAAA;AAAA,kBACd,KAAA,EAAO;AAAA,oBACL,eAAA,EAAiB,WAAA;AAAA,oBACjB,gBAAA,EAAkB,OAAO,CAAA,GAAI,WAAA;AAAA,oBAC7B,UAAA,EAAY,OAAA,GACR,MAAA,GACA,CAAA,kBAAA,EAAqB,QAAQ,CAAA,iCAAA;AAAA;AACnC;AAAA;AACF;AAAA;AACF;AAAA,OAAA;AAAA,KACF;AAAA,EAEJ;AACF","file":"chunk-G57PPNB3.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { useReducedMotion } from \"@/hooks/use-reduced-motion\"\nimport { useIntersection } from \"@/hooks/use-intersection\"\n\nexport interface HandUnderlineProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /** Delay em ms entre intersecao no viewport e inicio do draw. Default `600`. */\n delay?: number\n /** Duracao do draw em ms (stroke-dashoffset 240 → 0). Default `780`. */\n duration?: number\n children: React.ReactNode\n}\n\nconst PATH_LENGTH = 240\nconst STROKE_WIDTH = 2.2\nconst SVG_PATH = \"M 2 9 C 30 4, 70 12, 110 7 S 180 4, 198 8\"\nconst SVG_VIEWBOX = \"0 0 200 14\"\n\n/**\n * Wrapper que desenha um underline SVG em curva sutil abaixo do texto, animado\n * via stroke-dasharray quando entra no viewport. Util pra dar enfase em\n * palavras-chave em headings sem usar bold/color.\n *\n * Cor do stroke: `currentColor` — herda do parent. Wrappa com\n * `<span className=\"text-[color:var(--brand-default)]\">` ou outra cor.\n *\n * Acessibilidade:\n * - SVG `aria-hidden` (decorativo).\n * - `prefers-reduced-motion: reduce` — desenha o underline completo direto,\n * sem animar.\n *\n * @example\n * <h2>\n * Acelere o ingles do seu{\" \"}\n * <HandUnderline className=\"text-[color:var(--brand-default)]\">\n * time corporativo\n * </HandUnderline>\n * </h2>\n */\nconst HandUnderline = React.forwardRef<HTMLSpanElement, HandUnderlineProps>(\n function HandUnderline(\n { delay = 600, duration = 780, className, children, ...props },\n forwardedRef\n ) {\n const reduced = useReducedMotion()\n const { ref, isVisible } = useIntersection<HTMLSpanElement>({\n threshold: 0.6,\n rootMargin: \"0px\",\n startVisible: reduced,\n })\n const [draw, setDraw] = React.useState(reduced)\n\n // Combina forwarded ref + ref interno do useIntersection.\n React.useImperativeHandle(forwardedRef, () => ref.current as HTMLSpanElement, [ref])\n\n React.useEffect(() => {\n if (!isVisible || draw) return\n if (reduced) {\n setDraw(true)\n return\n }\n const id = window.setTimeout(() => setDraw(true), delay)\n return () => window.clearTimeout(id)\n }, [isVisible, draw, delay, reduced])\n\n return (\n <span\n ref={ref}\n data-slot=\"hand-underline\"\n className={cn(\"relative inline-block whitespace-nowrap\", className)}\n {...props}\n >\n {children}\n <svg\n aria-hidden=\"true\"\n viewBox={SVG_VIEWBOX}\n preserveAspectRatio=\"none\"\n className=\"absolute left-0 right-0 -bottom-1 w-full pointer-events-none\"\n style={{ height: \"0.32em\" }}\n >\n <path\n d={SVG_PATH}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n style={{\n strokeDasharray: PATH_LENGTH,\n strokeDashoffset: draw ? 0 : PATH_LENGTH,\n transition: reduced\n ? \"none\"\n : `stroke-dashoffset ${duration}ms cubic-bezier(0.22, 1, 0.36, 1)`,\n }}\n />\n </svg>\n </span>\n )\n }\n)\n\nexport { HandUnderline }\n"]}
@@ -0,0 +1,82 @@
1
+ import { useReducedMotion } from './chunk-T7EIAVSF.js';
2
+ import { cn } from './chunk-QTH4LSO6.js';
3
+ import { __objRest, __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
4
+ import * as React from 'react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var RotatingWord = React.forwardRef(
8
+ function RotatingWord2(_a, ref) {
9
+ var _b = _a, { words, typingMs = 80, eraseMs = 45, holdMs = 1500, className } = _b, props = __objRest(_b, ["words", "typingMs", "eraseMs", "holdMs", "className"]);
10
+ var _a2;
11
+ const reduced = useReducedMotion();
12
+ const [wordIdx, setWordIdx] = React.useState(0);
13
+ const [chars, setChars] = React.useState(0);
14
+ const [phase, setPhase] = React.useState("typing");
15
+ const current = (_a2 = words[wordIdx]) != null ? _a2 : "";
16
+ React.useEffect(() => {
17
+ if (reduced) return;
18
+ let timer;
19
+ if (phase === "typing") {
20
+ if (chars < current.length) {
21
+ timer = window.setTimeout(() => setChars((c) => c + 1), typingMs);
22
+ } else {
23
+ timer = window.setTimeout(() => setPhase("erasing"), holdMs);
24
+ }
25
+ } else if (phase === "erasing") {
26
+ if (chars > 0) {
27
+ timer = window.setTimeout(() => setChars((c) => c - 1), eraseMs);
28
+ } else {
29
+ setWordIdx((i) => (i + 1) % words.length);
30
+ setPhase("typing");
31
+ }
32
+ }
33
+ return () => {
34
+ if (timer) window.clearTimeout(timer);
35
+ };
36
+ }, [chars, phase, current, typingMs, eraseMs, holdMs, words.length, reduced]);
37
+ if (reduced) {
38
+ return /* @__PURE__ */ jsx(
39
+ "span",
40
+ __spreadProps(__spreadValues({
41
+ ref,
42
+ "data-slot": "rotating-word",
43
+ className: cn(className),
44
+ "aria-live": "polite"
45
+ }, props), {
46
+ children: words[0]
47
+ })
48
+ );
49
+ }
50
+ const displayed = current.substring(0, chars);
51
+ return /* @__PURE__ */ jsxs(
52
+ "span",
53
+ __spreadProps(__spreadValues({
54
+ ref,
55
+ "data-slot": "rotating-word",
56
+ className: cn("whitespace-nowrap", className),
57
+ "aria-live": "polite",
58
+ "aria-atomic": "true"
59
+ }, props), {
60
+ children: [
61
+ displayed,
62
+ /* @__PURE__ */ jsx(
63
+ "span",
64
+ {
65
+ "aria-hidden": "true",
66
+ className: "cycle-rotating-cursor inline-block align-baseline bg-current ml-[0.05em]",
67
+ style: {
68
+ width: "0.06em",
69
+ height: "0.82em",
70
+ transform: "translateY(0.08em)"
71
+ }
72
+ }
73
+ )
74
+ ]
75
+ })
76
+ );
77
+ }
78
+ );
79
+
80
+ export { RotatingWord };
81
+ //# sourceMappingURL=chunk-JQRGW2RF.js.map
82
+ //# sourceMappingURL=chunk-JQRGW2RF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/marketing/effects/rotating-word.tsx"],"names":["RotatingWord","_a"],"mappings":"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","file":"chunk-JQRGW2RF.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { useReducedMotion } from \"@/hooks/use-reduced-motion\"\n\nexport interface RotatingWordProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /** Lista de palavras a ciclar (typewriter). */\n words: string[]\n /** Velocidade de digitacao em ms por caractere. Default `80`. */\n typingMs?: number\n /** Velocidade de apagar em ms por caractere. Default `45`. */\n eraseMs?: number\n /** Tempo segurando a palavra completa em ms. Default `1500`. */\n holdMs?: number\n}\n\ntype Phase = \"typing\" | \"holding\" | \"erasing\"\n\n/**\n * Typewriter de palavras em loop. Util em headings de hero para destacar\n * caracteristicas ou propostas de valor que rotacionam.\n *\n * State machine: `typing → holding → erasing → next word`.\n *\n * Cor herda do parent via `currentColor` — wrappa com `text-[color]` ou\n * `text-foreground` no consumer pra controlar. O cursor blink usa a mesma\n * cor do texto (`bg-current`).\n *\n * Acessibilidade:\n * - `aria-live=\"polite\"` + `aria-atomic=\"true\"` — screen reader anuncia a\n * palavra completa apos cada ciclo, sem ler caractere por caractere.\n * - `prefers-reduced-motion: reduce` — renderiza so a primeira palavra,\n * estatica, sem cursor.\n *\n * @example\n * <h1>\n * Aprenda ingles com{\" \"}\n * <RotatingWord\n * className=\"text-[color:var(--brand-default)]\"\n * words={[\"IA\", \"proposito\", \"confianca\"]}\n * />\n * </h1>\n */\nconst RotatingWord = React.forwardRef<HTMLSpanElement, RotatingWordProps>(\n function RotatingWord(\n { words, typingMs = 80, eraseMs = 45, holdMs = 1500, className, ...props },\n ref\n ) {\n const reduced = useReducedMotion()\n const [wordIdx, setWordIdx] = React.useState(0)\n const [chars, setChars] = React.useState(0)\n const [phase, setPhase] = React.useState<Phase>(\"typing\")\n\n const current = words[wordIdx] ?? \"\"\n\n React.useEffect(() => {\n if (reduced) return\n\n let timer: number | undefined\n\n if (phase === \"typing\") {\n if (chars < current.length) {\n timer = window.setTimeout(() => setChars((c) => c + 1), typingMs)\n } else {\n timer = window.setTimeout(() => setPhase(\"erasing\"), holdMs)\n }\n } else if (phase === \"erasing\") {\n if (chars > 0) {\n timer = window.setTimeout(() => setChars((c) => c - 1), eraseMs)\n } else {\n setWordIdx((i) => (i + 1) % words.length)\n setPhase(\"typing\")\n }\n }\n\n return () => {\n if (timer) window.clearTimeout(timer)\n }\n }, [chars, phase, current, typingMs, eraseMs, holdMs, words.length, reduced])\n\n if (reduced) {\n return (\n <span\n ref={ref}\n data-slot=\"rotating-word\"\n className={cn(className)}\n aria-live=\"polite\"\n {...props}\n >\n {words[0]}\n </span>\n )\n }\n\n const displayed = current.substring(0, chars)\n\n return (\n <span\n ref={ref}\n data-slot=\"rotating-word\"\n className={cn(\"whitespace-nowrap\", className)}\n aria-live=\"polite\"\n aria-atomic=\"true\"\n {...props}\n >\n {displayed}\n <span\n aria-hidden=\"true\"\n className=\"cycle-rotating-cursor inline-block align-baseline bg-current ml-[0.05em]\"\n style={{\n width: \"0.06em\",\n height: \"0.82em\",\n transform: \"translateY(0.08em)\",\n }}\n />\n </span>\n )\n }\n)\n\nexport { RotatingWord }\n"]}
@@ -0,0 +1,105 @@
1
+ import { useReducedMotion } from './chunk-T7EIAVSF.js';
2
+ import { cn } from './chunk-QTH4LSO6.js';
3
+ import { __objRest, __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
4
+ import * as React from 'react';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ var MagneticButton = React.forwardRef(
8
+ function MagneticButton2(_a, forwardedRef) {
9
+ var _b = _a, { strength = 0.25, radius = 100, className, style, children } = _b, props = __objRest(_b, ["strength", "radius", "className", "style", "children"]);
10
+ const innerRef = React.useRef(null);
11
+ const ref = forwardedRef != null ? forwardedRef : innerRef;
12
+ const hoveredRef = React.useRef(false);
13
+ const reduced = useReducedMotion();
14
+ const [hovered, setHovered] = React.useState(false);
15
+ React.useEffect(() => {
16
+ const el = ref.current;
17
+ if (!el) return;
18
+ const s = hovered ? 1.03 : 1;
19
+ const cur = el.style.transform;
20
+ if (cur && /scale\([^)]+\)/.test(cur)) {
21
+ el.style.transform = cur.replace(/scale\([^)]+\)/, `scale(${s})`);
22
+ } else {
23
+ el.style.transform = `translate3d(0,0,0) scale(${s})`;
24
+ }
25
+ }, [hovered, ref]);
26
+ React.useEffect(() => {
27
+ if (reduced) return;
28
+ if (typeof window === "undefined") return;
29
+ if (window.matchMedia("(pointer: coarse)").matches) return;
30
+ const el = ref.current;
31
+ if (!el) return;
32
+ let isVisible = false;
33
+ let rafId = 0;
34
+ let lastX = 0;
35
+ let lastY = 0;
36
+ const io = new IntersectionObserver(
37
+ ([entry]) => {
38
+ var _a2;
39
+ isVisible = (_a2 = entry == null ? void 0 : entry.isIntersecting) != null ? _a2 : false;
40
+ if (!isVisible) {
41
+ el.style.transform = `translate3d(0,0,0) scale(${hoveredRef.current ? 1.03 : 1})`;
42
+ }
43
+ },
44
+ { rootMargin: "200px" }
45
+ );
46
+ io.observe(el);
47
+ function flush() {
48
+ rafId = 0;
49
+ if (!isVisible) return;
50
+ const cur = ref.current;
51
+ if (!cur) return;
52
+ const rect = cur.getBoundingClientRect();
53
+ const cx = rect.left + rect.width / 2;
54
+ const cy = rect.top + rect.height / 2;
55
+ const dx = lastX - cx;
56
+ const dy = lastY - cy;
57
+ const dist = Math.hypot(dx, dy);
58
+ const tx = dist < radius ? dx * strength : 0;
59
+ const ty = dist < radius ? dy * strength : 0;
60
+ const s = hoveredRef.current ? 1.03 : 1;
61
+ cur.style.transform = `translate3d(${tx}px, ${ty}px, 0) scale(${s})`;
62
+ }
63
+ function onMove(e) {
64
+ if (!isVisible) return;
65
+ lastX = e.clientX;
66
+ lastY = e.clientY;
67
+ if (rafId) return;
68
+ rafId = requestAnimationFrame(flush);
69
+ }
70
+ window.addEventListener("mousemove", onMove, { passive: true });
71
+ return () => {
72
+ window.removeEventListener("mousemove", onMove);
73
+ io.disconnect();
74
+ if (rafId) cancelAnimationFrame(rafId);
75
+ };
76
+ }, [reduced, strength, radius, ref]);
77
+ return /* @__PURE__ */ jsx(
78
+ "div",
79
+ __spreadProps(__spreadValues({
80
+ ref,
81
+ "data-slot": "magnetic-button",
82
+ onMouseEnter: () => {
83
+ hoveredRef.current = true;
84
+ setHovered(true);
85
+ },
86
+ onMouseLeave: () => {
87
+ hoveredRef.current = false;
88
+ setHovered(false);
89
+ },
90
+ className: cn("inline-block", className),
91
+ style: __spreadValues({
92
+ transition: "transform 0.25s cubic-bezier(0.22, 1, 0.36, 1)",
93
+ willChange: "transform",
94
+ filter: hovered ? "var(--shadow-brand-glow-hover)" : "var(--shadow-brand-glow-idle)"
95
+ }, style)
96
+ }, props), {
97
+ children
98
+ })
99
+ );
100
+ }
101
+ );
102
+
103
+ export { MagneticButton };
104
+ //# sourceMappingURL=chunk-WCDYTWKA.js.map
105
+ //# sourceMappingURL=chunk-WCDYTWKA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/marketing/effects/magnetic-button.tsx"],"names":["MagneticButton","_a"],"mappings":"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","file":"chunk-WCDYTWKA.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { useReducedMotion } from \"@/hooks/use-reduced-motion\"\n\nexport interface MagneticButtonProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Multiplicador do offset (quanto o filho \"puxa\" em direcao ao cursor). Default `0.25`. */\n strength?: number\n /** Distancia em px na qual o efeito ativa. Default `100`. */\n radius?: number\n children: React.ReactNode\n}\n\n/**\n * Wrapper que aplica efeito magnetico no filho (puxa em direcao ao cursor\n * quando proximo). Inspirado em CTAs premium de landing pages.\n *\n * Otimizacoes de performance:\n * - Escreve `transform` direto no DOM via ref (sem React state na hot path).\n * - Throttle com `requestAnimationFrame` (max 1 update por frame).\n * - `IntersectionObserver` gating: so processa `mousemove` quando visivel\n * (rootMargin 200px pra suavidade na entrada).\n *\n * Acessibilidade:\n * - Skip total se `prefers-reduced-motion: reduce`.\n * - Skip total em `pointer: coarse` (touch devices) — sem cursor real.\n *\n * O drop-shadow usa CSS vars (`--shadow-brand-glow-idle/-hover`) — consumer\n * pode override pro brand proprio sem mexer no componente.\n *\n * @example\n * <MagneticButton>\n * <a href=\"#vendas\">\n * <Button variant=\"default\">CTA principal</Button>\n * </a>\n * </MagneticButton>\n */\nconst MagneticButton = React.forwardRef<HTMLDivElement, MagneticButtonProps>(\n function MagneticButton(\n { strength = 0.25, radius = 100, className, style, children, ...props },\n forwardedRef\n ) {\n const innerRef = React.useRef<HTMLDivElement>(null)\n const ref = (forwardedRef as React.RefObject<HTMLDivElement>) ?? innerRef\n const hoveredRef = React.useRef(false)\n const reduced = useReducedMotion()\n const [hovered, setHovered] = React.useState(false)\n\n // Sincroniza scale quando hover muda — sem tocar no offset do mousemove.\n React.useEffect(() => {\n const el = ref.current\n if (!el) return\n const s = hovered ? 1.03 : 1\n const cur = el.style.transform\n if (cur && /scale\\([^)]+\\)/.test(cur)) {\n el.style.transform = cur.replace(/scale\\([^)]+\\)/, `scale(${s})`)\n } else {\n el.style.transform = `translate3d(0,0,0) scale(${s})`\n }\n }, [hovered, ref])\n\n React.useEffect(() => {\n if (reduced) return\n if (typeof window === \"undefined\") return\n if (window.matchMedia(\"(pointer: coarse)\").matches) return\n\n const el = ref.current\n if (!el) return\n\n let isVisible = false\n let rafId = 0\n let lastX = 0\n let lastY = 0\n\n const io = new IntersectionObserver(\n ([entry]) => {\n isVisible = entry?.isIntersecting ?? false\n if (!isVisible) {\n el.style.transform = `translate3d(0,0,0) scale(${\n hoveredRef.current ? 1.03 : 1\n })`\n }\n },\n { rootMargin: \"200px\" }\n )\n io.observe(el)\n\n function flush() {\n rafId = 0\n if (!isVisible) return\n const cur = ref.current\n if (!cur) return\n const rect = cur.getBoundingClientRect()\n const cx = rect.left + rect.width / 2\n const cy = rect.top + rect.height / 2\n const dx = lastX - cx\n const dy = lastY - cy\n const dist = Math.hypot(dx, dy)\n const tx = dist < radius ? dx * strength : 0\n const ty = dist < radius ? dy * strength : 0\n const s = hoveredRef.current ? 1.03 : 1\n cur.style.transform = `translate3d(${tx}px, ${ty}px, 0) scale(${s})`\n }\n\n function onMove(e: MouseEvent) {\n if (!isVisible) return\n lastX = e.clientX\n lastY = e.clientY\n if (rafId) return\n rafId = requestAnimationFrame(flush)\n }\n\n window.addEventListener(\"mousemove\", onMove, { passive: true })\n return () => {\n window.removeEventListener(\"mousemove\", onMove)\n io.disconnect()\n if (rafId) cancelAnimationFrame(rafId)\n }\n }, [reduced, strength, radius, ref])\n\n return (\n <div\n ref={ref}\n data-slot=\"magnetic-button\"\n onMouseEnter={() => {\n hoveredRef.current = true\n setHovered(true)\n }}\n onMouseLeave={() => {\n hoveredRef.current = false\n setHovered(false)\n }}\n className={cn(\"inline-block\", className)}\n style={{\n transition: \"transform 0.25s cubic-bezier(0.22, 1, 0.36, 1)\",\n willChange: \"transform\",\n filter: hovered\n ? \"var(--shadow-brand-glow-hover)\"\n : \"var(--shadow-brand-glow-idle)\",\n ...style,\n }}\n {...props}\n >\n {children}\n </div>\n )\n }\n)\n\nexport { MagneticButton }\n"]}
@@ -0,0 +1,48 @@
1
+ import { cn } from './chunk-QTH4LSO6.js';
2
+ import { __objRest, __spreadProps, __spreadValues } from './chunk-2NMEKWO5.js';
3
+ import * as React from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var BASE_CLASSES = [
7
+ "text-[color:var(--brand-default)]",
8
+ "hover:text-[color:var(--brand-dark)]",
9
+ "underline-offset-4 transition-colors",
10
+ "outline-none focus-visible:outline-2 focus-visible:outline-offset-2",
11
+ "focus-visible:outline-[color:var(--brand-default)] rounded-sm"
12
+ ].join(" ");
13
+ var LinkBrand = React.forwardRef(
14
+ function LinkBrand2(props, ref) {
15
+ if (props.as === "button") {
16
+ const _a = props, { as: _as, className: className2, children: children2 } = _a, rest2 = __objRest(_a, ["as", "className", "children"]);
17
+ return /* @__PURE__ */ jsx(
18
+ "button",
19
+ __spreadProps(__spreadValues({
20
+ ref,
21
+ type: "button",
22
+ "data-slot": "link-brand",
23
+ className: cn(BASE_CLASSES, className2)
24
+ }, rest2), {
25
+ children: children2
26
+ })
27
+ );
28
+ }
29
+ const _b = props, { href, external = false, className, children } = _b, rest = __objRest(_b, ["href", "external", "className", "children"]);
30
+ return /* @__PURE__ */ jsx(
31
+ "a",
32
+ __spreadProps(__spreadValues({
33
+ ref,
34
+ href,
35
+ target: external ? "_blank" : void 0,
36
+ rel: external ? "noopener noreferrer" : void 0,
37
+ "data-slot": "link-brand",
38
+ className: cn(BASE_CLASSES, className)
39
+ }, rest), {
40
+ children
41
+ })
42
+ );
43
+ }
44
+ );
45
+
46
+ export { LinkBrand };
47
+ //# sourceMappingURL=chunk-YZNOE4TO.js.map
48
+ //# sourceMappingURL=chunk-YZNOE4TO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/marketing/buttons/link-brand.tsx"],"names":["LinkBrand","className","children","rest"],"mappings":";;;;;AAMA,IAAM,YAAA,GAAe;AAAA,EACnB,mCAAA;AAAA,EACA,sCAAA;AAAA,EACA,sCAAA;AAAA,EACA,qEAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,GAAG,CAAA;AAuCV,IAAM,SAAA,GAAkB,KAAA,CAAA,UAAA;AAAA,EACtB,SAASA,UAAAA,CAAU,KAAA,EAAO,GAAA,EAAK;AAC7B,IAAA,IAAI,KAAA,CAAM,OAAO,QAAA,EAAU;AACzB,MAAA,MAAkD,EAAA,GAAA,KAAA,EAA1C,EAAA,EAAA,EAAI,GAAA,EAAK,SAAA,EAAAC,YAAW,QAAA,EAAAC,SAAAA,EAtDlC,GAsDwD,EAAA,EAATC,KAAAA,GAAA,SAAA,CAAS,EAAA,EAAT,CAAjC,MAAS,WAAA,EAAW,UAAA,CAAA,CAAA;AAC5B,MAAA,uBACE,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA,aAAA,CAAA,cAAA,CAAA;AAAA,UACC,GAAA;AAAA,UACA,IAAA,EAAK,QAAA;AAAA,UACL,WAAA,EAAU,YAAA;AAAA,UACV,SAAA,EAAW,EAAA,CAAG,YAAA,EAAcF,UAAS;AAAA,SAAA,EACjCE,KAAAA,CAAAA,EALL;AAAA,UAOE,QAAA,EAAAD;AAAA,SAAA;AAAA,OACH;AAAA,IAEJ;AAEA,IAAA,MAAiE,EAAA,GAAA,KAAA,EAAzD,EAAA,IAAA,EAAM,QAAA,GAAW,KAAA,EAAO,SAAA,EAAW,QAAA,EApE/C,GAoEqE,EAAA,EAAT,IAAA,GAAA,SAAA,CAAS,EAAA,EAAT,CAAhD,MAAA,EAAM,YAAkB,WAAA,EAAW,UAAA,CAAA,CAAA;AAC3C,IAAA,uBACE,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA,aAAA,CAAA,cAAA,CAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA;AAAA,QACA,MAAA,EAAQ,WAAW,QAAA,GAAW,MAAA;AAAA,QAC9B,GAAA,EAAK,WAAW,qBAAA,GAAwB,MAAA;AAAA,QACxC,WAAA,EAAU,YAAA;AAAA,QACV,SAAA,EAAW,EAAA,CAAG,YAAA,EAAc,SAAS;AAAA,OAAA,EACjC,IAAA,CAAA,EAPL;AAAA,QASE;AAAA,OAAA;AAAA,KACH;AAAA,EAEJ;AACF","file":"chunk-YZNOE4TO.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst BASE_CLASSES = [\n \"text-[color:var(--brand-default)]\",\n \"hover:text-[color:var(--brand-dark)]\",\n \"underline-offset-4 transition-colors\",\n \"outline-none focus-visible:outline-2 focus-visible:outline-offset-2\",\n \"focus-visible:outline-[color:var(--brand-default)] rounded-sm\",\n].join(\" \")\n\ntype LinkBrandAnchorProps = {\n /** Renderiza como `<a>` (default). */\n as?: \"a\"\n /** URL destino. */\n href: string\n external?: boolean\n} & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, \"as\" | \"href\">\n\ntype LinkBrandButtonProps = {\n /** Renderiza como `<button>` quando a acao e JS (sem URL). */\n as: \"button\"\n href?: never\n external?: never\n} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"as\">\n\nexport type LinkBrandProps = LinkBrandAnchorProps | LinkBrandButtonProps\n\n/**\n * Link de texto discreto da marca — `text-brand-default` com hover pra\n * `text-brand-dark`. Usado em nav, footer e texto inline.\n *\n * Sem underline por default (visual mais clean). Pra ter underline, passe\n * `className=\"hover:underline\"` ou `underline` no consumer.\n *\n * Pode renderizar como `<a>` (default) ou `<button>` via prop `as`.\n *\n * @example\n * <LinkBrand href=\"/precos\">Ver planos</LinkBrand>\n *\n * <LinkBrand href=\"https://twitter.com\" external>\n * Siga no Twitter\n * </LinkBrand>\n *\n * <LinkBrand as=\"button\" onClick={() => openModal()}>\n * Abrir filtros\n * </LinkBrand>\n */\nconst LinkBrand = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, LinkBrandProps>(\n function LinkBrand(props, ref) {\n if (props.as === \"button\") {\n const { as: _as, className, children, ...rest } = props\n return (\n <button\n ref={ref as React.Ref<HTMLButtonElement>}\n type=\"button\"\n data-slot=\"link-brand\"\n className={cn(BASE_CLASSES, className)}\n {...rest}\n >\n {children}\n </button>\n )\n }\n\n const { href, external = false, className, children, ...rest } = props\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noopener noreferrer\" : undefined}\n data-slot=\"link-brand\"\n className={cn(BASE_CLASSES, className)}\n {...rest}\n >\n {children}\n </a>\n )\n }\n)\n\nexport { LinkBrand }\n"]}
@@ -0,0 +1,39 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as React from 'react';
3
+ import { VariantProps } from 'class-variance-authority';
4
+
5
+ declare const ctaBrandVariants: (props?: ({
6
+ size?: "md" | "lg" | null | undefined;
7
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
8
+ interface CtaBrandProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "size">, VariantProps<typeof ctaBrandVariants> {
9
+ /** URL destino. Sempre renderiza como `<a>`. Pra acao de JS sem URL, use `<Button>` core. */
10
+ href: string;
11
+ /** Quando true, abre em nova aba com `rel="noopener noreferrer"`. Default `false`. */
12
+ external?: boolean;
13
+ /**
14
+ * Escape hatch — desliga o efeito magnetic wrapper. Use em contextos onde o
15
+ * movimento incomoda (dentro de Sheet/Drawer/popover). Default `false`
16
+ * (magnetic SEMPRE ligado — esse e o ponto do CtaBrand).
17
+ */
18
+ disableMagnetic?: boolean;
19
+ }
20
+ /**
21
+ * CTA principal de marketing — botao com gradient brand rose (Fluencypass),
22
+ * rounded-full, drop-shadow glow + brightness hover, e efeito magnetic
23
+ * embutido (puxa em direcao ao cursor).
24
+ *
25
+ * Pra acoes de JS sem URL (submit de form, abrir modal etc), use o
26
+ * `<Button>` core do Cycle em vez disso.
27
+ *
28
+ * @example
29
+ * <CtaBrand href="#vendas">
30
+ * Solicitar demonstracao
31
+ * </CtaBrand>
32
+ *
33
+ * <CtaBrand href="https://app.fluencypass.com" external size="md">
34
+ * Acessar app
35
+ * </CtaBrand>
36
+ */
37
+ declare const CtaBrand: React.ForwardRefExoticComponent<CtaBrandProps & React.RefAttributes<HTMLAnchorElement>>;
38
+
39
+ export { CtaBrand, type CtaBrandProps, ctaBrandVariants };
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ export { CtaBrand, ctaBrandVariants } from '../../chunk-DJ3DMPKK.js';
3
+ import '../../chunk-WCDYTWKA.js';
4
+ import '../../chunk-T7EIAVSF.js';
5
+ import '../../chunk-QTH4LSO6.js';
6
+ import '../../chunk-2NMEKWO5.js';
7
+ //# sourceMappingURL=cta-brand.js.map
8
+ //# sourceMappingURL=cta-brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"cta-brand.js"}
@@ -0,0 +1,5 @@
1
+ export { CtaBrand, CtaBrandProps, ctaBrandVariants } from './cta-brand.js';
2
+ export { LinkBrand, LinkBrandProps } from './link-brand.js';
3
+ import 'class-variance-authority/types';
4
+ import 'react';
5
+ import 'class-variance-authority';
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ export { CtaBrand, ctaBrandVariants } from '../../chunk-DJ3DMPKK.js';
3
+ export { LinkBrand } from '../../chunk-YZNOE4TO.js';
4
+ import '../../chunk-WCDYTWKA.js';
5
+ import '../../chunk-T7EIAVSF.js';
6
+ import '../../chunk-QTH4LSO6.js';
7
+ import '../../chunk-2NMEKWO5.js';
8
+ //# sourceMappingURL=index.js.map
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+
3
+ type LinkBrandAnchorProps = {
4
+ /** Renderiza como `<a>` (default). */
5
+ as?: "a";
6
+ /** URL destino. */
7
+ href: string;
8
+ external?: boolean;
9
+ } & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "as" | "href">;
10
+ type LinkBrandButtonProps = {
11
+ /** Renderiza como `<button>` quando a acao e JS (sem URL). */
12
+ as: "button";
13
+ href?: never;
14
+ external?: never;
15
+ } & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "as">;
16
+ type LinkBrandProps = LinkBrandAnchorProps | LinkBrandButtonProps;
17
+ /**
18
+ * Link de texto discreto da marca — `text-brand-default` com hover pra
19
+ * `text-brand-dark`. Usado em nav, footer e texto inline.
20
+ *
21
+ * Sem underline por default (visual mais clean). Pra ter underline, passe
22
+ * `className="hover:underline"` ou `underline` no consumer.
23
+ *
24
+ * Pode renderizar como `<a>` (default) ou `<button>` via prop `as`.
25
+ *
26
+ * @example
27
+ * <LinkBrand href="/precos">Ver planos</LinkBrand>
28
+ *
29
+ * <LinkBrand href="https://twitter.com" external>
30
+ * Siga no Twitter
31
+ * </LinkBrand>
32
+ *
33
+ * <LinkBrand as="button" onClick={() => openModal()}>
34
+ * Abrir filtros
35
+ * </LinkBrand>
36
+ */
37
+ declare const LinkBrand: React.ForwardRefExoticComponent<LinkBrandProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
38
+
39
+ export { LinkBrand, type LinkBrandProps };
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ export { LinkBrand } from '../../chunk-YZNOE4TO.js';
3
+ import '../../chunk-QTH4LSO6.js';
4
+ import '../../chunk-2NMEKWO5.js';
5
+ //# sourceMappingURL=link-brand.js.map
6
+ //# sourceMappingURL=link-brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"link-brand.js"}
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+
3
+ interface HandUnderlineProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
4
+ /** Delay em ms entre intersecao no viewport e inicio do draw. Default `600`. */
5
+ delay?: number;
6
+ /** Duracao do draw em ms (stroke-dashoffset 240 → 0). Default `780`. */
7
+ duration?: number;
8
+ children: React.ReactNode;
9
+ }
10
+ /**
11
+ * Wrapper que desenha um underline SVG em curva sutil abaixo do texto, animado
12
+ * via stroke-dasharray quando entra no viewport. Util pra dar enfase em
13
+ * palavras-chave em headings sem usar bold/color.
14
+ *
15
+ * Cor do stroke: `currentColor` — herda do parent. Wrappa com
16
+ * `<span className="text-[color:var(--brand-default)]">` ou outra cor.
17
+ *
18
+ * Acessibilidade:
19
+ * - SVG `aria-hidden` (decorativo).
20
+ * - `prefers-reduced-motion: reduce` — desenha o underline completo direto,
21
+ * sem animar.
22
+ *
23
+ * @example
24
+ * <h2>
25
+ * Acelere o ingles do seu{" "}
26
+ * <HandUnderline className="text-[color:var(--brand-default)]">
27
+ * time corporativo
28
+ * </HandUnderline>
29
+ * </h2>
30
+ */
31
+ declare const HandUnderline: React.ForwardRefExoticComponent<HandUnderlineProps & React.RefAttributes<HTMLSpanElement>>;
32
+
33
+ export { HandUnderline, type HandUnderlineProps };
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ export { HandUnderline } from '../../chunk-G57PPNB3.js';
3
+ import '../../chunk-T7EIAVSF.js';
4
+ import '../../chunk-OHRYR7PN.js';
5
+ import '../../chunk-QTH4LSO6.js';
6
+ import '../../chunk-2NMEKWO5.js';
7
+ //# sourceMappingURL=hand-underline.js.map
8
+ //# sourceMappingURL=hand-underline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"hand-underline.js"}
@@ -1,4 +1,7 @@
1
1
  export { CircleGlow, CircleGlowProps } from './circle-glow.js';
2
2
  export { CardDarkGlow, CardDarkGlowProps } from './card-dark-glow.js';
3
3
  export { Marquee, MarqueeProps } from './marquee.js';
4
+ export { MagneticButton, MagneticButtonProps } from './magnetic-button.js';
5
+ export { RotatingWord, RotatingWordProps } from './rotating-word.js';
6
+ export { HandUnderline, HandUnderlineProps } from './hand-underline.js';
4
7
  import 'react';
@@ -1,7 +1,12 @@
1
1
  "use client";
2
+ export { RotatingWord } from '../../chunk-JQRGW2RF.js';
3
+ export { HandUnderline } from '../../chunk-G57PPNB3.js';
4
+ export { MagneticButton } from '../../chunk-WCDYTWKA.js';
2
5
  export { CircleGlow } from '../../chunk-U27GHP5Z.js';
3
6
  export { CardDarkGlow } from '../../chunk-K2XIXOMO.js';
4
7
  export { Marquee } from '../../chunk-HXFGULDU.js';
8
+ import '../../chunk-T7EIAVSF.js';
9
+ import '../../chunk-OHRYR7PN.js';
5
10
  import '../../chunk-QTH4LSO6.js';
6
11
  import '../../chunk-2NMEKWO5.js';
7
12
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+
3
+ interface MagneticButtonProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
4
+ /** Multiplicador do offset (quanto o filho "puxa" em direcao ao cursor). Default `0.25`. */
5
+ strength?: number;
6
+ /** Distancia em px na qual o efeito ativa. Default `100`. */
7
+ radius?: number;
8
+ children: React.ReactNode;
9
+ }
10
+ /**
11
+ * Wrapper que aplica efeito magnetico no filho (puxa em direcao ao cursor
12
+ * quando proximo). Inspirado em CTAs premium de landing pages.
13
+ *
14
+ * Otimizacoes de performance:
15
+ * - Escreve `transform` direto no DOM via ref (sem React state na hot path).
16
+ * - Throttle com `requestAnimationFrame` (max 1 update por frame).
17
+ * - `IntersectionObserver` gating: so processa `mousemove` quando visivel
18
+ * (rootMargin 200px pra suavidade na entrada).
19
+ *
20
+ * Acessibilidade:
21
+ * - Skip total se `prefers-reduced-motion: reduce`.
22
+ * - Skip total em `pointer: coarse` (touch devices) — sem cursor real.
23
+ *
24
+ * O drop-shadow usa CSS vars (`--shadow-brand-glow-idle/-hover`) — consumer
25
+ * pode override pro brand proprio sem mexer no componente.
26
+ *
27
+ * @example
28
+ * <MagneticButton>
29
+ * <a href="#vendas">
30
+ * <Button variant="default">CTA principal</Button>
31
+ * </a>
32
+ * </MagneticButton>
33
+ */
34
+ declare const MagneticButton: React.ForwardRefExoticComponent<MagneticButtonProps & React.RefAttributes<HTMLDivElement>>;
35
+
36
+ export { MagneticButton, type MagneticButtonProps };
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ export { MagneticButton } from '../../chunk-WCDYTWKA.js';
3
+ import '../../chunk-T7EIAVSF.js';
4
+ import '../../chunk-QTH4LSO6.js';
5
+ import '../../chunk-2NMEKWO5.js';
6
+ //# sourceMappingURL=magnetic-button.js.map
7
+ //# sourceMappingURL=magnetic-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"magnetic-button.js"}
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+
3
+ interface RotatingWordProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
4
+ /** Lista de palavras a ciclar (typewriter). */
5
+ words: string[];
6
+ /** Velocidade de digitacao em ms por caractere. Default `80`. */
7
+ typingMs?: number;
8
+ /** Velocidade de apagar em ms por caractere. Default `45`. */
9
+ eraseMs?: number;
10
+ /** Tempo segurando a palavra completa em ms. Default `1500`. */
11
+ holdMs?: number;
12
+ }
13
+ /**
14
+ * Typewriter de palavras em loop. Util em headings de hero para destacar
15
+ * caracteristicas ou propostas de valor que rotacionam.
16
+ *
17
+ * State machine: `typing → holding → erasing → next word`.
18
+ *
19
+ * Cor herda do parent via `currentColor` — wrappa com `text-[color]` ou
20
+ * `text-foreground` no consumer pra controlar. O cursor blink usa a mesma
21
+ * cor do texto (`bg-current`).
22
+ *
23
+ * Acessibilidade:
24
+ * - `aria-live="polite"` + `aria-atomic="true"` — screen reader anuncia a
25
+ * palavra completa apos cada ciclo, sem ler caractere por caractere.
26
+ * - `prefers-reduced-motion: reduce` — renderiza so a primeira palavra,
27
+ * estatica, sem cursor.
28
+ *
29
+ * @example
30
+ * <h1>
31
+ * Aprenda ingles com{" "}
32
+ * <RotatingWord
33
+ * className="text-[color:var(--brand-default)]"
34
+ * words={["IA", "proposito", "confianca"]}
35
+ * />
36
+ * </h1>
37
+ */
38
+ declare const RotatingWord: React.ForwardRefExoticComponent<RotatingWordProps & React.RefAttributes<HTMLSpanElement>>;
39
+
40
+ export { RotatingWord, type RotatingWordProps };
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ export { RotatingWord } from '../../chunk-JQRGW2RF.js';
3
+ import '../../chunk-T7EIAVSF.js';
4
+ import '../../chunk-QTH4LSO6.js';
5
+ import '../../chunk-2NMEKWO5.js';
6
+ //# sourceMappingURL=rotating-word.js.map
7
+ //# sourceMappingURL=rotating-word.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"rotating-word.js"}
@@ -59,6 +59,7 @@
59
59
  --color-neutral-input: var(--neutral-input);
60
60
  --color-neutral-bg: var(--neutral-bg);
61
61
  --color-neutral-accent: var(--neutral-accent);
62
+ --color-neutral-ring: var(--neutral-ring);
62
63
  --radius-sm: calc(var(--radius) - 4px);
63
64
  --radius-md: calc(var(--radius) - 2px);
64
65
  --radius-lg: var(--radius);
@@ -139,6 +140,7 @@
139
140
  --neutral-input: var(--gray-200);
140
141
  --neutral-bg: var(--base-white);
141
142
  --neutral-accent: var(--gray-100);
143
+ --neutral-ring: var(--gray-500);
142
144
  --alpha-backdrop: rgba(0, 0, 0, 0.6);
143
145
  --alpha-overlay: rgba(255, 255, 255, 0.6);
144
146
 
@@ -191,6 +193,7 @@
191
193
  --neutral-input: var(--gray-800);
192
194
  --neutral-bg: var(--gray-950);
193
195
  --neutral-accent: var(--gray-800);
196
+ --neutral-ring: var(--gray-500);
194
197
  --alpha-backdrop: rgba(0, 0, 0, 0.6);
195
198
  --alpha-overlay: rgba(255, 255, 255, 0.6);
196
199
 
@@ -222,7 +225,7 @@
222
225
  --popover-foreground: #D45558;
223
226
  --muted: #FFD0D1;
224
227
  --muted-foreground: #8C2D2F;
225
- --accent: #FF8A8D;
228
+ --accent: #FFE8E8;
226
229
  --accent-foreground: #6B1E20;
227
230
  --primary: #D45558;
228
231
  --primary-foreground: #FFFFFF;
@@ -242,7 +245,7 @@
242
245
  --popover-foreground: #1A5FE0;
243
246
  --muted: #C2D6FF;
244
247
  --muted-foreground: #0A3590;
245
- --accent: #70A0FF;
248
+ --accent: #E0EBFF;
246
249
  --accent-foreground: #062268;
247
250
  --primary: #1A5FE0;
248
251
  --primary-foreground: #FFFFFF;
@@ -262,7 +265,7 @@
262
265
  --popover-foreground: #E56530;
263
266
  --muted: #FFD9C2;
264
267
  --muted-foreground: #993A16;
265
- --accent: #FFA570;
268
+ --accent: #FFEDE0;
266
269
  --accent-foreground: #73270E;
267
270
  --primary: #E56530;
268
271
  --primary-foreground: #FFFFFF;
@@ -282,7 +285,7 @@
282
285
  --popover-foreground: #00C234;
283
286
  --muted: #B8FFD0;
284
287
  --muted-foreground: #00731E;
285
- --accent: #47FF8A;
288
+ --accent: #E0FFE9;
286
289
  --accent-foreground: #004D14;
287
290
  --primary: #00C234;
288
291
  --primary-foreground: #0a0a0a;
@@ -302,7 +305,7 @@
302
305
  --popover-foreground: #7D0DD4;
303
306
  --muted: #EFC2FF;
304
307
  --muted-foreground: #48077C;
305
- --accent: #D070FF;
308
+ --accent: #F7E0FF;
306
309
  --accent-foreground: #48077C;
307
310
  --primary: #7D0DD4;
308
311
  --primary-foreground: #FFFFFF;
@@ -322,7 +325,7 @@
322
325
  --popover-foreground: #098A5E;
323
326
  --muted: #B8F2D9;
324
327
  --muted-foreground: #05523A;
325
- --accent: #47D99E;
328
+ --accent: #E0FAF0;
326
329
  --accent-foreground: #033526;
327
330
  --primary: #098A5E;
328
331
  --primary-foreground: #FFFFFF;
@@ -342,7 +345,7 @@
342
345
  --popover-foreground: #D48E00;
343
346
  --muted: #FEEAB0;
344
347
  --muted-foreground: #7D5400;
345
- --accent: #FECF50;
348
+ --accent: #FFF5D9;
346
349
  --accent-foreground: #523800;
347
350
  --primary: #D48E00;
348
351
  --primary-foreground: #FFFFFF;
@@ -362,7 +365,7 @@
362
365
  --popover-foreground: #B32020;
363
366
  --muted: #FFD0D0;
364
367
  --muted-foreground: #6B1010;
365
- --accent: #FF8585;
368
+ --accent: #FFE8E8;
366
369
  --accent-foreground: #470A0A;
367
370
  --primary: #B32020;
368
371
  --primary-foreground: #FFFFFF;
@@ -1039,6 +1042,32 @@
1039
1042
  .cycle-marquee-track { animation: none; }
1040
1043
  }
1041
1044
 
1045
+ /* ============================================================
1046
+ Cycle Marketing — Camada 2b (Buttons + Motion avançado)
1047
+ Replicado de globals.css para o CSS distribuido (styles.css).
1048
+ ============================================================ */
1049
+
1050
+ :root {
1051
+ --shadow-brand-glow-idle: drop-shadow(0 4px 8px rgba(237, 106, 109, 0.15));
1052
+ --shadow-brand-glow-hover: drop-shadow(0 12px 24px rgba(237, 106, 109, 0.35));
1053
+ }
1054
+
1055
+ @keyframes cycle-rotating-cursor-blink {
1056
+ 0%, 49% { opacity: 1; }
1057
+ 50%, 100% { opacity: 0; }
1058
+ }
1059
+
1060
+ .cycle-rotating-cursor {
1061
+ animation: cycle-rotating-cursor-blink 1s steps(2, end) infinite;
1062
+ }
1063
+
1064
+ @media (prefers-reduced-motion: reduce) {
1065
+ .cycle-rotating-cursor {
1066
+ opacity: 0;
1067
+ animation: none;
1068
+ }
1069
+ }
1070
+
1042
1071
  @media (pointer: fine) {
1043
1072
  [data-media-player][data-layout='video']:not([data-fullscreen]) {
1044
1073
  border-radius: 16px !important;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluencypassdevs/cycle",
3
- "version": "1.15.1",
3
+ "version": "1.16.1",
4
4
  "description": "Cycle Design System — UI component library by Fluencypass",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -83,6 +83,14 @@
83
83
  "import": "./dist/marketing/effects/*.js",
84
84
  "types": "./dist/marketing/effects/*.d.ts"
85
85
  },
86
+ "./marketing/buttons": {
87
+ "import": "./dist/marketing/buttons/index.js",
88
+ "types": "./dist/marketing/buttons/index.d.ts"
89
+ },
90
+ "./marketing/buttons/*": {
91
+ "import": "./dist/marketing/buttons/*.js",
92
+ "types": "./dist/marketing/buttons/*.d.ts"
93
+ },
86
94
  "./styles.css": "./dist/styles/tokens.css",
87
95
  "./tailwind-theme.css": "./dist/styles/tokens.css"
88
96
  },