@xetwa/design-system 1.0.49 → 1.0.51

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.
@@ -0,0 +1 @@
1
+ @import "https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap";@layer components;@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:"Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:var(--black);--color-white:var(--white);--radius-xs:6px;--radius-sm:10px;--radius-md:12px;--radius-lg:16px;--radius-xl:20px;--radius-2xl:24px;--shadow-xs:0 1px 3px #51332d14;--shadow-sm:0 2px 8px #51332d1a;--shadow-md:0 4px 14px #51332d21;--shadow-lg:0 8px 26px #51332d2e;--shadow-xl:0 18px 40px #51332d47;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-title:"Fredoka", sans-serif;--color-primary:var(--primary);--color-primary-dark:var(--primary-dark);--color-primary-light:var(--primary-light);--color-brown-900:var(--brown-900);--color-brown-800:var(--brown-800);--color-brown-700:var(--brown-700);--color-brown-600:var(--brown-600);--color-brown-500:var(--brown-500);--color-brown-400:var(--brown-400);--color-brown-300:var(--brown-300);--color-brown-100:var(--brown-100);--color-brown-50:var(--brown-50);--color-cream:var(--cream);--color-canvas:var(--canvas);--color-success:var(--success);--color-error:var(--error);--color-info:var(--info);--radius-screen:34px;--radius-phone:42px;--shadow-btn-primary:0 4px 0 #cf7a30;--shadow-btn-secondary:0 4px 0 #efe2d6;--shadow-btn-dark:0 4px 0 #3a241f;--shadow-focus:0 0 0 3px #f2974b38;--ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--ease-smooth:cubic-bezier(.4, 0, .2, 1);--duration-fast:.12s;--duration-base:.2s;--duration-medium:.3s;--duration-slow:.5s;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px;--space-16:64px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.container{width:100%}@media (width>=0){.container{max-width:0}}@media (width>=375px){.container{max-width:375px}}@media (width>=430px){.container{max-width:430px}}@media (width>=768px){.container{max-width:768px}}@media (width>=1024px){.container{max-width:1024px}}@media (width>=1280px){.container{max-width:1280px}}@media (width>=1440px){.container{max-width:1440px}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.flex-wrap{flex-wrap:wrap}.border{border-style:var(--tw-border-style);border-width:1px}.uppercase{text-transform:uppercase}.italic{font-style:italic}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-bounce{--tw-ease:var(--ease-bounce);transition-timing-function:var(--ease-bounce)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.ease-smooth{--tw-ease:var(--ease-smooth);transition-timing-function:var(--ease-smooth)}}:root{--primary:#f2974b;--primary-dark:#cf7a30;--primary-light:#fef6ee;--brown-900:#15100d;--brown-800:#51332d;--brown-700:#3f2620;--brown-600:#6e5a4f;--brown-500:#9a8478;--brown-400:#b9a392;--brown-300:#c3b1a4;--brown-100:#efe2d6;--brown-50:#fdf3ea;--cream:#fbf6f0;--canvas:#e7e5df;--success:#16915a;--error:#cf5b48;--info:#2a6fc2;--black:#000;--white:#fff;--bg-app:#fbf6f0;--bg-surface:#fff;--bg-surface-hover:#fdf3ea;--border-color:#efe2d6;--text-primary:#51332d;--text-secondary:#9a8478}.dark-theme{--bg-app:#15100d;--bg-surface:#51332d;--bg-surface-hover:#3f2620;--border-color:#3f2620;--text-primary:#efe2d6;--text-secondary:#b9a392;--primary-light:#f2974b26}body{background-color:var(--bg-app);color:var(--text-primary);font-family:var(--font-sans);transition:background-color .25s,color .25s}.text-display-1{font-family:var(--font-title);font-size:28px;font-weight:700;line-height:1.08}@media (width>=1024px){.text-display-1{font-size:42px}}.text-display-2{font-family:var(--font-title);font-size:24px;font-weight:700;line-height:1.12}@media (width>=1024px){.text-display-2{font-size:34px}}.text-h1{font-family:var(--font-title);font-size:22px;font-weight:700;line-height:1.15}@media (width>=1024px){.text-h1{font-size:28px}}.text-h2{font-family:var(--font-title);font-size:18px;font-weight:600;line-height:1.2}@media (width>=1024px){.text-h2{font-size:22px}}.text-h3{font-family:var(--font-title);font-size:16px;font-weight:600;line-height:1.25}@media (width>=1024px){.text-h3{font-size:19px}}.text-label{font-family:var(--font-sans);letter-spacing:.12em;text-transform:uppercase;font-size:10px;font-weight:800;line-height:1}@media (width>=1024px){.text-label{font-size:11px}}.text-body-lg{font-family:var(--font-sans);font-size:15px;font-weight:600;line-height:1.55}@media (width>=1024px){.text-body-lg{font-size:17px}}.text-body-md{font-family:var(--font-sans);font-size:13.5px;font-weight:600;line-height:1.5}@media (width>=1024px){.text-body-md{font-size:15px}}.text-body-sm{font-family:var(--font-sans);font-size:12px;font-weight:700;line-height:1.45}@media (width>=1024px){.text-body-sm{font-size:13px}}.text-caption{font-family:var(--font-sans);font-size:10px;font-weight:700;line-height:1.4}@media (width>=1024px){.text-caption{font-size:11px}}.text-btn-lg{font-family:var(--font-sans);letter-spacing:.04em;font-size:16px;font-weight:800;line-height:1}@media (width>=1024px){.text-btn-lg{font-size:17px}}.text-btn-md{font-family:var(--font-sans);letter-spacing:.04em;font-size:14px;font-weight:800;line-height:1}@media (width>=1024px){.text-btn-md{font-size:15px}}.text-btn-sm{font-family:var(--font-sans);letter-spacing:.04em;font-size:12px;font-weight:800;line-height:1}@media (width>=1024px){.text-btn-sm{font-size:12px}}@keyframes xtBob{0%{transform:translateY(0)}50%{transform:translateY(-7px)}to{transform:translateY(0)}}@keyframes xtPop{0%{transform:translateY(0)}50%{transform:translateY(-5px)}to{transform:translateY(0)}}@keyframes xtFlame{0%,to{transform:scale(1)rotate(0)}50%{transform:scale(1.15)rotate(5deg)}}@keyframes xtRing{0%{box-shadow:0 0 #f2974b8c}to{box-shadow:0 0 0 16px #f2974b00}}.animate-bob{animation:2.9s ease-in-out infinite xtBob}.animate-pop{animation:1.6s ease-in-out infinite xtPop}.animate-flame{animation:1.8s ease-in-out infinite xtFlame}.animate-ring{animation:1.8s cubic-bezier(0,0,.2,1) infinite xtRing}@keyframes xtShadow{0%,to{opacity:.2;transform:scale(1)}50%{opacity:.08;transform:scale(.6)}}.animate-contact-shadow{animation:2.9s ease-in-out infinite xtShadow}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}.counter{color:var(--accent);background:var(--accent-bg);border:2px solid #0000;border-radius:5px;margin-bottom:24px;padding:5px 10px;font-size:16px;transition:border-color .3s}.counter:hover{border-color:var(--accent-border)}.counter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.hero{position:relative}.hero .base,.hero .framework,.hero .vite{margin:0 auto;inset-inline:0}.hero .base{z-index:0;width:170px;position:relative}.hero .framework,.hero .vite{position:absolute}.hero .framework{z-index:1;height:28px;top:34px;transform:perspective(2000px)rotate(300deg)rotateX(44deg)rotateY(39deg)scale(1.4)}.hero .vite{z-index:0;width:auto;height:26px;top:107px;transform:perspective(2000px)rotate(300deg)rotateX(40deg)rotateY(39deg)scale(.8)}#center{flex-direction:column;flex-grow:1;place-content:center;place-items:center;gap:25px;display:flex}@media (width<=1024px){#center{gap:18px;padding:32px 20px 24px}}#next-steps{border-top:1px solid var(--border);text-align:left;display:flex}#next-steps>div{flex:1 1 0;padding:32px}@media (width<=1024px){#next-steps>div{padding:24px 20px}}#next-steps .icon{width:22px;height:22px;margin-bottom:16px}@media (width<=1024px){#next-steps{text-align:center;flex-direction:column}}#docs{border-right:1px solid var(--border)}@media (width<=1024px){#docs{border-right:none;border-bottom:1px solid var(--border)}}#next-steps ul{gap:8px;margin:32px 0 0;padding:0;list-style:none;display:flex}#next-steps ul .logo{height:18px}#next-steps ul a{color:var(--text-h);background:var(--social-bg);border-radius:6px;align-items:center;gap:8px;padding:6px 12px;font-size:16px;text-decoration:none;transition:box-shadow .3s;display:flex}#next-steps ul a:hover{box-shadow:var(--shadow)}#next-steps ul a .button-icon{width:18px;height:18px}@media (width<=1024px){#next-steps ul{flex-wrap:wrap;justify-content:center;margin-top:20px}#next-steps ul li{flex:calc(50% - 8px)}#next-steps ul a{box-sizing:border-box;justify-content:center;width:100%}}#spacer{border-top:1px solid var(--border);height:88px}@media (width<=1024px){#spacer{height:48px}}.ticks{width:100%;position:relative}.ticks:before,.ticks:after{content:"";border:5px solid #0000;position:absolute;top:-4.5px}.ticks:before{border-left-color:var(--border);left:0}.ticks:after{border-right-color:var(--border);right:0}
package/dist/index.html CHANGED
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>design-system</title>
8
- <script type="module" crossorigin src="/assets/index-DsSiuRvH.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DGJGCwsd.css">
8
+ <script type="module" crossorigin src="/assets/index-DK8EExZ4.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-r3VUzEKX.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xetwa/design-system",
3
- "version": "1.0.49",
3
+ "version": "1.0.51",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -27,6 +27,8 @@ export interface BaseButtonProps {
27
27
  bgColor?: string;
28
28
  /** Cor da sombra customizada (sobrepõe a cor da variante) */
29
29
  shadowColor?: string;
30
+ /** Altura/Tamanho da sombra customizada */
31
+ shadowHeight?: number;
30
32
  /** Cor de fundo no hover (opcional) */
31
33
  hoverColor?: string;
32
34
  style?: ViewStyle | ViewStyle[];
@@ -45,6 +47,7 @@ export const BaseButton = React.forwardRef<React.ElementRef<typeof Pressable>, B
45
47
  onPress,
46
48
  bgColor,
47
49
  shadowColor,
50
+ shadowHeight,
48
51
  hoverColor,
49
52
  ...props
50
53
  },
