astro-component-kit 0.3.0 → 0.4.0

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 (115) hide show
  1. package/README.md +159 -176
  2. package/package.json +2 -2
  3. package/src/components/index.ts +16 -202
  4. package/src/components/lib/buttons/NeonBorderButton.astro +162 -0
  5. package/src/components/lib/buttons/RippleButton.astro +199 -0
  6. package/src/components/lib/buttons/SlideRevealButton.astro +159 -0
  7. package/src/components/lib/buttons/index.ts +4 -0
  8. package/src/components/lib/cards/GithubRepoCard.astro +176 -0
  9. package/src/components/lib/cards/PricingCompareMiniCard.astro +196 -0
  10. package/src/components/lib/cards/StatsTrendCard.astro +149 -0
  11. package/src/components/lib/cards/index.ts +3 -0
  12. package/src/components/lib/charts/ActivityRings.astro +105 -0
  13. package/src/components/lib/charts/BoxPlotChart.astro +109 -0
  14. package/src/components/lib/charts/BubbleChart.astro +101 -0
  15. package/src/components/lib/charts/BulletChart.astro +160 -0
  16. package/src/components/lib/charts/BumpChart.astro +92 -0
  17. package/src/components/lib/charts/CandlestickChart.astro +105 -0
  18. package/src/components/lib/charts/ChartFrame.astro +373 -0
  19. package/src/components/lib/charts/DivergingBarChart.astro +98 -0
  20. package/src/components/lib/charts/DumbbellChart.astro +92 -0
  21. package/src/components/lib/charts/ForecastLineChart.astro +125 -0
  22. package/src/components/lib/charts/FunnelChart.astro +122 -0
  23. package/src/components/lib/charts/GaugeChart.astro +111 -0
  24. package/src/components/lib/charts/GoalProgressChart.astro +97 -0
  25. package/src/components/lib/charts/GroupedBarChart.astro +125 -0
  26. package/src/components/lib/charts/HeatmapChart.astro +131 -0
  27. package/src/components/lib/charts/HistogramChart.astro +111 -0
  28. package/src/components/lib/charts/HorizontalBarChart.astro +155 -0
  29. package/src/components/lib/charts/KpiStatTile.astro +116 -0
  30. package/src/components/lib/charts/KpiTrendGrid.astro +60 -0
  31. package/src/components/lib/charts/LineChart.astro +160 -0
  32. package/src/components/lib/charts/LinearProgressGauge.astro +136 -0
  33. package/src/components/lib/charts/LollipopChart.astro +100 -0
  34. package/src/components/lib/charts/MeterGauge.astro +122 -0
  35. package/src/components/lib/charts/MetricDeltaTable.astro +125 -0
  36. package/src/components/lib/charts/PieChart.astro +135 -0
  37. package/src/components/lib/charts/PolarAreaChart.astro +85 -0
  38. package/src/components/lib/charts/PopulationPyramid.astro +122 -0
  39. package/src/components/lib/charts/PunchCardChart.astro +89 -0
  40. package/src/components/lib/charts/RadarChart.astro +94 -0
  41. package/src/components/lib/charts/RadarMetricChart.astro +168 -0
  42. package/src/components/lib/charts/RadialBarChart.astro +124 -0
  43. package/src/components/lib/charts/RangeAreaChart.astro +113 -0
  44. package/src/components/lib/charts/RetentionCohortTable.astro +126 -0
  45. package/src/components/lib/charts/SankeyChart.astro +128 -0
  46. package/src/components/lib/charts/ScatterPlot.astro +139 -0
  47. package/src/components/lib/charts/SegmentedBarChart.astro +119 -0
  48. package/src/components/lib/charts/SlopeChart.astro +105 -0
  49. package/src/components/lib/charts/SparklineChart.astro +87 -0
  50. package/src/components/lib/charts/StackedAreaChart.astro +130 -0
  51. package/src/components/lib/charts/StackedBarChart.astro +136 -0
  52. package/src/components/lib/charts/SunburstChart.astro +103 -0
  53. package/src/components/lib/charts/TreemapChart.astro +94 -0
  54. package/src/components/lib/charts/WaffleChart.astro +121 -0
  55. package/src/components/lib/charts/WaterfallChart.astro +118 -0
  56. package/src/components/lib/charts/chart-tooltip.ts +75 -0
  57. package/src/components/lib/charts/chart-utils.ts +271 -0
  58. package/src/components/lib/charts/index.ts +46 -0
  59. package/src/components/lib/data/ActivityHeatmapGrid.astro +157 -0
  60. package/src/components/lib/data/MetricComparisonRow.astro +109 -0
  61. package/src/components/lib/data/StatusTimelineFeed.astro +168 -0
  62. package/src/components/lib/data/index.ts +3 -0
  63. package/src/components/lib/feedback/ProgressRingCounter.astro +118 -0
  64. package/src/components/lib/feedback/ToastNotificationGroup.astro +173 -0
  65. package/src/components/lib/feedback/index.ts +2 -0
  66. package/src/components/lib/forms/GlassInlineToggleGroup.astro +161 -0
  67. package/src/components/lib/forms/PasswordStrengthInput.astro +242 -0
  68. package/src/components/lib/forms/PhoneCountryInput.astro +145 -0
  69. package/src/components/lib/forms/TagsAutocompleteInput.astro +249 -0
  70. package/src/components/lib/forms/index.ts +5 -0
  71. package/src/components/lib/layout/DiagonalSectionDivider.astro +69 -0
  72. package/src/components/lib/layout/HolyGrailGrid.astro +125 -0
  73. package/src/components/lib/layout/index.ts +3 -1
  74. package/src/components/lib/navigation/QuickJumpDock.astro +125 -0
  75. package/src/components/lib/navigation/StepBreadcrumbBar.astro +151 -0
  76. package/src/components/lib/navigation/SubNavbarPills.astro +102 -0
  77. package/src/components/lib/navigation/index.ts +3 -0
  78. package/src/components/lib/overlays/CookiePreferencesDrawer.astro +226 -0
  79. package/src/components/lib/overlays/FloatingFeedbackWidget.astro +203 -0
  80. package/src/components/lib/overlays/ImageCompareSlider.astro +172 -0
  81. package/src/components/lib/overlays/index.ts +3 -0
  82. package/src/components/lib/sections/BlogGridSection.astro +131 -0
  83. package/src/components/lib/sections/CareersOpenings.astro +139 -0
  84. package/src/components/lib/sections/ChangelogSection.astro +144 -0
  85. package/src/components/lib/sections/ContactSplitMap.astro +139 -0
  86. package/src/components/lib/sections/CtaNewsletterSection.astro +163 -0
  87. package/src/components/lib/sections/CtaSplitImage.astro +147 -0
  88. package/src/components/lib/sections/FaqTwoColumn.astro +119 -0
  89. package/src/components/lib/sections/FeatureAlternatingRows.astro +190 -0
  90. package/src/components/lib/sections/FeatureIconGrid.astro +155 -0
  91. package/src/components/lib/sections/FeatureTabsShowcase.astro +180 -0
  92. package/src/components/lib/sections/FooterMegaSection.astro +155 -0
  93. package/src/components/lib/sections/HeroDashboardPreview.astro +180 -0
  94. package/src/components/lib/sections/HeroMinimalCentered.astro +175 -0
  95. package/src/components/lib/sections/HeroShowcaseSplit.astro +304 -0
  96. package/src/components/lib/sections/HeroTerminal.astro +251 -0
  97. package/src/components/lib/sections/HeroWaitlist.astro +229 -0
  98. package/src/components/lib/sections/HowItWorksSteps.astro +178 -0
  99. package/src/components/lib/sections/IntegrationsGrid.astro +167 -0
  100. package/src/components/lib/sections/LogoCloudSection.astro +96 -0
  101. package/src/components/lib/sections/LogoWallGrid.astro +91 -0
  102. package/src/components/lib/sections/PricingSinglePlan.astro +157 -0
  103. package/src/components/lib/sections/PricingToggleCards.astro +171 -0
  104. package/src/components/lib/sections/StatsCounterBand.astro +122 -0
  105. package/src/components/lib/sections/StatsDashboardSection.astro +83 -0
  106. package/src/components/lib/sections/TestimonialCarousel.astro +165 -0
  107. package/src/components/lib/sections/TestimonialFeatured.astro +120 -0
  108. package/src/components/lib/sections/TestimonialGridSection.astro +188 -0
  109. package/src/components/lib/sections/index.ts +29 -0
  110. package/src/components/lib/typography/AnimatedCounterText.astro +102 -0
  111. package/src/components/lib/typography/CurvedPathHeading.astro +86 -0
  112. package/src/components/lib/typography/GradientTextMarquee.astro +105 -0
  113. package/src/components/lib/typography/HackerDecodeText.astro +85 -0
  114. package/src/components/lib/typography/index.ts +4 -0
  115. package/src/styles/global.css +18 -1
