create-fornix 0.0.11 → 0.2.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.
- package/LICENSE +21 -0
- package/dist/blocks/about-stats/about-stats.astro +72 -0
- package/dist/blocks/about-stats/about-stats.css +167 -0
- package/dist/blocks/about-stats/block.json +78 -0
- package/dist/blocks/about-stats/default-content.json +12 -0
- package/dist/blocks/about-stats/schema.ts +19 -0
- package/dist/blocks/about-timeline/about-timeline.astro +106 -0
- package/dist/blocks/about-timeline/about-timeline.css +261 -0
- package/dist/blocks/about-timeline/block.json +49 -0
- package/dist/blocks/about-timeline/default-content.json +14 -0
- package/dist/blocks/about-timeline/schema.ts +20 -0
- package/dist/blocks/about-values/about-values.astro +103 -0
- package/dist/blocks/about-values/about-values.css +242 -0
- package/dist/blocks/about-values/block.json +52 -0
- package/dist/blocks/about-values/default-content.json +37 -0
- package/dist/blocks/about-values/schema.ts +23 -0
- package/dist/blocks/ai-og-images/block.json +57 -0
- package/dist/blocks/ai-og-images/default-content.json +1 -0
- package/dist/blocks/ai-og-images/og-api.ts +25 -0
- package/dist/blocks/ai-og-images/og-generator.ts +80 -0
- package/dist/blocks/analytics-cf/CfAnalytics.astro +13 -0
- package/dist/blocks/analytics-cf/block.json +39 -0
- package/dist/blocks/analytics-cf/default-content.json +1 -0
- package/dist/blocks/auth-better-auth/auth-api.ts +15 -0
- package/dist/blocks/auth-better-auth/auth.ts +34 -0
- package/dist/blocks/auth-better-auth/block.json +99 -0
- package/dist/blocks/auth-better-auth/default-content.json +18 -0
- package/dist/blocks/auth-better-auth/login.astro +47 -0
- package/dist/blocks/auth-better-auth/middleware.ts +35 -0
- package/dist/blocks/auth-better-auth/signup.astro +45 -0
- package/dist/blocks/blog-mdx/block.json +48 -0
- package/dist/blocks/blog-mdx/pages/[slug].astro +39 -0
- package/dist/blocks/blog-mdx/pages/index.astro +37 -0
- package/dist/blocks/blog-mdx/pages/rss.xml.ts +17 -0
- package/dist/blocks/blog-mdx/schema.ts +8 -0
- package/dist/blocks/callout/block.json +46 -0
- package/dist/blocks/callout/callout.astro +44 -0
- package/dist/blocks/callout/callout.css +96 -0
- package/dist/blocks/contact-form/block.json +86 -0
- package/dist/blocks/contact-form/contact-action.ts +28 -0
- package/dist/blocks/contact-form/contact-form.astro +92 -0
- package/dist/blocks/contact-form/contact-form.css +90 -0
- package/dist/blocks/contact-form/default-content.json +10 -0
- package/dist/blocks/contact-form/schema.ts +14 -0
- package/dist/blocks/contact-split/block.json +41 -0
- package/dist/blocks/contact-split/contact-split.astro +84 -0
- package/dist/blocks/contact-split/contact-split.css +175 -0
- package/dist/blocks/contact-split/default-content.json +16 -0
- package/dist/blocks/contact-split/schema.ts +18 -0
- package/dist/blocks/cta-banner/block.json +55 -0
- package/dist/blocks/cta-banner/cta-banner.astro +35 -0
- package/dist/blocks/cta-banner/cta-banner.css +62 -0
- package/dist/blocks/cta-banner/default-content.json +6 -0
- package/dist/blocks/cta-banner/schema.ts +10 -0
- package/dist/blocks/cta-newsletter/block.json +62 -0
- package/dist/blocks/cta-newsletter/cta-newsletter.astro +49 -0
- package/dist/blocks/cta-newsletter/cta-newsletter.css +86 -0
- package/dist/blocks/cta-newsletter/default-content.json +7 -0
- package/dist/blocks/cta-newsletter/schema.ts +11 -0
- package/dist/blocks/db-d1/block.json +49 -0
- package/dist/blocks/db-d1/db.ts +21 -0
- package/dist/blocks/db-d1/default-content.json +1 -0
- package/dist/blocks/db-d1/drizzle.config.ts +7 -0
- package/dist/blocks/db-d1/migrations/.gitkeep +0 -0
- package/dist/blocks/db-d1/schema.ts +20 -0
- package/dist/blocks/docs-collection/block.json +39 -0
- package/dist/blocks/docs-collection/pages/[...slug].astro +63 -0
- package/dist/blocks/docs-collection/schema.ts +7 -0
- package/dist/blocks/email-resend/block.json +45 -0
- package/dist/blocks/email-resend/default-content.json +1 -0
- package/dist/blocks/email-resend/resend.ts +9 -0
- package/dist/blocks/faq-accordion/block.json +61 -0
- package/dist/blocks/faq-accordion/default-content.json +11 -0
- package/dist/blocks/faq-accordion/faq-accordion.astro +45 -0
- package/dist/blocks/faq-accordion/faq-accordion.css +89 -0
- package/dist/blocks/faq-accordion/schema.ts +14 -0
- package/dist/blocks/faq-home/block.json +43 -0
- package/dist/blocks/faq-home/default-content.json +11 -0
- package/dist/blocks/faq-home/faq-home.astro +88 -0
- package/dist/blocks/faq-home/faq-home.css +150 -0
- package/dist/blocks/faq-home/schema.ts +14 -0
- package/dist/blocks/features-bento/block.json +64 -0
- package/dist/blocks/features-bento/default-content.json +12 -0
- package/dist/blocks/features-bento/features-bento.astro +45 -0
- package/dist/blocks/features-bento/features-bento.css +87 -0
- package/dist/blocks/features-bento/schema.ts +16 -0
- package/dist/blocks/features-grid/block.json +58 -0
- package/dist/blocks/features-grid/default-content.json +36 -0
- package/dist/blocks/features-grid/features-grid.astro +39 -0
- package/dist/blocks/features-grid/features-grid.css +83 -0
- package/dist/blocks/features-grid/schema.ts +19 -0
- package/dist/blocks/footer-minimal/block.json +91 -0
- package/dist/blocks/footer-minimal/default-content.json +10 -0
- package/dist/blocks/footer-minimal/footer-minimal.astro +42 -0
- package/dist/blocks/footer-minimal/footer-minimal.css +66 -0
- package/dist/blocks/footer-minimal/schema.ts +18 -0
- package/dist/blocks/footer-rich/block.json +132 -0
- package/dist/blocks/footer-rich/default-content.json +30 -0
- package/dist/blocks/footer-rich/footer-rich.astro +61 -0
- package/dist/blocks/footer-rich/footer-rich.css +89 -0
- package/dist/blocks/footer-rich/schema.ts +20 -0
- package/dist/blocks/header-sticky/block.json +65 -0
- package/dist/blocks/header-sticky/default-content.json +11 -0
- package/dist/blocks/header-sticky/header-sticky.astro +51 -0
- package/dist/blocks/header-sticky/header-sticky.css +115 -0
- package/dist/blocks/header-sticky/schema.ts +15 -0
- package/dist/blocks/header-transparent/block.json +64 -0
- package/dist/blocks/header-transparent/default-content.json +10 -0
- package/dist/blocks/header-transparent/header-transparent.astro +51 -0
- package/dist/blocks/header-transparent/header-transparent.css +116 -0
- package/dist/blocks/header-transparent/schema.ts +15 -0
- package/dist/blocks/hero-glass/block.json +67 -0
- package/dist/blocks/hero-glass/default-content.json +9 -0
- package/dist/blocks/hero-glass/hero-glass.astro +99 -0
- package/dist/blocks/hero-glass/hero-glass.css +283 -0
- package/dist/blocks/hero-glass/schema.ts +13 -0
- package/dist/blocks/hero-gradient/block.json +80 -0
- package/dist/blocks/hero-gradient/default-content.json +7 -0
- package/dist/blocks/hero-gradient/hero-gradient.astro +39 -0
- package/dist/blocks/hero-gradient/hero-gradient.css +96 -0
- package/dist/blocks/hero-gradient/schema.ts +15 -0
- package/dist/blocks/hero-split/block.json +93 -0
- package/dist/blocks/hero-split/default-content.json +9 -0
- package/dist/blocks/hero-split/hero-split.astro +56 -0
- package/dist/blocks/hero-split/hero-split.css +101 -0
- package/dist/blocks/hero-split/schema.ts +13 -0
- package/dist/blocks/hero-video/block.json +84 -0
- package/dist/blocks/hero-video/default-content.json +8 -0
- package/dist/blocks/hero-video/hero-video.astro +48 -0
- package/dist/blocks/hero-video/hero-video.css +82 -0
- package/dist/blocks/hero-video/schema.ts +12 -0
- package/dist/blocks/how-it-works/block.json +67 -0
- package/dist/blocks/how-it-works/default-content.json +10 -0
- package/dist/blocks/how-it-works/how-it-works.astro +77 -0
- package/dist/blocks/how-it-works/how-it-works.css +186 -0
- package/dist/blocks/how-it-works/schema.ts +17 -0
- package/dist/blocks/layout-dashboard/block.json +52 -0
- package/dist/blocks/layout-dashboard/default-content.json +7 -0
- package/dist/blocks/layout-dashboard/layout-dashboard.astro +56 -0
- package/dist/blocks/layout-docs/block.json +41 -0
- package/dist/blocks/layout-docs/default-content.json +4 -0
- package/dist/blocks/layout-docs/layout-docs.astro +33 -0
- package/dist/blocks/layout-marketing/block.json +50 -0
- package/dist/blocks/layout-marketing/default-content.json +8 -0
- package/dist/blocks/layout-marketing/layout-marketing.astro +37 -0
- package/dist/blocks/logo-cloud/block.json +57 -0
- package/dist/blocks/logo-cloud/default-content.json +11 -0
- package/dist/blocks/logo-cloud/logo-cloud.astro +41 -0
- package/dist/blocks/logo-cloud/logo-cloud.css +74 -0
- package/dist/blocks/logo-cloud/schema.ts +14 -0
- package/dist/blocks/payments-stripe/block.json +55 -0
- package/dist/blocks/payments-stripe/default-content.json +1 -0
- package/dist/blocks/payments-stripe/stripe.ts +12 -0
- package/dist/blocks/portfolio-grid/block.json +65 -0
- package/dist/blocks/portfolio-grid/default-content.json +14 -0
- package/dist/blocks/portfolio-grid/portfolio-grid.astro +120 -0
- package/dist/blocks/portfolio-grid/portfolio-grid.css +193 -0
- package/dist/blocks/portfolio-grid/schema.ts +19 -0
- package/dist/blocks/pricing-comparison/block.json +169 -0
- package/dist/blocks/pricing-comparison/default-content.json +17 -0
- package/dist/blocks/pricing-comparison/pricing-comparison.astro +78 -0
- package/dist/blocks/pricing-comparison/pricing-comparison.css +111 -0
- package/dist/blocks/pricing-comparison/schema.ts +23 -0
- package/dist/blocks/pricing-table/block.json +142 -0
- package/dist/blocks/pricing-table/default-content.json +36 -0
- package/dist/blocks/pricing-table/pricing-table.astro +60 -0
- package/dist/blocks/pricing-table/pricing-table.css +137 -0
- package/dist/blocks/pricing-table/schema.ts +20 -0
- package/dist/blocks/pricing-toggle/block.json +53 -0
- package/dist/blocks/pricing-toggle/default-content.json +40 -0
- package/dist/blocks/pricing-toggle/pricing-toggle.astro +134 -0
- package/dist/blocks/pricing-toggle/pricing-toggle.css +256 -0
- package/dist/blocks/pricing-toggle/schema.ts +24 -0
- package/dist/blocks/stats-strip/block.json +59 -0
- package/dist/blocks/stats-strip/default-content.json +9 -0
- package/dist/blocks/stats-strip/schema.ts +13 -0
- package/dist/blocks/stats-strip/stats-strip.astro +88 -0
- package/dist/blocks/stats-strip/stats-strip.css +71 -0
- package/dist/blocks/testimonials-carousel/block.json +55 -0
- package/dist/blocks/testimonials-carousel/default-content.json +8 -0
- package/dist/blocks/testimonials-carousel/schema.ts +15 -0
- package/dist/blocks/testimonials-carousel/testimonials-carousel.astro +56 -0
- package/dist/blocks/testimonials-carousel/testimonials-carousel.css +99 -0
- package/dist/blocks/testimonials-chat/block.json +49 -0
- package/dist/blocks/testimonials-chat/default-content.json +56 -0
- package/dist/blocks/testimonials-chat/schema.ts +25 -0
- package/dist/blocks/testimonials-chat/testimonials-chat.astro +147 -0
- package/dist/blocks/testimonials-chat/testimonials-chat.css +257 -0
- package/dist/blocks/testimonials-wall/block.json +64 -0
- package/dist/blocks/testimonials-wall/default-content.json +12 -0
- package/dist/blocks/testimonials-wall/schema.ts +16 -0
- package/dist/blocks/testimonials-wall/testimonials-wall.astro +59 -0
- package/dist/blocks/testimonials-wall/testimonials-wall.css +82 -0
- package/dist/blocks/theme-switcher/block.json +38 -0
- package/dist/blocks/theme-switcher/default-content.json +3 -0
- package/dist/blocks/theme-switcher/schema.ts +10 -0
- package/dist/blocks/theme-switcher/theme-switcher.astro +133 -0
- package/dist/blocks/theme-switcher/theme-switcher.css +122 -0
- package/dist/cli.js +939 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.js +623 -5889
- package/dist/index.js.map +1 -1
- package/dist/palettes/arctic.json +14 -0
- package/dist/palettes/charcoal.json +14 -0
- package/dist/palettes/copper.json +14 -0
- package/dist/palettes/corporate-blue.json +14 -0
- package/dist/palettes/cotton.json +14 -0
- package/dist/palettes/cream.json +14 -0
- package/dist/palettes/cyber-punk.json +14 -0
- package/dist/palettes/deep-sea.json +14 -0
- package/dist/palettes/desert-sand.json +14 -0
- package/dist/palettes/electric-violet.json +14 -0
- package/dist/palettes/ember.json +14 -0
- package/dist/palettes/executive.json +14 -0
- package/dist/palettes/fintech-dark.json +14 -0
- package/dist/palettes/forest.json +14 -0
- package/dist/palettes/frost.json +14 -0
- package/dist/palettes/glacier.json +14 -0
- package/dist/palettes/golden-hour.json +14 -0
- package/dist/palettes/health-clean.json +14 -0
- package/dist/palettes/luxury-gold.json +14 -0
- package/dist/palettes/midnight.json +14 -0
- package/dist/palettes/neon-tokyo.json +14 -0
- package/dist/palettes/obsidian.json +14 -0
- package/dist/palettes/ocean-breeze.json +14 -0
- package/dist/palettes/pearl.json +14 -0
- package/dist/palettes/slate-modern.json +14 -0
- package/dist/palettes/snow.json +14 -0
- package/dist/palettes/startup-bold.json +14 -0
- package/dist/palettes/storm.json +14 -0
- package/dist/palettes/sunset-glow.json +14 -0
- package/dist/palettes/terracotta.json +14 -0
- package/dist/palettes/trust.json +14 -0
- package/dist/palettes/void.json +14 -0
- package/dist/templates/astro.config.mjs.template +13 -0
- package/dist/templates/content.config.ts.template +11 -0
- package/dist/templates/gitignore.template +6 -0
- package/dist/templates/index.astro.template +7 -0
- package/dist/templates/layout.astro.template +44 -0
- package/dist/templates/package.json.template +16 -0
- package/dist/templates/tsconfig.json.template +5 -0
- package/dist/templates/wrangler.json.template +6 -0
- package/package.json +42 -19
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kama kama
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* About Stats — about section with stat cards and trust strip.
|
|
4
|
+
*
|
|
5
|
+
* ALL text is sourced from the content collection (zero hardcoded strings).
|
|
6
|
+
* Content is read from `src/content/sections/about-stats.json`.
|
|
7
|
+
*/
|
|
8
|
+
import { getEntry } from "astro:content";
|
|
9
|
+
|
|
10
|
+
interface StatCard {
|
|
11
|
+
icon: string;
|
|
12
|
+
value: string;
|
|
13
|
+
subtitle: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const locale = Astro.currentLocale ?? "en";
|
|
18
|
+
const entry = await getEntry("sections", `${locale}/about-stats`);
|
|
19
|
+
const {
|
|
20
|
+
badge = "",
|
|
21
|
+
headline = "",
|
|
22
|
+
description = "",
|
|
23
|
+
trustHeadline = "",
|
|
24
|
+
} = entry?.data ?? {};
|
|
25
|
+
const stats = (entry?.data?.stats ?? []) as StatCard[];
|
|
26
|
+
const trustLabels = (entry?.data?.trustLabels ?? []) as string[];
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<section class="about-stats">
|
|
30
|
+
<!-- Header -->
|
|
31
|
+
<div class="about-stats__header">
|
|
32
|
+
{badge && (
|
|
33
|
+
<span class="about-stats__badge">{badge}</span>
|
|
34
|
+
)}
|
|
35
|
+
<h2 class="about-stats__headline">{headline}</h2>
|
|
36
|
+
{description && (
|
|
37
|
+
<p class="about-stats__description">{description}</p>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<!-- Stats Grid -->
|
|
42
|
+
<div class="about-stats__grid">
|
|
43
|
+
{stats.map((stat, i) => (
|
|
44
|
+
<div class="about-stats__card" style={`animation-delay: ${i * 100}ms`}>
|
|
45
|
+
<div class="about-stats__icon">{stat.icon}</div>
|
|
46
|
+
<div class="about-stats__value">{stat.value}</div>
|
|
47
|
+
<div class="about-stats__subtitle">{stat.subtitle}</div>
|
|
48
|
+
{stat.description && (
|
|
49
|
+
<p class="about-stats__card-desc">{stat.description}</p>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<!-- Trust Strip -->
|
|
56
|
+
{trustLabels.length > 0 && (
|
|
57
|
+
<div class="about-stats__trust">
|
|
58
|
+
{trustHeadline && (
|
|
59
|
+
<p class="about-stats__trust-headline">{trustHeadline}</p>
|
|
60
|
+
)}
|
|
61
|
+
<div class="about-stats__trust-logos">
|
|
62
|
+
{trustLabels.map((label) => (
|
|
63
|
+
<span class="about-stats__trust-label">{label}</span>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</section>
|
|
69
|
+
|
|
70
|
+
<style>
|
|
71
|
+
@import "../../styles/sections/about-stats.css";
|
|
72
|
+
</style>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* ── About Stats ──────────────────────────────────────── */
|
|
2
|
+
|
|
3
|
+
.about-stats {
|
|
4
|
+
padding: 5rem 2rem;
|
|
5
|
+
background: var(--color-surface, rgba(0, 0, 0, 0.02));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* ── Header ──────────────────────────────────────── */
|
|
9
|
+
|
|
10
|
+
.about-stats__header {
|
|
11
|
+
max-width: 48rem;
|
|
12
|
+
margin: 0 auto 3.5rem;
|
|
13
|
+
text-align: center;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.about-stats__badge {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: 0.5rem;
|
|
20
|
+
padding: 0.375rem 1rem;
|
|
21
|
+
margin-bottom: 1.25rem;
|
|
22
|
+
font-size: 0.6875rem;
|
|
23
|
+
font-weight: 700;
|
|
24
|
+
text-transform: uppercase;
|
|
25
|
+
letter-spacing: 0.15em;
|
|
26
|
+
color: var(--color-accent, #6366f1);
|
|
27
|
+
background: var(--color-accent-soft, rgba(99, 102, 241, 0.08));
|
|
28
|
+
border: 1px solid var(--color-accent-border, rgba(99, 102, 241, 0.15));
|
|
29
|
+
border-radius: 9999px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.about-stats__headline {
|
|
33
|
+
font-size: clamp(1.75rem, 4vw, 2.75rem);
|
|
34
|
+
font-weight: 800;
|
|
35
|
+
line-height: 1.15;
|
|
36
|
+
letter-spacing: -0.025em;
|
|
37
|
+
color: var(--color-foreground, #0f172a);
|
|
38
|
+
margin: 0 0 1rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.about-stats__description {
|
|
42
|
+
font-size: clamp(1rem, 2vw, 1.25rem);
|
|
43
|
+
line-height: 1.7;
|
|
44
|
+
color: var(--color-muted, #64748b);
|
|
45
|
+
max-width: 36rem;
|
|
46
|
+
margin: 0 auto;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ── Stats Grid ──────────────────────────────────── */
|
|
50
|
+
|
|
51
|
+
.about-stats__grid {
|
|
52
|
+
display: grid;
|
|
53
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
54
|
+
gap: 1.5rem;
|
|
55
|
+
max-width: 1100px;
|
|
56
|
+
margin: 0 auto 3rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.about-stats__card {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
align-items: center;
|
|
63
|
+
text-align: center;
|
|
64
|
+
padding: 2.5rem 2rem;
|
|
65
|
+
background: var(--color-background, #ffffff);
|
|
66
|
+
border: 1px solid var(--color-border, rgba(0, 0, 0, 0.06));
|
|
67
|
+
border-radius: 1.25rem;
|
|
68
|
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.about-stats__card:hover {
|
|
72
|
+
transform: translateY(-4px);
|
|
73
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.about-stats__icon {
|
|
77
|
+
width: 3.5rem;
|
|
78
|
+
height: 3.5rem;
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
font-size: 1.75rem;
|
|
83
|
+
background: var(--color-accent-soft, rgba(99, 102, 241, 0.08));
|
|
84
|
+
border-radius: 0.875rem;
|
|
85
|
+
margin-bottom: 1.5rem;
|
|
86
|
+
transition: transform 0.4s ease;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.about-stats__card:hover .about-stats__icon {
|
|
90
|
+
transform: scale(1.1) rotate(6deg);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.about-stats__value {
|
|
94
|
+
font-size: clamp(2rem, 4vw, 3rem);
|
|
95
|
+
font-weight: 900;
|
|
96
|
+
line-height: 1.1;
|
|
97
|
+
color: var(--color-foreground, #0f172a);
|
|
98
|
+
margin-bottom: 0.25rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.about-stats__subtitle {
|
|
102
|
+
font-size: 0.875rem;
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
color: var(--color-muted, #64748b);
|
|
105
|
+
margin-bottom: 0.75rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.about-stats__card-desc {
|
|
109
|
+
font-size: 0.8125rem;
|
|
110
|
+
line-height: 1.6;
|
|
111
|
+
color: var(--color-muted, #64748b);
|
|
112
|
+
margin: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ── Trust Strip ──────────────────────────────────── */
|
|
116
|
+
|
|
117
|
+
.about-stats__trust {
|
|
118
|
+
max-width: 48rem;
|
|
119
|
+
margin: 0 auto;
|
|
120
|
+
text-align: center;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.about-stats__trust-headline {
|
|
124
|
+
font-size: 0.6875rem;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
text-transform: uppercase;
|
|
127
|
+
letter-spacing: 0.12em;
|
|
128
|
+
color: var(--color-muted, #64748b);
|
|
129
|
+
margin: 0 0 1rem;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.about-stats__trust-logos {
|
|
133
|
+
display: flex;
|
|
134
|
+
flex-wrap: wrap;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
gap: 1.5rem 2.5rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.about-stats__trust-label {
|
|
141
|
+
font-size: 1.125rem;
|
|
142
|
+
font-weight: 700;
|
|
143
|
+
color: var(--color-foreground, #0f172a);
|
|
144
|
+
opacity: 0.5;
|
|
145
|
+
transition: opacity 0.3s ease;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.about-stats__trust-label:hover {
|
|
149
|
+
opacity: 0.8;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ── Responsive ──────────────────────────────────── */
|
|
153
|
+
|
|
154
|
+
@media (max-width: 640px) {
|
|
155
|
+
.about-stats {
|
|
156
|
+
padding: 3rem 1.5rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.about-stats__grid {
|
|
160
|
+
grid-template-columns: 1fr;
|
|
161
|
+
gap: 1rem;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.about-stats__card {
|
|
165
|
+
padding: 2rem 1.5rem;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"name": "about-stats",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "section",
|
|
6
|
+
"description": "About section with centered narrative, stat cards with icons, and optional trust strip of partner logos",
|
|
7
|
+
"category": "about",
|
|
8
|
+
"tags": ["about", "stats", "team", "mission", "social-proof"],
|
|
9
|
+
"dependencies": {},
|
|
10
|
+
"requires": [],
|
|
11
|
+
"conflicts": [],
|
|
12
|
+
"envVars": [],
|
|
13
|
+
"variants": ["default", "no-trust-strip"],
|
|
14
|
+
"slots": ["trust-strip"],
|
|
15
|
+
"files": [
|
|
16
|
+
{
|
|
17
|
+
"source": "about-stats.astro",
|
|
18
|
+
"destination": "src/components/sections/about-stats.astro"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"source": "about-stats.css",
|
|
22
|
+
"destination": "src/styles/sections/about-stats.css"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"ai": {
|
|
26
|
+
"whenToUse": "Use for company/team introduction with key metrics. Great after a hero section to build credibility. Works well for SaaS, agencies, and product pages that want to combine narrative with social proof numbers.",
|
|
27
|
+
"whenNotToUse": "Don't use if you don't have meaningful stats to showcase. Avoid on minimal landing pages where a simple text block suffices.",
|
|
28
|
+
"pairsWith": ["hero-gradient", "hero-split", "stats-strip", "features-grid", "testimonials-wall"],
|
|
29
|
+
"contentSlots": {
|
|
30
|
+
"badge": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Small eyebrow label above the title (e.g. 'Our Mission')",
|
|
33
|
+
"maxLength": 30,
|
|
34
|
+
"example": "Our Mission"
|
|
35
|
+
},
|
|
36
|
+
"headline": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Section headline",
|
|
39
|
+
"maxLength": 80,
|
|
40
|
+
"example": "Crafting Digital Experiences That Matter"
|
|
41
|
+
},
|
|
42
|
+
"description": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Supporting paragraph about the company or team",
|
|
45
|
+
"maxLength": 300,
|
|
46
|
+
"example": "We're a team of designers and engineers obsessed with performance, accessibility, and beautiful interfaces."
|
|
47
|
+
},
|
|
48
|
+
"stats": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"description": "Stat cards with icon, value, subtitle, and description",
|
|
51
|
+
"minItems": 2,
|
|
52
|
+
"maxItems": 4,
|
|
53
|
+
"items": {
|
|
54
|
+
"icon": { "type": "string" },
|
|
55
|
+
"value": { "type": "string" },
|
|
56
|
+
"subtitle": { "type": "string" },
|
|
57
|
+
"description": { "type": "string" }
|
|
58
|
+
},
|
|
59
|
+
"example": [
|
|
60
|
+
{ "icon": "⚡", "value": "99.9%", "subtitle": "Uptime", "description": "Enterprise-grade reliability across all deployments." },
|
|
61
|
+
{ "icon": "🎨", "value": "50+", "subtitle": "Templates", "description": "Professionally designed, ready to customize." },
|
|
62
|
+
{ "icon": "🌍", "value": "120+", "subtitle": "Countries", "description": "Serving users around the globe." }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"trustLabels": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"description": "Optional list of partner/client names for social proof strip",
|
|
68
|
+
"items": { "type": "string" },
|
|
69
|
+
"example": ["Vercel", "Linear", "Stripe", "Notion"]
|
|
70
|
+
},
|
|
71
|
+
"trustHeadline": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "Label above the trust strip",
|
|
74
|
+
"example": "Trusted by industry leaders"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"badge": "Our Mission",
|
|
3
|
+
"headline": "Crafting Digital Experiences That Matter",
|
|
4
|
+
"description": "We're a team of designers and engineers obsessed with performance, accessibility, and beautiful interfaces that users love.",
|
|
5
|
+
"stats": [
|
|
6
|
+
{ "icon": "⚡", "value": "99.9%", "subtitle": "Uptime", "description": "Enterprise-grade reliability across all deployments." },
|
|
7
|
+
{ "icon": "🎨", "value": "50+", "subtitle": "Templates", "description": "Professionally designed, ready to customize." },
|
|
8
|
+
{ "icon": "🌍", "value": "120+", "subtitle": "Countries", "description": "Serving users around the globe." }
|
|
9
|
+
],
|
|
10
|
+
"trustHeadline": "Trusted by industry leaders",
|
|
11
|
+
"trustLabels": ["Vercel", "Linear", "Stripe", "Notion"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "astro:content";
|
|
2
|
+
|
|
3
|
+
const statCardSchema = z.object({
|
|
4
|
+
icon: z.string(),
|
|
5
|
+
value: z.string(),
|
|
6
|
+
subtitle: z.string(),
|
|
7
|
+
description: z.string().optional().default(""),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const aboutStatsSchema = z.object({
|
|
11
|
+
badge: z.string().max(30).optional().default(""),
|
|
12
|
+
headline: z.string().max(80),
|
|
13
|
+
description: z.string().max(300).optional().default(""),
|
|
14
|
+
stats: z.array(statCardSchema).min(2).max(4),
|
|
15
|
+
trustHeadline: z.string().optional().default(""),
|
|
16
|
+
trustLabels: z.array(z.string()).optional().default([]),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type AboutStatsContent = z.infer<typeof aboutStatsSchema>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* About Timeline — vertical milestone timeline with glowing nodes and alternating cards.
|
|
4
|
+
*
|
|
5
|
+
* ALL text is sourced from the content collection (zero hardcoded strings).
|
|
6
|
+
* Content is read from `src/content/sections/about-timeline.json`.
|
|
7
|
+
*/
|
|
8
|
+
import { getEntry } from "astro:content";
|
|
9
|
+
|
|
10
|
+
interface Milestone {
|
|
11
|
+
year: string;
|
|
12
|
+
icon: string;
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
isCta?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const locale = Astro.currentLocale ?? "en";
|
|
19
|
+
const entry = await getEntry("sections", `${locale}/about-timeline`);
|
|
20
|
+
const {
|
|
21
|
+
badge = "",
|
|
22
|
+
headline = "",
|
|
23
|
+
subheadline = "",
|
|
24
|
+
ctaText = "",
|
|
25
|
+
ctaHref = "#",
|
|
26
|
+
} = entry?.data ?? {};
|
|
27
|
+
const milestones = (entry?.data?.milestones ?? []) as Milestone[];
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<section class="about-timeline">
|
|
31
|
+
<div class="about-timeline__inner">
|
|
32
|
+
<!-- Header -->
|
|
33
|
+
<div class="about-timeline__header">
|
|
34
|
+
{badge && <span class="about-timeline__badge">{badge}</span>}
|
|
35
|
+
<h2 class="about-timeline__headline">{headline}</h2>
|
|
36
|
+
{subheadline && <p class="about-timeline__subheadline">{subheadline}</p>}
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Timeline -->
|
|
40
|
+
<div class="about-timeline__track">
|
|
41
|
+
{/* Central line */}
|
|
42
|
+
<div class="about-timeline__line" aria-hidden="true">
|
|
43
|
+
<div class="about-timeline__line-fill"></div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{/* Milestones */}
|
|
47
|
+
{milestones.map((m, i) => (
|
|
48
|
+
<div
|
|
49
|
+
class:list={[
|
|
50
|
+
"about-timeline__milestone",
|
|
51
|
+
i % 2 === 0 ? "about-timeline__milestone--left" : "about-timeline__milestone--right",
|
|
52
|
+
]}
|
|
53
|
+
data-timeline-index={i}
|
|
54
|
+
>
|
|
55
|
+
{/* Node */}
|
|
56
|
+
<div class="about-timeline__node">
|
|
57
|
+
<div class="about-timeline__node-ring">
|
|
58
|
+
<div class="about-timeline__node-icon">{m.icon}</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="about-timeline__node-pulse" aria-hidden="true"></div>
|
|
61
|
+
<span class="about-timeline__year">{m.year}</span>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
{/* Card */}
|
|
65
|
+
<div class:list={["about-timeline__card", m.isCta && "about-timeline__card--cta"]}>
|
|
66
|
+
<h3 class="about-timeline__card-title">{m.title}</h3>
|
|
67
|
+
<p class="about-timeline__card-desc">{m.description}</p>
|
|
68
|
+
{m.isCta && ctaText && (
|
|
69
|
+
<a href={ctaHref} class="about-timeline__card-cta">
|
|
70
|
+
{ctaText}
|
|
71
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
72
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
|
73
|
+
</svg>
|
|
74
|
+
</a>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
function initTimeline() {
|
|
85
|
+
const milestones = document.querySelectorAll("[data-timeline-index]");
|
|
86
|
+
const observer = new IntersectionObserver(
|
|
87
|
+
(entries) => {
|
|
88
|
+
entries.forEach((entry) => {
|
|
89
|
+
if (entry.isIntersecting) {
|
|
90
|
+
entry.target.classList.add("in-view");
|
|
91
|
+
observer.unobserve(entry.target);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
{ threshold: 0.3, rootMargin: "0px 0px -10% 0px" }
|
|
96
|
+
);
|
|
97
|
+
milestones.forEach((m) => observer.observe(m));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
initTimeline();
|
|
101
|
+
document.addEventListener("astro:after-swap", initTimeline);
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<style>
|
|
105
|
+
@import "../../styles/sections/about-timeline.css";
|
|
106
|
+
</style>
|