@@ -80,19 +83,21 @@ export const BaseButton = React.forwardRef<React.ElementRef<typeof Pressable>, B
80
83
  return {};
81
84
  }
82
85
 
86
+ const activeShadowHeight = shadowHeight !== undefined ? shadowHeight : 4;
87
+
83
88
  if (Platform.OS === 'web') {
84
89
  const resolvedShadowColor = shadowColor || (variant === 'primary' ? theme.colors.primaryDark : theme.colors.brown[100]);
85
90
  return {
86
- boxShadow: `0px ${scale(4)}px 0px ${resolvedShadowColor}`
91
+ boxShadow: `0px ${scale(activeShadowHeight)}px 0px ${resolvedShadowColor}`
87
92
  };
88
93
  } else {
89
- if (shadowColor) {
94
+ if (shadowColor || shadowHeight !== undefined) {
90
95
  return {
91
- shadowColor,
92
- shadowOffset: { width: 0, height: scale(4) },
96
+ shadowColor: shadowColor || (variant === 'primary' ? theme.shadows.btnPrimary.shadowColor : theme.shadows.btnSecondary.shadowColor),
97
+ shadowOffset: { width: 0, height: scale(activeShadowHeight) },
93
98
  shadowOpacity: 1,
94
99
  shadowRadius: 0,
95
- elevation: 4,
100
+ elevation: activeShadowHeight,
96
101
  };
97
102
  }
98
103
  return variant === 'primary' ? theme.shadows.btnPrimary : theme.shadows.btnSecondary;
@@ -139,8 +144,9 @@ export const BaseButton = React.forwardRef<React.ElementRef<typeof Pressable>, B
139
144
  if (variant === 'ghost') {
140
145
  stateStyles.push({ opacity: 0.7 });
141
146
  } else {
147
+ const activeShadowHeight = shadowHeight !== undefined ? shadowHeight : 4;
142
148
  stateStyles.push({
143
- transform: [{ translateY: scale(4) }],
149
+ transform: [{ translateY: scale(activeShadowHeight) }],
144
150
  });
145
151
  if (Platform.OS === 'web') {
146
152
  stateStyles.push({ boxShadow: 'none' });
@@ -1,5 +1,5 @@
1
1
  import React, { useRef } from 'react';
2
- import { Pressable, StyleSheet, Animated } from 'react-native';
2
+ import { Pressable, StyleSheet, Animated, View } from 'react-native';
3
3
  import type { ViewStyle } from 'react-native';
4
4
  import { Typography } from '../../Typography/Typography';
5
5
  import { useResponsive } from '../../../hooks/useResponsive';
@@ -8,6 +8,8 @@ import { theme } from '../../../styles/theme';
8
8
  export interface ChipProps {
9
9
  /** Texto a ser exibido no chip */
10
10
  label: string;
11
+ /** Elemento extra à esquerda (ex: ícone, flag) */
12
+ leftElement?: React.ReactNode;
11
13
  /** Função a executar ao pressionar */
12
14
  onPress?: () => void;
13
15
  /** Tamanho do chip (afeta paddings e fonte) */
@@ -30,6 +32,7 @@ export interface ChipProps {
30
32
  */
31
33
  export const Chip = ({
32
34
  label,
35
+ leftElement,
33
36
  onPress,
34
37
  size = 'md',
35
38
  backgroundColor = theme.colors.white,
@@ -104,6 +107,7 @@ export const Chip = ({
104
107
  style,
105
108
  ]}
106
109
  >
110
+ {leftElement && <View style={{ marginRight: 6 }}>{leftElement}</View>}
107
111
  <Typography variant={typographyVariant} color={textColor} style={{ textAlign: 'center' }}>
108
112
  {label}
109
113
  </Typography>
@@ -0,0 +1,67 @@
1
+ // eslint-disable-next-line storybook/no-renderer-packages
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import React from 'react';
4
+ import { View } from 'react-native';
5
+ import { CircularProgress } from './CircularProgress';
6
+ import { Typography } from '../../Typography/Typography';
7
+
8
+ const meta = {
9
+ title: 'Components/DataDisplay/CircularProgress',
10
+ component: CircularProgress,
11
+ parameters: {
12
+ layout: 'centered',
13
+ docs: {
14
+ description: {
15
+ component: 'O **CircularProgress** exibe o progresso de uma determinada métrica num formato circular (usando SVG). Permite configurar cores, espessura da linha e incluir texto ou filhos personalizados no centro.\n\n### Exemplo de Uso\n```tsx\n<CircularProgress progress={50} size={120} strokeWidth={12} text="50%" />\n```',
16
+ }
17
+ }
18
+ },
19
+ tags: ['autodocs'],
20
+ argTypes: {
21
+ progress: { control: { type: 'range', min: 0, max: 100 } },
22
+ size: { control: 'number' },
23
+ strokeWidth: { control: 'number' },
24
+ color: { control: 'color' },
25
+ trackColor: { control: 'color' },
26
+ text: { control: 'text' },
27
+ },
28
+ } satisfies Meta<typeof CircularProgress>;
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof meta>;
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ progress: 45,
36
+ size: 100,
37
+ strokeWidth: 10,
38
+ text: '45%',
39
+ },
40
+ };
41
+
42
+ export const CustomTypography: Story = {
43
+ args: {
44
+ progress: 75,
45
+ size: 140,
46
+ strokeWidth: 12,
47
+ },
48
+ render: (args) => (
49
+ <CircularProgress {...args}>
50
+ <View style={{ alignItems: 'center' }}>
51
+ <Typography variant="h2">75%</Typography>
52
+ <Typography variant="small" color="#9a8478">Concluído</Typography>
53
+ </View>
54
+ </CircularProgress>
55
+ ),
56
+ };
57
+
58
+ export const Sizes: Story = {
59
+ args: { progress: 30 },
60
+ render: () => (
61
+ <View style={{ flexDirection: 'row', gap: 20, alignItems: 'center' }}>
62
+ <CircularProgress progress={15} size={50} strokeWidth={6} text="15%" />
63
+ <CircularProgress progress={30} size={80} strokeWidth={8} text="30%" />
64
+ <CircularProgress progress={85} size={120} strokeWidth={12} text="85%" />
65
+ </View>
66
+ ),
67
+ };
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import Svg, { Circle } from 'react-native-svg';
4
+ import { theme } from '../../../styles/theme';
5
+ import { Typography } from '../../Typography/Typography';
6
+
7
+ export interface CircularProgressProps {
8
+ /** O valor do progresso (0 a 100) */
9
+ progress: number;
10
+ /** Tamanho total (largura/altura) do círculo */
11
+ size?: number;
12
+ /** Grossura da linha */
13
+ strokeWidth?: number;
14
+ /** Cor da linha de progresso */
15
+ color?: string;
16
+ /** Cor da linha de fundo (track) */
17
+ trackColor?: string;
18
+ /** Texto opcional para aparecer no centro */
19
+ text?: string;
20
+ /** Permite customizar o conteúdo central, sobrepõe a prop text */
21
+ children?: React.ReactNode;
22
+ }
23
+
24
+ export const CircularProgress = ({
25
+ progress,
26
+ size = 100,
27
+ strokeWidth = 10,
28
+ color = theme.colors.primary,
29
+ trackColor = theme.colors.primaryLight,
30
+ text,
31
+ children,
32
+ }: CircularProgressProps) => {
33
+ const radius = (size - strokeWidth) / 2;
34
+ const circumference = radius * 2 * Math.PI;
35
+ // Limita o progresso entre 0 e 100
36
+ const safeProgress = Math.min(Math.max(progress, 0), 100);
37
+ const strokeDashoffset = circumference - (safeProgress / 100) * circumference;
38
+
39
+ return (
40
+ <View style={[styles.container, { width: size, height: size }]}>
41
+ <Svg width={size} height={size} style={styles.svg}>
42
+ {/* Fundo do Progresso (Track) */}
43
+ <Circle
44
+ stroke={trackColor}
45
+ fill="none"
46
+ cx={size / 2}
47
+ cy={size / 2}
48
+ r={radius}
49
+ strokeWidth={strokeWidth}
50
+ />
51
+ {/* Linha de Progresso */}
52
+ <Circle
53
+ stroke={color}
54
+ fill="none"
55
+ cx={size / 2}
56
+ cy={size / 2}
57
+ r={radius}
58
+ strokeWidth={strokeWidth}
59
+ strokeDasharray={circumference}
60
+ strokeDashoffset={strokeDashoffset}
61
+ strokeLinecap="round"
62
+ />
63
+ </Svg>
64
+
65
+ {/* Conteúdo Central */}
66
+ <View style={styles.contentContainer}>
67
+ {children ? (
68
+ children
69
+ ) : text ? (
70
+ <Typography variant="h3" color={theme.colors.brown[800]}>
71
+ {text}
72
+ </Typography>
73
+ ) : null}
74
+ </View>
75
+ </View>
76
+ );
77
+ };
78
+
79
+ const styles = StyleSheet.create({
80
+ container: {
81
+ alignItems: 'center',
82
+ justifyContent: 'center',
83
+ },
84
+ svg: {
85
+ transform: [{ rotate: '-90deg' }],
86
+ },
87
+ contentContainer: {
88
+ position: 'absolute',
89
+ alignItems: 'center',
90
+ justifyContent: 'center',
91
+ },
92
+ });
@@ -53,7 +53,7 @@ export const ProgressBar = ({
53
53
  }, [progress, animatedProgress]);
54
54
 
55
55
  const sizeStyles = {
56
- xs: { height: 8, borderRadius: 5 },
56
+ xs: { height: 8, borderRadius: 4 },
57
57
  sm: { height: 12, borderRadius: 7 },
58
58
  md: { height: 16, borderRadius: 9 },
59
59
  }[size === 'lg' ? 'md' : size] || { height: 16, borderRadius: 9 };
@@ -1,4 +1,5 @@
1
- import { View, StyleSheet } from 'react-native';
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { View, StyleSheet, Animated, Easing } from 'react-native';
2
3
  import type { ViewStyle } from 'react-native';
3
4
  import { useResponsive } from '../../../hooks/useResponsive';
4
5
  import { getCountryTheme } from '../../../styles/countryThemes';
@@ -27,6 +28,8 @@ export interface CountryHeaderProps {
27
28
  * O developer deve passar o \`insets.top\` gerado pelo \`react-native-safe-area-context\`.
28
29
  */
29
30
  safeAreaTop?: number;
31
+ /** Se deve ocultar a barra de progresso com animação */
32
+ hideProgressBar?: boolean;
30
33
  /** Estilos customizados opcionais para o container principal */
31
34
  style?: ViewStyle | ViewStyle[];
32
35
  }
@@ -40,11 +43,23 @@ export const CountryHeader = ({
40
43
  progressSegments = 4,
41
44
  onBackPress,
42
45
  safeAreaTop = 0,
46
+ hideProgressBar = false,
43
47
  style,
44
48
  }: CountryHeaderProps) => {
45
49
  const { scale, scaleText } = useResponsive();
46
50
  const countryTheme = getCountryTheme(countryCode);
47
51
 
52
+ const progressAnim = useRef(new Animated.Value(hideProgressBar ? 0 : 1)).current;
53
+
54
+ useEffect(() => {
55
+ Animated.timing(progressAnim, {
56
+ toValue: hideProgressBar ? 0 : 1,
57
+ duration: 300,
58
+ easing: Easing.inOut(Easing.ease),
59
+ useNativeDriver: false,
60
+ }).start();
61
+ }, [hideProgressBar, progressAnim]);
62
+
48
63
  return (
49
64
  <View style={[styles.container, { backgroundColor: countryTheme.bg }, style]}>
50
65
  {/* Círculo decorativo de fundo */}
@@ -87,13 +102,24 @@ export const CountryHeader = ({
87
102
  </View>
88
103
 
89
104
  {/* Progress Bar */}
90
- <ProgressBar
91
- progress={progress}
92
- segments={progressSegments}
93
- size="sm"
94
- fillColor={countryTheme.progressFill}
95
- trackColor={countryTheme.progressTrack}
96
- />
105
+ <Animated.View
106
+ style={{
107
+ opacity: progressAnim,
108
+ height: progressAnim.interpolate({
109
+ inputRange: [0, 1],
110
+ outputRange: [0, 12],
111
+ }),
112
+ overflow: 'hidden',
113
+ }}
114
+ >
115
+ <ProgressBar
116
+ progress={progress}
117
+ segments={progressSegments}
118
+ size="sm"
119
+ fillColor={countryTheme.progressFill}
120
+ trackColor={countryTheme.progressTrack}
121
+ />
122
+ </Animated.View>
97
123
  </View>
98
124
  </View>
99
125
  );
@@ -83,6 +83,8 @@ export interface RouteHeaderProps {
83
83
  onNotificationPress?: () => void;
84
84
  /** Callback ao clicar no mapa/globo. Requer variant="minimal" */
85
85
  onGlobePress?: () => void;
86
+ /** Indica se está na visão global, para trocar o ícone do globo pelo pino de mapa */
87
+ isGlobalView?: boolean;
86
88
  /**
87
89
  * Estilos adicionais para o contentor exterior (ex: zIndex, position absolute).
88
90
  */
@@ -116,6 +118,7 @@ export const RouteHeader = ({
116
118
  notificationCount = 0,
117
119
  onNotificationPress,
118
120
  onGlobePress,
121
+ isGlobalView = false,
119
122
  safeAreaTop = 0,
120
123
  style,
121
124
  }: RouteHeaderProps) => {
@@ -188,7 +191,7 @@ export const RouteHeader = ({
188
191
  )}
189
192
  </View>
190
193
  <IconButton
191
- iconName="Globe"
194
+ iconName={isGlobalView ? "MapPin" : "Globe"}
192
195
  variant="secondary"
193
196
  size="sm"
194
197
  iconColor={theme.colors.brown[800]}
@@ -202,7 +205,7 @@ export const RouteHeader = ({
202
205
  <ProgressBar
203
206
  progress={progress}
204
207
  segments={segments}
205
- size="sm"
208
+ size="xs"
206
209
  variant="default" // Usa a cor primária (laranja)
207
210
  trackColor={theme.colors.brown[100]} // Fundo da barra um pouco mais escuro para ser visível
208
211
  />
@@ -0,0 +1,139 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { View, StyleSheet, Pressable, Animated, LayoutChangeEvent } from 'react-native';
3
+ import type { ViewStyle } from 'react-native';
4
+ import { theme } from '../../../styles/theme';
5
+ import { Typography } from '../../Typography/Typography';
6
+ import { useResponsive } from '../../../hooks/useResponsive';
7
+
8
+ export interface TopTabItem {
9
+ id: string;
10
+ label: string;
11
+ activeColor: string; // The color to use for the text and underline when active
12
+ }
13
+
14
+ export interface TopTabBarProps {
15
+ tabs: TopTabItem[];
16
+ activeTabId: string;
17
+ onTabPress: (id: string) => void;
18
+ style?: ViewStyle | ViewStyle[];
19
+ }
20
+
21
+ export const TopTabBar = ({ tabs, activeTabId, onTabPress, style }: TopTabBarProps) => {
22
+ const { scale } = useResponsive();
23
+ const [containerWidth, setContainerWidth] = useState(0);
24
+
25
+ const activeIndex = tabs.findIndex((t) => t.id === activeTabId) !== -1 ? tabs.findIndex((t) => t.id === activeTabId) : 0;
26
+
27
+ const animatedIndex = useRef(new Animated.Value(activeIndex)).current;
28
+
29
+ useEffect(() => {
30
+ Animated.spring(animatedIndex, {
31
+ toValue: activeIndex,
32
+ useNativeDriver: false, // Color interpolation requires useNativeDriver: false
33
+ tension: 60,
34
+ friction: 8,
35
+ }).start();
36
+ }, [activeIndex, animatedIndex]);
37
+
38
+ const handleLayout = (event: LayoutChangeEvent) => {
39
+ setContainerWidth(event.nativeEvent.layout.width);
40
+ };
41
+
42
+ const tabWidth = containerWidth > 0 ? containerWidth / tabs.length : 0;
43
+
44
+ // Animate indicator position
45
+ const translateX = animatedIndex.interpolate({
46
+ inputRange: tabs.map((_, i) => i),
47
+ outputRange: tabs.map((_, i) => i * tabWidth),
48
+ });
49
+
50
+ // Animate indicator color
51
+ const indicatorColor = tabs.length > 1 ? animatedIndex.interpolate({
52
+ inputRange: tabs.map((_, i) => i),
53
+ outputRange: tabs.map((t) => t.activeColor),
54
+ }) : tabs[0]?.activeColor || theme.colors.primary;
55
+
56
+ return (
57
+ <View style={[styles.container, style]} onLayout={handleLayout}>
58
+ <View style={styles.tabsRow}>
59
+ {tabs.map((tab, index) => {
60
+ const isActive = index === activeIndex;
61
+ // When active, use the bold font variant and active color
62
+ // When inactive, use regular font and brown color
63
+ return (
64
+ <Pressable
65
+ key={tab.id}
66
+ style={[styles.tabButton, { paddingVertical: scale(16) }]}
67
+ onPress={() => onTabPress(tab.id)}
68
+ >
69
+ <Typography
70
+ variant={isActive ? 'bodyMdHeavy' : 'bodyMd'}
71
+ style={[
72
+ styles.tabLabel,
73
+ { color: isActive ? tab.activeColor : theme.colors.brown[300] }
74
+ ]}
75
+ >
76
+ {tab.label}
77
+ </Typography>
78
+ </Pressable>
79
+ );
80
+ })}
81
+ </View>
82
+
83
+ {/* Animated Indicator */}
84
+ {containerWidth > 0 && (
85
+ <Animated.View
86
+ style={[
87
+ styles.indicatorContainer,
88
+ {
89
+ width: tabWidth,
90
+ transform: [{ translateX }],
91
+ },
92
+ ]}
93
+ >
94
+ <Animated.View
95
+ style={[
96
+ styles.indicator,
97
+ { backgroundColor: indicatorColor }
98
+ ]}
99
+ />
100
+ </Animated.View>
101
+ )}
102
+ </View>
103
+ );
104
+ };
105
+
106
+ const styles = StyleSheet.create({
107
+ container: {
108
+ width: '100%',
109
+ backgroundColor: theme.colors.white,
110
+ borderBottomWidth: 1,
111
+ borderBottomColor: theme.colors.brown[50],
112
+ position: 'relative',
113
+ },
114
+ tabsRow: {
115
+ flexDirection: 'row',
116
+ width: '100%',
117
+ },
118
+ tabButton: {
119
+ flex: 1,
120
+ alignItems: 'center',
121
+ justifyContent: 'center',
122
+ },
123
+ tabLabel: {
124
+ textAlign: 'center',
125
+ },
126
+ indicatorContainer: {
127
+ position: 'absolute',
128
+ bottom: 0,
129
+ left: 0,
130
+ alignItems: 'center',
131
+ justifyContent: 'center',
132
+ },
133
+ indicator: {
134
+ width: '60%', // Make the underline a bit shorter than the full tab width for aesthetics
135
+ height: 4,
136
+ borderTopLeftRadius: 4,
137
+ borderTopRightRadius: 4,
138
+ },
139
+ });
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ export * from './components/Cards/TeacherCard/TeacherCard';
22
22
  export * from './components/Charts/WeeklyProgressChart';
23
23
  export * from './components/DataDisplay/Avatar/Avatar';
24
24
  export * from './components/DataDisplay/Badge/Badge';
25
+ export * from './components/DataDisplay/CircularProgress/CircularProgress';
25
26
  export * from './components/DataDisplay/Flag/Flag';
26
27
  export * from './components/DataDisplay/InstructionBubble/InstructionBubble';
27
28
  export * from './components/DataDisplay/LevelBadge/LevelBadge';
@@ -52,6 +53,7 @@ export * from './components/Navigation/BottomTabBar/BottomTabBar';
52
53
  export * from './components/Navigation/TabBar/TabBar';
53
54
  export * from './components/Navigation/PaginationDots/PaginationDots';
54
55
  export * from './components/Navigation/ProgressMap/ProgressMap';
56
+ export * from './components/Navigation/TopTabBar/TopTabBar';
55
57
  export * from './components/Navigation/WorldProgressMap/WorldProgressMap';
56
58
  export * from './components/Typography/Typography';
57
59
  export * from './hooks/useResponsive';
@@ -1,11 +1,11 @@
1
- import { Play, Volume2, Volume1, ArrowRight, X, Check, ChevronRight, ChevronLeft, Info, AlertTriangle, Book, Mic, Home, Compass, GraduationCap, FolderOpen, User, ShoppingCart, FileText, Wallet, Bell, Globe } from 'lucide-react-native';
1
+ import { Play, Volume2, Volume1, ArrowRight, X, Check, ChevronRight, ChevronLeft, Info, AlertTriangle, Book, Mic, Home, Compass, GraduationCap, FolderOpen, User, ShoppingCart, FileText, Wallet, Bell, Globe, Shield, Settings, CheckCheck, Users, ShieldAlert, Plus, Lock, MapPin, Clock, Smartphone, LocateFixed } from 'lucide-react-native';
2
2
 
3
3
  /**
4
4
  * Para evitar que o Vite e o Metro (React Native) tentem empacotar os 1400+ ícones
5
5
  * da biblioteca (o que causa crash de memória e aumenta muito o tamanho final da app),
6
6
  * mapeamos aqui explicitamente os ícones que usamos.
7
7
  *
8
- * Se precisar de um novo ícone da \`lucide-react-native\`, basta importá-lo acima
8
+ * Se precisar de um novo ícone da `lucide-react-native`, basta importá-lo acima
9
9
  * e adicioná-lo a este objeto.
10
10
  */
11
11
  export const IconMap: Record<string, any> = {
@@ -31,4 +31,15 @@ export const IconMap: Record<string, any> = {
31
31
  Wallet,
32
32
  Bell,
33
33
  Globe,
34
+ Shield,
35
+ Settings,
36
+ CheckCheck,
37
+ Users,
38
+ ShieldAlert,
39
+ Plus,
40
+ Lock,
41
+ MapPin,
42
+ Clock,
43
+ Smartphone,
44
+ LocateFixed,
34
45
  };
@@ -1 +0,0 @@
1
- @import "https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap";@layer components;@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:"Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:var(--black);--color-white:var(--white);--radius-xs:6px;--radius-sm:10px;--radius-md:12px;--radius-lg:16px;--radius-xl:20px;--radius-2xl:24px;--shadow-xs:0 1px 3px #51332d14;--shadow-sm:0 2px 8px #51332d1a;--shadow-md:0 4px 14px #51332d21;--shadow-lg:0 8px 26px #51332d2e;--shadow-xl:0 18px 40px #51332d47;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-title:"Fredoka", sans-serif;--color-primary:var(--primary);--color-primary-dark:var(--primary-dark);--color-primary-light:var(--primary-light);--color-brown-900:var(--brown-900);--color-brown-800:var(--brown-800);--color-brown-700:var(--brown-700);--color-brown-600:var(--brown-600);--color-brown-500:var(--brown-500);--color-brown-400:var(--brown-400);--color-brown-300:var(--brown-300);--color-brown-100:var(--brown-100);--color-brown-50:var(--brown-50);--color-cream:var(--cream);--color-canvas:var(--canvas);--color-success:var(--success);--color-error:var(--error);--color-info:var(--info);--radius-screen:34px;--radius-phone:42px;--shadow-btn-primary:0 4px 0 #cf7a30;--shadow-btn-secondary:0 4px 0 #efe2d6;--shadow-btn-dark:0 4px 0 #3a241f;--shadow-focus:0 0 0 3px #f2974b38;--ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--ease-smooth:cubic-bezier(.4, 0, .2, 1);--duration-fast:.12s;--duration-base:.2s;--duration-medium:.3s;--duration-slow:.5s;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px;--space-16:64px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.container{width:100%}@media (width>=0){.container{max-width:0}}@media (width>=375px){.container{max-width:375px}}@media (width>=430px){.container{max-width:430px}}@media (width>=768px){.container{max-width:768px}}@media (width>=1024px){.container{max-width:1024px}}@media (width>=1280px){.container{max-width:1280px}}@media (width>=1440px){.container{max-width:1440px}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.flex-wrap{flex-wrap:wrap}.border{border-style:var(--tw-border-style);border-width:1px}.uppercase{text-transform:uppercase}.italic{font-style:italic}.line-through{text-decoration-line:line-through}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-bounce{--tw-ease:var(--ease-bounce);transition-timing-function:var(--ease-bounce)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.ease-smooth{--tw-ease:var(--ease-smooth);transition-timing-function:var(--ease-smooth)}}:root{--primary:#f2974b;--primary-dark:#cf7a30;--primary-light:#fef6ee;--brown-900:#15100d;--brown-800:#51332d;--brown-700:#3f2620;--brown-600:#6e5a4f;--brown-500:#9a8478;--brown-400:#b9a392;--brown-300:#c3b1a4;--brown-100:#efe2d6;--brown-50:#fdf3ea;--cream:#fbf6f0;--canvas:#e7e5df;--success:#16915a;--error:#cf5b48;--info:#2a6fc2;--black:#000;--white:#fff;--bg-app:#fbf6f0;--bg-surface:#fff;--bg-surface-hover:#fdf3ea;--border-color:#efe2d6;--text-primary:#51332d;--text-secondary:#9a8478}.dark-theme{--bg-app:#15100d;--bg-surface:#51332d;--bg-surface-hover:#3f2620;--border-color:#3f2620;--text-primary:#efe2d6;--text-secondary:#b9a392;--primary-light:#f2974b26}body{background-color:var(--bg-app);color:var(--text-primary);font-family:var(--font-sans);transition:background-color .25s,color .25s}.text-display-1{font-family:var(--font-title);font-size:28px;font-weight:700;line-height:1.08}@media (width>=1024px){.text-display-1{font-size:42px}}.text-display-2{font-family:var(--font-title);font-size:24px;font-weight:700;line-height:1.12}@media (width>=1024px){.text-display-2{font-size:34px}}.text-h1{font-family:var(--font-title);font-size:22px;font-weight:700;line-height:1.15}@media (width>=1024px){.text-h1{font-size:28px}}.text-h2{font-family:var(--font-title);font-size:18px;font-weight:600;line-height:1.2}@media (width>=1024px){.text-h2{font-size:22px}}.text-h3{font-family:var(--font-title);font-size:16px;font-weight:600;line-height:1.25}@media (width>=1024px){.text-h3{font-size:19px}}.text-label{font-family:var(--font-sans);letter-spacing:.12em;text-transform:uppercase;font-size:10px;font-weight:800;line-height:1}@media (width>=1024px){.text-label{font-size:11px}}.text-body-lg{font-family:var(--font-sans);font-size:15px;font-weight:600;line-height:1.55}@media (width>=1024px){.text-body-lg{font-size:17px}}.text-body-md{font-family:var(--font-sans);font-size:13.5px;font-weight:600;line-height:1.5}@media (width>=1024px){.text-body-md{font-size:15px}}.text-body-sm{font-family:var(--font-sans);font-size:12px;font-weight:700;line-height:1.45}@media (width>=1024px){.text-body-sm{font-size:13px}}.text-caption{font-family:var(--font-sans);font-size:10px;font-weight:700;line-height:1.4}@media (width>=1024px){.text-caption{font-size:11px}}.text-btn-lg{font-family:var(--font-sans);letter-spacing:.04em;font-size:16px;font-weight:800;line-height:1}@media (width>=1024px){.text-btn-lg{font-size:17px}}.text-btn-md{font-family:var(--font-sans);letter-spacing:.04em;font-size:14px;font-weight:800;line-height:1}@media (width>=1024px){.text-btn-md{font-size:15px}}.text-btn-sm{font-family:var(--font-sans);letter-spacing:.04em;font-size:12px;font-weight:800;line-height:1}@media (width>=1024px){.text-btn-sm{font-size:12px}}@keyframes xtBob{0%{transform:translateY(0)}50%{transform:translateY(-7px)}to{transform:translateY(0)}}@keyframes xtPop{0%{transform:translateY(0)}50%{transform:translateY(-5px)}to{transform:translateY(0)}}@keyframes xtFlame{0%,to{transform:scale(1)rotate(0)}50%{transform:scale(1.15)rotate(5deg)}}@keyframes xtRing{0%{box-shadow:0 0 #f2974b8c}to{box-shadow:0 0 0 16px #f2974b00}}.animate-bob{animation:2.9s ease-in-out infinite xtBob}.animate-pop{animation:1.6s ease-in-out infinite xtPop}.animate-flame{animation:1.8s ease-in-out infinite xtFlame}.animate-ring{animation:1.8s cubic-bezier(0,0,.2,1) infinite xtRing}@keyframes xtShadow{0%,to{opacity:.2;transform:scale(1)}50%{opacity:.08;transform:scale(.6)}}.animate-contact-shadow{animation:2.9s ease-in-out infinite xtShadow}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}.counter{color:var(--accent);background:var(--accent-bg);border:2px solid #0000;border-radius:5px;margin-bottom:24px;padding:5px 10px;font-size:16px;transition:border-color .3s}.counter:hover{border-color:var(--accent-border)}.counter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.hero{position:relative}.hero .base,.hero .framework,.hero .vite{margin:0 auto;inset-inline:0}.hero .base{z-index:0;width:170px;position:relative}.hero .framework,.hero .vite{position:absolute}.hero .framework{z-index:1;height:28px;top:34px;transform:perspective(2000px)rotate(300deg)rotateX(44deg)rotateY(39deg)scale(1.4)}.hero .vite{z-index:0;width:auto;height:26px;top:107px;transform:perspective(2000px)rotate(300deg)rotateX(40deg)rotateY(39deg)scale(.8)}#center{flex-direction:column;flex-grow:1;place-content:center;place-items:center;gap:25px;display:flex}@media (width<=1024px){#center{gap:18px;padding:32px 20px 24px}}#next-steps{border-top:1px solid var(--border);text-align:left;display:flex}#next-steps>div{flex:1 1 0;padding:32px}@media (width<=1024px){#next-steps>div{padding:24px 20px}}#next-steps .icon{width:22px;height:22px;margin-bottom:16px}@media (width<=1024px){#next-steps{text-align:center;flex-direction:column}}#docs{border-right:1px solid var(--border)}@media (width<=1024px){#docs{border-right:none;border-bottom:1px solid var(--border)}}#next-steps ul{gap:8px;margin:32px 0 0;padding:0;list-style:none;display:flex}#next-steps ul .logo{height:18px}#next-steps ul a{color:var(--text-h);background:var(--social-bg);border-radius:6px;align-items:center;gap:8px;padding:6px 12px;font-size:16px;text-decoration:none;transition:box-shadow .3s;display:flex}#next-steps ul a:hover{box-shadow:var(--shadow)}#next-steps ul a .button-icon{width:18px;height:18px}@media (width<=1024px){#next-steps ul{flex-wrap:wrap;justify-content:center;margin-top:20px}#next-steps ul li{flex:calc(50% - 8px)}#next-steps ul a{box-sizing:border-box;justify-content:center;width:100%}}#spacer{border-top:1px solid var(--border);height:88px}@media (width<=1024px){#spacer{height:48px}}.ticks{width:100%;position:relative}.ticks:before,.ticks:after{content:"";border:5px solid #0000;position:absolute;top:-4.5px}.ticks:before{border-left-color:var(--border);left:0}.ticks:after{border-right-color:var(--border);right:0}