@@ -0,0 +1,229 @@
1
+ ---
2
+ /**
3
+ * HeroWaitlist — Pre-launch hero with an accessible email capture form,
4
+ * inline validation, success state and avatar-stack social proof.
5
+ *
6
+ * @param {string} badge - Status pill text (e.g. 'Private beta').
7
+ * @param {string} title - Headline.
8
+ * @param {string} subtitle - Supporting paragraph.
9
+ * @param {string} action - Form action URL (posts `email`).
10
+ * @param {string} buttonLabel - Submit button text.
11
+ * @param {string} successMessage - Message after a valid submission.
12
+ * @param {number} count - Number of people already on the list.
13
+ * @param {string} variant - Background treatment (default|aurora|minimal).
14
+ * @param {string} size - Vertical rhythm (sm|md|lg).
15
+ * @param {string} className - Extra CSS class.
16
+ */
17
+ interface Props {
18
+ badge?: string;
19
+ title?: string;
20
+ subtitle?: string;
21
+ action?: string;
22
+ buttonLabel?: string;
23
+ successMessage?: string;
24
+ count?: number;
25
+ variant?: 'default' | 'aurora' | 'minimal';
26
+ size?: 'sm' | 'md' | 'lg';
27
+ className?: string;
28
+ }
29
+
30
+ const {
31
+ badge = 'Private beta · Spring 2027',
32
+ title = 'Analytics that respect your users',
33
+ subtitle = 'Cookieless, privacy-first insights with beautiful dashboards. Join the waitlist for early access and founder pricing.',
34
+ action = '#',
35
+ buttonLabel = 'Join waitlist',
36
+ successMessage = "You're on the list! Check your inbox to confirm.",
37
+ count = 4280,
38
+ variant = 'aurora',
39
+ size = 'lg',
40
+ className = '',
41
+ } = Astro.props;
42
+
43
+ const id = `hw-${Math.random().toString(36).slice(2, 8)}`;
44
+ const initials = ['AK', 'MR', 'JS', 'LP', 'TW'];
45
+ const hues = ['var(--chart-1, #3987e5)', 'var(--chart-2, #d95926)', 'var(--chart-3, #199e70)', 'var(--chart-5, #d55181)', 'var(--chart-7, #9085e9)'];
46
+ ---
47
+
48
+ <section class={`hw hw--${variant} hw--${size} ${className}`} aria-labelledby={id}>
49
+ <div class="hw__aurora" aria-hidden="true"><span></span><span></span><span></span></div>
50
+ <div class="hw__inner">
51
+ <span class="hw__badge"><span class="hw__pulse" aria-hidden="true"></span>{badge}</span>
52
+ <h1 id={id} class="hw__title">{title}</h1>
53
+ <p class="hw__subtitle">{subtitle}</p>
54
+
55
+ <form class="hw__form" action={action} method="post" novalidate data-success={successMessage}>
56
+ <label for={`${id}-email`} class="hw__sr">Email address</label>
57
+ <input id={`${id}-email`} class="hw__input" type="email" name="email" placeholder="you@company.com" autocomplete="email" required aria-describedby={`${id}-msg`} />
58
+ <button class="hw__btn" type="submit">
59
+ <span>{buttonLabel}</span>
60
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
61
+ </button>
62
+ </form>
63
+ <p id={`${id}-msg`} class="hw__msg" role="status" aria-live="polite"></p>
64
+
65
+ <div class="hw__proof">
66
+ <div class="hw__avatars" aria-hidden="true">
67
+ {initials.map((ini, i) => <span style={`background:${hues[i]}`}>{ini}</span>)}
68
+ </div>
69
+ <p><strong>{count.toLocaleString('en-US')}+</strong> builders already joined</p>
70
+ </div>
71
+ </div>
72
+ </section>
73
+
74
+ <script>
75
+ document.querySelectorAll<HTMLFormElement>('.hw__form').forEach((form) => {
76
+ const input = form.querySelector<HTMLInputElement>('input[type="email"]');
77
+ const msg = document.getElementById(input?.getAttribute('aria-describedby') || '');
78
+ form.addEventListener('submit', (e) => {
79
+ e.preventDefault();
80
+ if (!input || !msg) return;
81
+ if (!input.value || !input.checkValidity()) {
82
+ input.setAttribute('aria-invalid', 'true');
83
+ msg.dataset.state = 'error';
84
+ msg.textContent = 'Please enter a valid email address.';
85
+ input.focus();
86
+ return;
87
+ }
88
+ input.removeAttribute('aria-invalid');
89
+ msg.dataset.state = 'success';
90
+ msg.textContent = form.dataset.success || 'Thanks!';
91
+ form.classList.add('is-done');
92
+ input.value = '';
93
+ });
94
+ input?.addEventListener('input', () => {
95
+ input.removeAttribute('aria-invalid');
96
+ if (msg && msg.dataset.state === 'error') msg.textContent = '';
97
+ });
98
+ });
99
+ </script>
100
+
101
+ <style>
102
+ .hw {
103
+ --hw-py: 6rem;
104
+ position: relative;
105
+ overflow: hidden;
106
+ padding: var(--hw-py) var(--sp-6, 1.5rem);
107
+ background: var(--c-bg, #050505);
108
+ text-align: center;
109
+ }
110
+ .hw--sm { --hw-py: 4rem; }
111
+ .hw--lg { --hw-py: 8.5rem; }
112
+ .hw__aurora { position: absolute; inset: 0; pointer-events: none; display: none; }
113
+ .hw--aurora .hw__aurora { display: block; }
114
+ .hw__aurora span {
115
+ position: absolute;
116
+ width: 40vw;
117
+ height: 40vw;
118
+ max-width: 520px;
119
+ max-height: 520px;
120
+ border-radius: 50%;
121
+ filter: blur(90px);
122
+ opacity: 0.22;
123
+ animation: hw-float 16s ease-in-out infinite alternate;
124
+ }
125
+ .hw__aurora span:nth-child(1) { background: var(--chart-7, #9085e9); top: -10%; left: 10%; }
126
+ .hw__aurora span:nth-child(2) { background: var(--chart-1, #3987e5); top: 10%; right: 5%; animation-delay: -5s; }
127
+ .hw__aurora span:nth-child(3) { background: var(--c-primary, #b8860b); bottom: -25%; left: 35%; animation-delay: -10s; }
128
+ @keyframes hw-float { to { transform: translate(6%, 8%) scale(1.1); } }
129
+ .hw__inner { position: relative; max-width: 680px; margin: 0 auto; }
130
+ .hw__badge {
131
+ display: inline-flex;
132
+ align-items: center;
133
+ gap: 0.55rem;
134
+ padding: 0.4rem 0.9rem;
135
+ border-radius: var(--r-full, 999px);
136
+ background: rgba(255, 255, 255, 0.05);
137
+ border: 1px solid rgba(255, 255, 255, 0.09);
138
+ font-size: 0.8125rem;
139
+ color: var(--c-text-1, #fff);
140
+ }
141
+ .hw__pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--chart-pos, #34c38f); box-shadow: 0 0 0 0 rgba(52, 195, 143, 0.6); animation: hw-pulse 2s infinite; }
142
+ @keyframes hw-pulse { 70% { box-shadow: 0 0 0 8px rgba(52, 195, 143, 0); } 100% { box-shadow: 0 0 0 0 rgba(52, 195, 143, 0); } }
143
+ .hw__title {
144
+ margin: 1.5rem 0 1rem;
145
+ font-size: clamp(2.4rem, 6vw, 4rem);
146
+ font-weight: 800;
147
+ letter-spacing: -0.035em;
148
+ line-height: 1.05;
149
+ color: var(--c-text-1, #fff);
150
+ text-wrap: balance;
151
+ }
152
+ .hw__subtitle { margin: 0 auto 2.25rem; max-width: 560px; font-size: 1.0625rem; line-height: 1.65; color: var(--c-text-2, #a3a3a3); }
153
+ .hw__form {
154
+ display: flex;
155
+ gap: 0.5rem;
156
+ max-width: 480px;
157
+ margin: 0 auto;
158
+ padding: 0.4rem;
159
+ border-radius: var(--r-lg, 16px);
160
+ background: rgba(255, 255, 255, 0.04);
161
+ border: 1px solid rgba(255, 255, 255, 0.1);
162
+ backdrop-filter: blur(12px);
163
+ transition: border-color 0.2s ease;
164
+ }
165
+ .hw__form:focus-within { border-color: rgba(255, 255, 255, 0.28); }
166
+ .hw__input {
167
+ flex: 1;
168
+ min-width: 0;
169
+ min-height: 48px;
170
+ padding: 0 0.9rem;
171
+ border: 0;
172
+ background: transparent;
173
+ color: var(--c-text-1, #fff);
174
+ font-size: 1rem;
175
+ font-family: inherit;
176
+ }
177
+ .hw__input::placeholder { color: var(--c-text-3, #737373); }
178
+ .hw__input:focus { outline: none; }
179
+ .hw__input[aria-invalid='true'] { box-shadow: inset 0 -2px 0 var(--chart-neg, #f06a6a); }
180
+ .hw__btn {
181
+ display: inline-flex;
182
+ align-items: center;
183
+ gap: 0.45rem;
184
+ min-height: 48px;
185
+ padding: 0 1.25rem;
186
+ border: 0;
187
+ border-radius: var(--r-md, 12px);
188
+ background: var(--c-text-1, #fff);
189
+ color: #0a0a0a;
190
+ font-weight: 650;
191
+ font-size: 0.9375rem;
192
+ font-family: inherit;
193
+ cursor: pointer;
194
+ white-space: nowrap;
195
+ transition: transform 0.2s ease, background 0.2s ease;
196
+ }
197
+ .hw__btn:hover { background: var(--c-accent, #fef3c7); }
198
+ .hw__btn:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; }
199
+ .hw__form.is-done .hw__btn { background: var(--chart-pos, #34c38f); }
200
+ .hw__msg { min-height: 1.5rem; margin: 0.75rem 0 0; font-size: 0.875rem; }
201
+ .hw__msg[data-state='error'] { color: #ff9b9b; }
202
+ .hw__msg[data-state='success'] { color: #7ee2b8; }
203
+ .hw__proof { display: inline-flex; align-items: center; gap: 0.85rem; margin-top: 1.5rem; }
204
+ .hw__proof p { margin: 0; font-size: 0.875rem; color: var(--c-text-2, #a3a3a3); }
205
+ .hw__proof strong { color: var(--c-text-1, #fff); }
206
+ .hw__avatars { display: flex; }
207
+ .hw__avatars span {
208
+ display: grid;
209
+ place-items: center;
210
+ width: 32px;
211
+ height: 32px;
212
+ margin-left: -8px;
213
+ border-radius: 50%;
214
+ border: 2px solid var(--c-bg, #050505);
215
+ font-size: 0.625rem;
216
+ font-weight: 700;
217
+ color: #fff;
218
+ }
219
+ .hw__avatars span:first-child { margin-left: 0; }
220
+ .hw__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
221
+ @media (max-width: 520px) {
222
+ .hw__form { flex-direction: column; }
223
+ .hw__btn { justify-content: center; }
224
+ .hw__proof { flex-direction: column; }
225
+ }
226
+ @media (prefers-reduced-motion: reduce) {
227
+ .hw__aurora span, .hw__pulse { animation: none; }
228
+ }
229
+ </style>
@@ -0,0 +1,178 @@
1
+ ---
2
+ /**
3
+ * HowItWorksSteps — Numbered process section ("How it works") with a connecting
4
+ * line between steps. Horizontal on desktop, vertical on mobile.
5
+ *
6
+ * @param {string} eyebrow - Small label above the heading.
7
+ * @param {string} title - Section heading.
8
+ * @param {Array<{title: string, text: string, time?: string}>} steps - Ordered steps.
9
+ * @param {{label: string, href: string}} cta - Optional CTA under the steps.
10
+ * @param {string} variant - Step marker style (numbers|dots|cards).
11
+ * @param {string} size - Vertical rhythm (sm|md|lg).
12
+ * @param {string} className - Extra CSS class.
13
+ */
14
+ interface Step { title: string; text: string; time?: string }
15
+ interface Props {
16
+ eyebrow?: string;
17
+ title?: string;
18
+ steps?: Step[];
19
+ cta?: { label: string; href: string };
20
+ variant?: 'numbers' | 'dots' | 'cards';
21
+ size?: 'sm' | 'md' | 'lg';
22
+ className?: string;
23
+ }
24
+
25
+ const {
26
+ eyebrow = 'How it works',
27
+ title = 'Ship a polished page in four steps',
28
+ steps = [
29
+ { title: 'Pick sections', text: 'Browse heroes, features, pricing and more in the catalog.', time: '2 min' },
30
+ { title: 'Copy the code', text: 'Paste components into your Astro project. No install needed.', time: '1 min' },
31
+ { title: 'Customize props', text: 'Change copy, colors and data through typed props and tokens.', time: '10 min' },
32
+ { title: 'Deploy', text: 'Build static HTML and ship to any CDN or edge host.', time: '30 s' },
33
+ ],
34
+ cta = { label: 'Start building', href: '#' },
35
+ variant = 'numbers',
36
+ size = 'md',
37
+ className = '',
38
+ } = Astro.props;
39
+
40
+ const id = `hiw-${Math.random().toString(36).slice(2, 8)}`;
41
+ ---
42
+
43
+ <section class={`hiw hiw--${variant} hiw--${size} ${className}`} aria-labelledby={id} style={`--hiw-n:${steps.length}`}>
44
+ <div class="hiw__inner">
45
+ <header class="hiw__head">
46
+ <p class="hiw__eyebrow">{eyebrow}</p>
47
+ <h2 id={id} class="hiw__title">{title}</h2>
48
+ </header>
49
+ <ol class="hiw__steps">
50
+ {steps.map((s, i) => (
51
+ <li class="hiw__step" style={`--i:${i}`}>
52
+ <span class="hiw__marker" aria-hidden="true">{variant === 'dots' ? '' : String(i + 1).padStart(2, '0')}</span>
53
+ <div class="hiw__body">
54
+ <h3 class="hiw__step-title"><span class="hiw__sr">Step {i + 1}: </span>{s.title}</h3>
55
+ <p class="hiw__text">{s.text}</p>
56
+ {s.time && (
57
+ <span class="hiw__time">
58
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M12 6v6l4 2" /></svg>
59
+ {s.time}
60
+ </span>
61
+ )}
62
+ </div>
63
+ </li>
64
+ ))}
65
+ </ol>
66
+ {cta && (
67
+ <div class="hiw__cta">
68
+ <a href={cta.href}>{cta.label}</a>
69
+ </div>
70
+ )}
71
+ </div>
72
+ </section>
73
+
74
+ <style>
75
+ .hiw {
76
+ --hiw-py: 5.5rem;
77
+ padding: var(--hiw-py) var(--sp-6, 1.5rem);
78
+ background: var(--c-bg, #050505);
79
+ }
80
+ .hiw--sm { --hiw-py: 3.5rem; }
81
+ .hiw--lg { --hiw-py: 7.5rem; }
82
+ .hiw__inner { max-width: 1180px; margin: 0 auto; }
83
+ .hiw__head { text-align: center; margin-bottom: 3.5rem; }
84
+ .hiw__eyebrow { margin: 0 0 0.75rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-primary-light, #d4a017); }
85
+ .hiw__title { margin: 0; font-size: clamp(1.9rem, 4vw, 2.75rem); font-weight: 800; letter-spacing: -0.03em; color: var(--c-text-1, #fff); text-wrap: balance; }
86
+ .hiw__steps {
87
+ position: relative;
88
+ list-style: none;
89
+ margin: 0;
90
+ padding: 0;
91
+ display: grid;
92
+ grid-template-columns: repeat(var(--hiw-n), minmax(0, 1fr));
93
+ gap: 2rem;
94
+ }
95
+ .hiw__steps::before {
96
+ content: '';
97
+ position: absolute;
98
+ top: 24px;
99
+ left: calc(100% / var(--hiw-n) / 2);
100
+ right: calc(100% / var(--hiw-n) / 2);
101
+ height: 1px;
102
+ background: linear-gradient(90deg, rgba(212, 160, 23, 0.6), rgba(255, 255, 255, 0.1));
103
+ }
104
+ .hiw--cards .hiw__steps::before { display: none; }
105
+ .hiw__step {
106
+ position: relative;
107
+ display: flex;
108
+ flex-direction: column;
109
+ align-items: center;
110
+ text-align: center;
111
+ animation: hiw-in 0.6s ease both;
112
+ animation-delay: calc(var(--i) * 120ms);
113
+ }
114
+ .hiw--cards .hiw__step {
115
+ align-items: flex-start;
116
+ text-align: left;
117
+ padding: 1.5rem;
118
+ border-radius: var(--r-lg, 20px);
119
+ background: rgba(255, 255, 255, 0.025);
120
+ border: 1px solid var(--c-border, rgba(255, 255, 255, 0.07));
121
+ }
122
+ @keyframes hiw-in { from { opacity: 0; transform: translateY(10px); } }
123
+ .hiw__marker {
124
+ position: relative;
125
+ z-index: 1;
126
+ display: grid;
127
+ place-items: center;
128
+ width: 48px;
129
+ height: 48px;
130
+ margin-bottom: 1.25rem;
131
+ border-radius: 50%;
132
+ background: var(--c-bg-1, #0a0a0a);
133
+ border: 1px solid rgba(212, 160, 23, 0.45);
134
+ color: var(--c-primary-light, #d4a017);
135
+ font-family: var(--font-mono, monospace);
136
+ font-size: 0.875rem;
137
+ font-weight: 700;
138
+ box-shadow: 0 0 0 6px var(--c-bg, #050505);
139
+ }
140
+ .hiw--dots .hiw__marker { width: 16px; height: 16px; margin: 16px 0 1.5rem; background: var(--c-primary-light, #d4a017); }
141
+ .hiw__step-title { margin: 0 0 0.5rem; font-size: 1.125rem; font-weight: 650; color: var(--c-text-1, #fff); }
142
+ .hiw__text { margin: 0; font-size: 0.9375rem; line-height: 1.6; color: var(--c-text-2, #a3a3a3); }
143
+ .hiw__time {
144
+ display: inline-flex;
145
+ align-items: center;
146
+ gap: 0.35rem;
147
+ margin-top: 0.85rem;
148
+ padding: 0.2rem 0.6rem;
149
+ border-radius: var(--r-full, 999px);
150
+ background: rgba(255, 255, 255, 0.05);
151
+ font-size: 0.75rem;
152
+ color: var(--c-text-2, #a3a3a3);
153
+ }
154
+ .hiw__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
155
+ .hiw__cta { text-align: center; margin-top: 3rem; }
156
+ .hiw__cta a {
157
+ display: inline-flex;
158
+ align-items: center;
159
+ min-height: 48px;
160
+ padding: 0 1.5rem;
161
+ border-radius: var(--r-md, 12px);
162
+ background: var(--c-primary-light, #d4a017);
163
+ color: #0a0a0a;
164
+ font-weight: 650;
165
+ text-decoration: none;
166
+ transition: background 0.2s ease;
167
+ }
168
+ .hiw__cta a:hover { background: var(--c-accent, #fef3c7); color: #0a0a0a; }
169
+ .hiw__cta a:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 3px; }
170
+ @media (max-width: 820px) {
171
+ .hiw__steps { grid-template-columns: 1fr; gap: 1.75rem; }
172
+ .hiw__steps::before { top: 24px; bottom: 24px; left: 24px; right: auto; width: 1px; height: auto; background: linear-gradient(180deg, rgba(212, 160, 23, 0.6), rgba(255, 255, 255, 0.1)); }
173
+ .hiw__step { flex-direction: row; align-items: flex-start; text-align: left; gap: 1.25rem; }
174
+ .hiw__marker { flex-shrink: 0; margin-bottom: 0; }
175
+ .hiw--dots .hiw__marker { margin: 16px 16px 0; }
176
+ }
177
+ @media (prefers-reduced-motion: reduce) { .hiw__step { animation: none; } }
178
+ </style>
@@ -0,0 +1,167 @@
1
+ ---
2
+ /**
3
+ * IntegrationsGrid — Integrations directory with category filter chips
4
+ * (progressively enhanced), monogram tiles and connection status badges.
5
+ *
6
+ * @param {string} title - Section heading.
7
+ * @param {string} subtitle - Section description.
8
+ * @param {Array<{name: string, category: string, description: string, status?: 'available'|'beta'|'soon'}>} items - Integrations.
9
+ * @param {string} variant - Tile style (card|compact|outline).
10
+ * @param {string} size - Vertical rhythm (sm|md|lg).
11
+ * @param {string} className - Extra CSS class.
12
+ */
13
+ interface Item { name: string; category: string; description: string; status?: 'available' | 'beta' | 'soon' }
14
+ interface Props {
15
+ title?: string;
16
+ subtitle?: string;
17
+ items?: Item[];
18
+ variant?: 'card' | 'compact' | 'outline';
19
+ size?: 'sm' | 'md' | 'lg';
20
+ className?: string;
21
+ }
22
+
23
+ const {
24
+ title = 'Plays well with your stack',
25
+ subtitle = 'Connect the tools your team already uses. New integrations every month.',
26
+ items = [
27
+ { name: 'Slack', category: 'Messaging', description: 'Post alerts and weekly digests to channels.', status: 'available' },
28
+ { name: 'GitHub', category: 'Developer', description: 'Annotate charts with deploys and releases.', status: 'available' },
29
+ { name: 'Stripe', category: 'Payments', description: 'Sync MRR, churn and invoices automatically.', status: 'available' },
30
+ { name: 'Linear', category: 'Developer', description: 'Turn insights into issues in one click.', status: 'beta' },
31
+ { name: 'Notion', category: 'Docs', description: 'Embed live charts in your team wiki.', status: 'available' },
32
+ { name: 'HubSpot', category: 'CRM', description: 'Attribute revenue to campaigns and deals.', status: 'beta' },
33
+ { name: 'Postgres', category: 'Data', description: 'Query your warehouse with zero ETL.', status: 'available' },
34
+ { name: 'Discord', category: 'Messaging', description: 'Community metrics and bot notifications.', status: 'soon' },
35
+ ],
36
+ variant = 'card',
37
+ size = 'md',
38
+ className = '',
39
+ } = Astro.props;
40
+
41
+ const id = `ig-${Math.random().toString(36).slice(2, 8)}`;
42
+ const categories = ['All', ...new Set(items.map((i) => i.category))];
43
+ const hues = ['var(--chart-1, #3987e5)', 'var(--chart-2, #d95926)', 'var(--chart-3, #199e70)', 'var(--chart-4, #c98500)', 'var(--chart-5, #d55181)', 'var(--chart-7, #9085e9)'];
44
+ const statusLabel = { available: 'Available', beta: 'Beta', soon: 'Coming soon' };
45
+ ---
46
+
47
+ <section class={`ig ig--${variant} ig--${size} ${className}`} aria-labelledby={id} data-ig>
48
+ <div class="ig__inner">
49
+ <header class="ig__head">
50
+ <h2 id={id} class="ig__title">{title}</h2>
51
+ <p class="ig__subtitle">{subtitle}</p>
52
+ </header>
53
+
54
+ <div class="ig__filters" role="group" aria-label="Filter integrations by category">
55
+ {categories.map((c, i) => (
56
+ <button type="button" class="ig__chip" data-filter={c} aria-pressed={i === 0 ? 'true' : 'false'}>{c}</button>
57
+ ))}
58
+ </div>
59
+ <p class="ig__count" role="status" aria-live="polite">{items.length} integrations</p>
60
+
61
+ <ul class="ig__grid">
62
+ {items.map((it, i) => (
63
+ <li class="ig__tile" data-category={it.category}>
64
+ <span class="ig__logo" style={`--c:${hues[i % hues.length]}`} aria-hidden="true">{it.name.slice(0, 2)}</span>
65
+ <div class="ig__body">
66
+ <div class="ig__row">
67
+ <h3 class="ig__name">{it.name}</h3>
68
+ {it.status && <span class={`ig__status ig__status--${it.status}`}>{statusLabel[it.status]}</span>}
69
+ </div>
70
+ <p class="ig__desc">{it.description}</p>
71
+ <span class="ig__cat">{it.category}</span>
72
+ </div>
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ </div>
77
+ </section>
78
+
79
+ <script>
80
+ document.querySelectorAll<HTMLElement>('[data-ig]').forEach((root) => {
81
+ const chips = root.querySelectorAll<HTMLButtonElement>('.ig__chip');
82
+ const tiles = root.querySelectorAll<HTMLElement>('.ig__tile');
83
+ const count = root.querySelector('.ig__count');
84
+ chips.forEach((chip) => {
85
+ chip.addEventListener('click', () => {
86
+ const f = chip.dataset.filter;
87
+ chips.forEach((c) => c.setAttribute('aria-pressed', String(c === chip)));
88
+ let shown = 0;
89
+ tiles.forEach((t) => {
90
+ const on = f === 'All' || t.dataset.category === f;
91
+ t.hidden = !on;
92
+ if (on) shown += 1;
93
+ });
94
+ if (count) count.textContent = `${shown} integration${shown === 1 ? '' : 's'}${f === 'All' ? '' : ` in ${f}`}`;
95
+ });
96
+ });
97
+ });
98
+ </script>
99
+
100
+ <style>
101
+ .ig {
102
+ --ig-py: 5.5rem;
103
+ padding: var(--ig-py) var(--sp-6, 1.5rem);
104
+ background: var(--c-bg, #050505);
105
+ }
106
+ .ig--sm { --ig-py: 3.5rem; }
107
+ .ig--lg { --ig-py: 7.5rem; }
108
+ .ig__inner { max-width: 1180px; margin: 0 auto; }
109
+ .ig__head { text-align: center; max-width: 620px; margin: 0 auto 2rem; }
110
+ .ig__title { margin: 0 0 0.75rem; font-size: clamp(1.9rem, 4vw, 2.75rem); font-weight: 800; letter-spacing: -0.03em; color: var(--c-text-1, #fff); }
111
+ .ig__subtitle { margin: 0; font-size: 1.0625rem; color: var(--c-text-2, #a3a3a3); }
112
+ .ig__filters { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem; }
113
+ .ig__chip {
114
+ min-height: 40px;
115
+ padding: 0 1rem;
116
+ border-radius: var(--r-full, 999px);
117
+ border: 1px solid rgba(255, 255, 255, 0.1);
118
+ background: transparent;
119
+ color: var(--c-text-2, #a3a3a3);
120
+ font: inherit;
121
+ font-size: 0.875rem;
122
+ font-weight: 550;
123
+ cursor: pointer;
124
+ transition: all 0.2s ease;
125
+ }
126
+ .ig__chip:hover { color: var(--c-text-1, #fff); border-color: rgba(255, 255, 255, 0.25); }
127
+ .ig__chip[aria-pressed='true'] { background: var(--c-text-1, #fff); color: #0a0a0a; border-color: transparent; }
128
+ .ig__chip:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; }
129
+ .ig__count { margin: 1rem 0 1.5rem; text-align: center; font-size: 0.8125rem; color: var(--c-text-3, #8a8a8a); }
130
+ .ig__grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; }
131
+ .ig__tile {
132
+ display: flex;
133
+ gap: 1rem;
134
+ padding: 1.25rem;
135
+ border-radius: var(--r-lg, 18px);
136
+ transition: border-color 0.2s ease, transform 0.2s ease;
137
+ }
138
+ .ig__tile[hidden] { display: none; }
139
+ .ig--card .ig__tile { background: rgba(255, 255, 255, 0.025); border: 1px solid var(--c-border, rgba(255, 255, 255, 0.07)); }
140
+ .ig--outline .ig__tile { border: 1px solid rgba(255, 255, 255, 0.12); }
141
+ .ig--compact .ig__tile { padding: 0.75rem; }
142
+ .ig--compact .ig__desc { display: none; }
143
+ .ig__tile:hover { border-color: rgba(255, 255, 255, 0.2); transform: translateY(-2px); }
144
+ .ig__logo {
145
+ display: grid;
146
+ place-items: center;
147
+ flex-shrink: 0;
148
+ width: 44px;
149
+ height: 44px;
150
+ border-radius: 12px;
151
+ background: color-mix(in srgb, var(--c) 22%, transparent);
152
+ border: 1px solid color-mix(in srgb, var(--c) 45%, transparent);
153
+ color: var(--c-text-1, #fff);
154
+ font-weight: 750;
155
+ font-size: 0.9rem;
156
+ }
157
+ .ig__body { min-width: 0; flex: 1; }
158
+ .ig__row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
159
+ .ig__name { margin: 0; font-size: 1rem; font-weight: 650; color: var(--c-text-1, #fff); }
160
+ .ig__status { font-size: 0.6875rem; font-weight: 650; padding: 0.15rem 0.5rem; border-radius: var(--r-full, 999px); white-space: nowrap; }
161
+ .ig__status--available { background: rgba(52, 195, 143, 0.14); color: #7ee2b8; }
162
+ .ig__status--beta { background: rgba(201, 133, 0, 0.16); color: #f2c66a; }
163
+ .ig__status--soon { background: rgba(255, 255, 255, 0.06); color: var(--c-text-2, #a3a3a3); }
164
+ .ig__desc { margin: 0.35rem 0 0.6rem; font-size: 0.875rem; line-height: 1.5; color: var(--c-text-2, #a3a3a3); }
165
+ .ig__cat { font-size: 0.75rem; color: var(--c-text-3, #8a8a8a); }
166
+ @media (prefers-reduced-motion: reduce) { .ig__tile, .ig__chip { transition: none; } }
167
+ </style>
@@ -0,0 +1,96 @@
1
+ ---
2
+ /**
3
+ * LogoCloudSection — High-trust partner and client brand wall with monochrome glass badges.
4
+ *
5
+ * @param {string} title - Section headline. Default is 'TRUSTED BY MODERN TECH STACKS'.
6
+ * @param {Array<{name: string, symbol: string}>} logos - Partner entries list.
7
+ */
8
+ interface Props {
9
+ title?: string;
10
+ logos?: Array<{ name: string; symbol: string }>;
11
+ }
12
+
13
+ const {
14
+ title = 'POWERING FORWARD-THINKING WEB PROJECTS',
15
+ logos = [
16
+ { name: 'Vercel', symbol: '▲' },
17
+ { name: 'Cloudflare', symbol: '☁' },
18
+ { name: 'Supabase', symbol: '⚡' },
19
+ { name: 'TailwindCSS', symbol: '≋' },
20
+ { name: 'Netlify', symbol: '◈' },
21
+ { name: 'Docker', symbol: '⚓' },
22
+ ],
23
+ } = Astro.props;
24
+ ---
25
+
26
+ <section class="logo-cloud">
27
+ <div class="logo-cloud__inner">
28
+ <p class="logo-cloud__title">{title}</p>
29
+ <div class="logo-cloud__grid">
30
+ {logos.map((logo) => (
31
+ <div class="logo-badge">
32
+ <span class="logo-badge__symbol">{logo.symbol}</span>
33
+ <span class="logo-badge__name">{logo.name}</span>
34
+ </div>
35
+ ))}
36
+ </div>
37
+ </div>
38
+ </section>
39
+
40
+ <style>
41
+ .logo-cloud {
42
+ padding: var(--sp-8, 2rem) var(--sp-6, 1.5rem);
43
+ width: 100%;
44
+ font-family: var(--font-sans, system-ui, sans-serif);
45
+ }
46
+
47
+ .logo-cloud__inner {
48
+ max-width: 1100px;
49
+ margin: 0 auto;
50
+ text-align: center;
51
+ }
52
+
53
+ .logo-cloud__title {
54
+ font-size: 0.75rem;
55
+ font-weight: 800;
56
+ letter-spacing: 0.12em;
57
+ color: var(--c-text-3, #737373);
58
+ text-transform: uppercase;
59
+ margin-bottom: 1.5rem;
60
+ }
61
+
62
+ .logo-cloud__grid {
63
+ display: flex;
64
+ flex-wrap: wrap;
65
+ align-items: center;
66
+ justify-content: center;
67
+ gap: 16px;
68
+ }
69
+
70
+ .logo-badge {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 10px;
74
+ padding: 0.65rem 1.4rem;
75
+ background: rgba(255, 255, 255, 0.02);
76
+ backdrop-filter: blur(12px);
77
+ border: 1px solid rgba(255, 255, 255, 0.06);
78
+ border-radius: var(--r-full, 9999px);
79
+ color: var(--c-text-2, #a3a3a3);
80
+ font-size: 0.88rem;
81
+ font-weight: 700;
82
+ transition: all 0.25s ease;
83
+ }
84
+
85
+ .logo-badge:hover {
86
+ color: var(--c-text-1, #ffffff);
87
+ background: rgba(255, 255, 255, 0.06);
88
+ border-color: rgba(184, 134, 11, 0.4);
89
+ transform: translateY(-2px);
90
+ }
91
+
92
+ .logo-badge__symbol {
93
+ font-size: 1.1rem;
94
+ color: var(--c-primary-light, #d4a017);
95
+ }
96
+ </style>