Compare commits
10 Commits
ce314adf2d
...
8dec3e792a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dec3e792a | ||
|
|
9cded54ce3 | ||
|
|
21c1122369 | ||
|
|
5f06e5ccad | ||
|
|
04b930de62 | ||
|
|
b1b6b52f3f | ||
|
|
0ad067847c | ||
|
|
d67a89bacb | ||
|
|
54bbb46755 | ||
|
|
29c2d13776 |
@@ -1,62 +0,0 @@
|
|||||||
---
|
|
||||||
interface Tile {
|
|
||||||
outcome: string;
|
|
||||||
capability: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tile.capability is a compile-time constant defined below — never feed
|
|
||||||
// user-supplied or CMS content into set:html further down (XSS risk).
|
|
||||||
const tiles: Tile[] = [
|
|
||||||
{
|
|
||||||
outcome: 'Ship integrations, then sleep.',
|
|
||||||
capability:
|
|
||||||
'Every route, every processor, every exchange — traced automatically. When something breaks at 3 AM, the answer is already waiting for you. So you do not have to be.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
outcome: 'Debug in daylight.',
|
|
||||||
capability:
|
|
||||||
'Replay the exact exchange that failed. Follow a single request across services. See payloads before and after each processor. The pieces your ops team needs at 3 AM, captured already — so 3 AM stays quiet.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
outcome: 'Keep what you built. Keep what you chose.',
|
|
||||||
capability:
|
|
||||||
'You picked Apache Camel on purpose — open, portable, yours. Cameleer runs and understands your Camel apps as they are. No code changes, no SDK, no rewrite, no lock-in.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
---
|
|
||||||
<section class="border-b border-border">
|
|
||||||
<div class="max-w-content mx-auto px-6 py-20 md:py-24">
|
|
||||||
<div class="grid md:grid-cols-3 gap-6 md:gap-8">
|
|
||||||
{tiles.map((tile, i) => (
|
|
||||||
<div
|
|
||||||
class="tile rounded-lg border border-border bg-bg-elevated p-7 md:p-8 transition-all duration-200 ease-out hover:-translate-y-0.5 hover:border-accent/40 hover:shadow-[0_12px_32px_-12px_rgba(240,180,41,0.18)]"
|
|
||||||
style={`--tile-delay:${i * 110}ms`}
|
|
||||||
>
|
|
||||||
<h2 class="text-xl md:text-2xl font-bold text-text mb-3 leading-snug">
|
|
||||||
{tile.outcome}
|
|
||||||
</h2>
|
|
||||||
<p class="text-text-muted leading-relaxed" set:html={tile.capability.replace(/`([^`]+)`/g, '<code class="font-mono text-accent bg-bg border border-border rounded px-1 py-0.5 text-sm">$1</code>')}></p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
@keyframes tile-rise {
|
|
||||||
from { opacity: 0; transform: translateY(18px); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
|
||||||
.tile {
|
|
||||||
opacity: 0;
|
|
||||||
animation: tile-rise 520ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
||||||
animation-delay: var(--tile-delay, 0ms);
|
|
||||||
}
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.tile {
|
|
||||||
opacity: 1;
|
|
||||||
animation: none;
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -6,13 +6,13 @@ import TopographicBg from '../TopographicBg.astro';
|
|||||||
<TopographicBg opacity={0.18} lines={6} />
|
<TopographicBg opacity={0.18} lines={6} />
|
||||||
<div class="relative max-w-content mx-auto px-6 py-24 md:py-32 text-center">
|
<div class="relative max-w-content mx-auto px-6 py-24 md:py-32 text-center">
|
||||||
<h2 class="text-display font-bold text-text mb-6">
|
<h2 class="text-display font-bold text-text mb-6">
|
||||||
Your camels called. Time to ride.
|
Ship integrations. Sleep through the night.
|
||||||
</h2>
|
</h2>
|
||||||
<p class="text-lg md:text-xl text-text-muted max-w-prose mx-auto mb-10">
|
<p class="text-lg md:text-xl text-text-muted max-w-prose mx-auto mb-10">
|
||||||
14-day free trial. Your first Camel app, hosted, traced, and running in under ten minutes. No code changes. No camels harmed.
|
14-day free trial. Your first Camel app, hosted, traced, and running in under ten minutes. No code changes.
|
||||||
</p>
|
</p>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<CTAButtons size="lg" />
|
<CTAButtons size="lg" showSecondary={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -2,6 +2,19 @@
|
|||||||
import CTAButtons from '../CTAButtons.astro';
|
import CTAButtons from '../CTAButtons.astro';
|
||||||
import TopographicBg from '../TopographicBg.astro';
|
import TopographicBg from '../TopographicBg.astro';
|
||||||
import Lightbox from '../Lightbox.astro';
|
import Lightbox from '../Lightbox.astro';
|
||||||
|
|
||||||
|
interface Pin {
|
||||||
|
label: string;
|
||||||
|
body: string;
|
||||||
|
top: string;
|
||||||
|
left: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pins: Pin[] = [
|
||||||
|
{ label: '01', body: 'Correlation ID — click to follow one exchange across services.', top: '14%', left: '12%' },
|
||||||
|
{ label: '02', body: 'Failure in context — circuit breaker tripped, fallback ran, tried backend:80.', top: '46%', left: '52%' },
|
||||||
|
{ label: '03', body: 'Full error pinned — exception, stack trace, headers, payload.', top: '78%', left: '78%' },
|
||||||
|
];
|
||||||
---
|
---
|
||||||
<section class="relative overflow-hidden border-b border-border">
|
<section class="relative overflow-hidden border-b border-border">
|
||||||
<TopographicBg opacity={0.22} lines={11} />
|
<TopographicBg opacity={0.22} lines={11} />
|
||||||
@@ -22,19 +35,20 @@ import Lightbox from '../Lightbox.astro';
|
|||||||
<span aria-hidden="true" class="text-base">✦</span>
|
<span aria-hidden="true" class="text-base">✦</span>
|
||||||
Your camels called. They want a GPS.
|
Your camels called. They want a GPS.
|
||||||
</p>
|
</p>
|
||||||
<h1
|
<h1 class="font-bold text-text mb-6 hero-h1">
|
||||||
class="font-bold text-text mb-6 hero-rotator"
|
Ship Camel integrations. Sleep through the night.
|
||||||
aria-live="off"
|
|
||||||
data-hero-rotator
|
|
||||||
>
|
|
||||||
<span class="hero-line" data-active aria-hidden="false">Run Apache Camel without running Apache Camel.</span>
|
|
||||||
<span class="hero-line" aria-hidden="true">Camel integrations, minus the baggage.</span>
|
|
||||||
<span class="hero-line" aria-hidden="true">Your camels, our caravan. You just ride.</span>
|
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-lg md:text-xl text-text-muted max-w-prose leading-relaxed mb-10">
|
<p class="text-lg md:text-xl text-text-muted max-w-prose leading-relaxed mb-8">
|
||||||
The hosted home for your Camel integrations — with deep tracing, replay, and live control built in. Because you chose Camel to stay free, not to stay up all night.
|
Cameleer is the hosted runtime and observability platform for Apache Camel — auto-traced, replay-ready, cross-service correlated. The 3 AM page becomes a 30-second answer.
|
||||||
|
</p>
|
||||||
|
<CTAButtons
|
||||||
|
size="lg"
|
||||||
|
secondaryLabel="See it in action ↓"
|
||||||
|
secondaryHref="#walkthrough"
|
||||||
|
/>
|
||||||
|
<p class="mt-4 font-mono text-xs text-text-faint">
|
||||||
|
14-day trial · from €20/mo · no credit card
|
||||||
</p>
|
</p>
|
||||||
<CTAButtons size="lg" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="lg:col-span-7 relative">
|
<div class="lg:col-span-7 relative">
|
||||||
<div class="hero-shot relative rounded-lg border border-border-strong bg-bg-elevated overflow-hidden">
|
<div class="hero-shot relative rounded-lg border border-border-strong bg-bg-elevated overflow-hidden">
|
||||||
@@ -46,7 +60,24 @@ import Lightbox from '../Lightbox.astro';
|
|||||||
loading="eager"
|
loading="eager"
|
||||||
/>
|
/>
|
||||||
<div class="absolute inset-0 ring-1 ring-inset ring-accent/10 pointer-events-none rounded-lg"></div>
|
<div class="absolute inset-0 ring-1 ring-inset ring-accent/10 pointer-events-none rounded-lg"></div>
|
||||||
|
{pins.map((pin) => (
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
class="hero-pin absolute inline-flex items-center justify-center w-7 h-7 rounded-full bg-accent text-bg font-mono text-xs font-bold pointer-events-none"
|
||||||
|
style={`top:${pin.top};left:${pin.left}`}
|
||||||
|
>
|
||||||
|
{pin.label}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<ul class="hero-pin-legend mt-5 grid sm:grid-cols-3 gap-3 text-text-muted">
|
||||||
|
{pins.map((pin) => (
|
||||||
|
<li class="flex items-start gap-2 text-sm leading-snug">
|
||||||
|
<span class="font-mono text-accent text-xs mt-0.5">{pin.label}</span>
|
||||||
|
<span>{pin.body}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
<div aria-hidden="true" class="hero-shot-glow"></div>
|
<div aria-hidden="true" class="hero-shot-glow"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,36 +85,21 @@ import Lightbox from '../Lightbox.astro';
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Rotating H1 — fluid size + fade transition */
|
.hero-h1 {
|
||||||
.hero-rotator {
|
|
||||||
font-size: clamp(2.25rem, 4.5vw, 4rem);
|
font-size: clamp(2.25rem, 4.5vw, 4rem);
|
||||||
line-height: 1.05;
|
line-height: 1.05;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
/* Reserve enough vertical space that a 2-line wrap of the longest line
|
|
||||||
does not push the page on swap (mobile wraps line 1 to 2 lines). */
|
|
||||||
min-height: 2.5em;
|
|
||||||
}
|
}
|
||||||
.hero-line {
|
|
||||||
display: block;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 700ms ease-in-out;
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
}
|
|
||||||
.hero-line[data-active] {
|
|
||||||
opacity: 1;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Product screenshot frame — subtle dropshadow + amber glow behind */
|
|
||||||
.hero-shot {
|
.hero-shot {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 1px 0 rgba(240, 180, 41, 0.08) inset,
|
0 1px 0 rgba(240, 180, 41, 0.08) inset,
|
||||||
0 30px 60px -20px rgba(0, 0, 0, 0.6),
|
0 30px 60px -20px rgba(0, 0, 0, 0.6),
|
||||||
0 10px 25px -10px rgba(0, 0, 0, 0.5);
|
0 10px 25px -10px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
.hero-pin {
|
||||||
|
box-shadow: 0 0 0 4px rgba(240, 180, 41, 0.22), 0 4px 10px -2px rgba(0, 0, 0, 0.5);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
.hero-shot-glow {
|
.hero-shot-glow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 10% -5% 10% -5%;
|
inset: 10% -5% 10% -5%;
|
||||||
@@ -96,7 +112,6 @@ import Lightbox from '../Lightbox.astro';
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Slow sway on the mark — tasteful, not distracting */
|
|
||||||
@keyframes hero-mark-sway {
|
@keyframes hero-mark-sway {
|
||||||
0%, 100% { transform: translateY(0) rotate(0deg); }
|
0%, 100% { transform: translateY(0) rotate(0deg); }
|
||||||
50% { transform: translateY(-2px) rotate(-1.5deg); }
|
50% { transform: translateY(-2px) rotate(-1.5deg); }
|
||||||
@@ -107,34 +122,6 @@ import Lightbox from '../Lightbox.astro';
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.hero-line { transition: none; }
|
|
||||||
.hero-mark { animation: none; }
|
.hero-mark { animation: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Hero rotating headline. Bundled by Astro (CSP: script-src 'self').
|
|
||||||
const rotator = document.querySelector<HTMLElement>('[data-hero-rotator]');
|
|
||||||
if (rotator) {
|
|
||||||
const lines = Array.from(rotator.querySelectorAll<HTMLElement>('.hero-line'));
|
|
||||||
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
||||||
if (!reduced && lines.length > 1) {
|
|
||||||
let index = 0;
|
|
||||||
let paused = false;
|
|
||||||
const pause = () => { paused = true; };
|
|
||||||
const resume = () => { paused = false; };
|
|
||||||
rotator.addEventListener('mouseenter', pause);
|
|
||||||
rotator.addEventListener('mouseleave', resume);
|
|
||||||
rotator.addEventListener('focusin', pause);
|
|
||||||
rotator.addEventListener('focusout', resume);
|
|
||||||
setInterval(() => {
|
|
||||||
if (paused) return;
|
|
||||||
lines[index].removeAttribute('data-active');
|
|
||||||
lines[index].setAttribute('aria-hidden', 'true');
|
|
||||||
index = (index + 1) % lines.length;
|
|
||||||
lines[index].setAttribute('data-active', '');
|
|
||||||
lines[index].setAttribute('aria-hidden', 'false');
|
|
||||||
}, 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const steps: Step[] = [
|
|||||||
{
|
{
|
||||||
n: '01',
|
n: '01',
|
||||||
title: 'Point us at your Camel app',
|
title: 'Point us at your Camel app',
|
||||||
body: 'Drop it in, or connect one you already run. No code changes. No SDK. Nothing to rewrite.',
|
body: 'Drop it in, or connect one you already run. No code changes.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '02',
|
n: '02',
|
||||||
@@ -20,7 +20,7 @@ const steps: Step[] = [
|
|||||||
{
|
{
|
||||||
n: '03',
|
n: '03',
|
||||||
title: 'Watch it run',
|
title: 'Watch it run',
|
||||||
body: 'Browse executions, tap live traffic, replay failed exchanges, follow flows across services. Nothing to instrument. Nothing to maintain.',
|
body: 'Browse executions, tap live traffic, replay failed exchanges, follow flows across services.',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
@@ -29,7 +29,7 @@ const steps: Step[] = [
|
|||||||
<div class="max-w-2xl mb-16">
|
<div class="max-w-2xl mb-16">
|
||||||
<p class="text-cyan font-mono text-xs tracking-[0.25em] uppercase mb-4">For engineers</p>
|
<p class="text-cyan font-mono text-xs tracking-[0.25em] uppercase mb-4">For engineers</p>
|
||||||
<h2 class="text-hero font-bold text-text mb-4">How it works</h2>
|
<h2 class="text-hero font-bold text-text mb-4">How it works</h2>
|
||||||
<p class="text-text-muted text-lg">Three steps. No code changes. Nothing to maintain.</p>
|
<p class="text-text-muted text-lg">Three steps. Nothing to maintain.</p>
|
||||||
</div>
|
</div>
|
||||||
<ol class="grid md:grid-cols-3 gap-6 md:gap-8">
|
<ol class="grid md:grid-cols-3 gap-6 md:gap-8">
|
||||||
{steps.map((step) => (
|
{steps.map((step) => (
|
||||||
|
|||||||
@@ -21,27 +21,13 @@ const tiers: Tier[] = [
|
|||||||
cta: 'Start free trial',
|
cta: 'Start free trial',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'MID',
|
name: 'Starter',
|
||||||
price: '20 € /mo',
|
price: '20 € /mo',
|
||||||
sub: '2 environments · 10 apps · 7-day retention',
|
sub: '2 environments · 10 apps · 7-day retention',
|
||||||
href: auth.signUpUrl,
|
href: auth.signUpUrl,
|
||||||
cta: 'Start on MID',
|
cta: 'Start on Starter',
|
||||||
highlight: true,
|
highlight: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'HIGH',
|
|
||||||
price: 'Contact',
|
|
||||||
sub: 'Unlimited envs · 50 apps · 90-day retention · Debugger, Replay',
|
|
||||||
href: auth.salesMailto,
|
|
||||||
cta: 'Talk to sales',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'BUSINESS',
|
|
||||||
price: 'Contact',
|
|
||||||
sub: 'Unlimited everything · 365-day retention · all features',
|
|
||||||
href: auth.salesMailto,
|
|
||||||
cta: 'Talk to sales',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
---
|
---
|
||||||
<section class="border-b border-border">
|
<section class="border-b border-border">
|
||||||
@@ -51,15 +37,14 @@ const tiers: Tier[] = [
|
|||||||
<h2 class="text-hero font-bold text-text mb-4">Start free. Grow when you need to.</h2>
|
<h2 class="text-hero font-bold text-text mb-4">Start free. Grow when you need to.</h2>
|
||||||
<p class="text-text-muted text-lg">
|
<p class="text-text-muted text-lg">
|
||||||
No credit card. No sales call. Just a working trial in ten minutes.
|
No credit card. No sales call. Just a working trial in ten minutes.
|
||||||
<a href="/pricing" class="text-accent hover:underline">See full comparison →</a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-5 lg:items-stretch">
|
<div class="grid md:grid-cols-2 gap-5 lg:items-stretch max-w-3xl">
|
||||||
{tiers.map((tier) => (
|
{tiers.map((tier) => (
|
||||||
<div
|
<div
|
||||||
class={`relative rounded-lg bg-bg-elevated p-6 flex flex-col transition-all duration-200 ease-out hover:-translate-y-0.5
|
class={`relative rounded-lg bg-bg-elevated p-6 flex flex-col transition-all duration-200 ease-out hover:-translate-y-0.5
|
||||||
${tier.highlight
|
${tier.highlight
|
||||||
? 'ring-2 ring-accent shadow-[0_20px_50px_-20px_rgba(240,180,41,0.35)] lg:-translate-y-2 lg:pt-8 lg:pb-7'
|
? 'ring-2 ring-accent shadow-[0_20px_50px_-20px_rgba(240,180,41,0.35)] md:-translate-y-2 md:pt-8 md:pb-7'
|
||||||
: 'border border-border hover:border-accent/40 hover:shadow-[0_12px_32px_-12px_rgba(240,180,41,0.18)]'}`}
|
: 'border border-border hover:border-accent/40 hover:shadow-[0_12px_32px_-12px_rgba(240,180,41,0.18)]'}`}
|
||||||
>
|
>
|
||||||
{tier.highlight && (
|
{tier.highlight && (
|
||||||
@@ -85,5 +70,10 @@ const tiers: Tier[] = [
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<p class="mt-8 font-mono text-sm text-text-muted">
|
||||||
|
<a href="/pricing" class="text-cyan hover:text-accent transition-colors">
|
||||||
|
See all plans (Scale, Enterprise) →
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
---
|
|
||||||
import TopographicBg from '../TopographicBg.astro';
|
|
||||||
import Lightbox from '../Lightbox.astro';
|
|
||||||
|
|
||||||
interface Callout {
|
|
||||||
title: string;
|
|
||||||
body: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const callouts: Callout[] = [
|
|
||||||
{
|
|
||||||
title: 'Cross-service correlation.',
|
|
||||||
body: 'Every exchange carries its correlation ID forward. One click jumps to what the downstream route did with the same message — 610 ms later.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Runtime detail, not guesswork.',
|
|
||||||
body: 'Circuit breaker tripped. Fallback path ran. The request tried to reach backend:80. The kind of pieces a 3 AM page actually needs — already captured.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'The whole story of a failure.',
|
|
||||||
body: 'Exception class, message, stack trace, headers, payload — all pinned to the exchange. No log-grepping tour. No SSH into the pod.',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
---
|
|
||||||
<section class="relative overflow-hidden border-b border-border bg-bg">
|
|
||||||
<TopographicBg opacity={0.14} lines={7} />
|
|
||||||
<div class="relative max-w-content mx-auto px-6 py-24 md:py-32">
|
|
||||||
<div class="max-w-3xl mb-14 md:mb-20">
|
|
||||||
<p class="text-cyan font-mono text-xs tracking-[0.25em] uppercase mb-4">When it breaks</p>
|
|
||||||
<h2 class="text-hero font-bold text-text mb-5">
|
|
||||||
When something breaks, the answer is already waiting.
|
|
||||||
</h2>
|
|
||||||
<p class="text-lg text-text-muted leading-relaxed">
|
|
||||||
Follow a single exchange from ingestion to failure. See the route it took, the fallback that ran, the stack trace, the correlated downstream work — in one place. Without writing a line of tracing code.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid lg:grid-cols-12 gap-10 lg:gap-14 items-start">
|
|
||||||
<figure class="lg:col-span-8 relative">
|
|
||||||
<div class="showcase-shot relative rounded-lg border border-border-strong bg-bg-elevated overflow-hidden">
|
|
||||||
<Lightbox
|
|
||||||
src="/product/error-detail.png"
|
|
||||||
alt="Cameleer Mission Control — complex fulfillment route with circuit breaker, fallback, correlated audit route, and full error context"
|
|
||||||
width={1920}
|
|
||||||
height={945}
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
<div class="absolute inset-0 ring-1 ring-inset ring-accent/10 pointer-events-none rounded-lg"></div>
|
|
||||||
</div>
|
|
||||||
<div aria-hidden="true" class="showcase-shot-glow"></div>
|
|
||||||
<figcaption class="sr-only">Screenshot of a failed exchange in Cameleer, showing the full execution graph, fallback path, and exception context.</figcaption>
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
<ul class="lg:col-span-4 space-y-7 lg:pt-4">
|
|
||||||
{callouts.map((c, i) => (
|
|
||||||
<li class="relative pl-10">
|
|
||||||
<span
|
|
||||||
class="absolute left-0 top-0 inline-flex items-center justify-center w-7 h-7 rounded-full border border-accent/40 bg-accent/10 text-accent font-mono text-xs"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
{String(i + 1).padStart(2, '0')}
|
|
||||||
</span>
|
|
||||||
<h3 class="text-text font-semibold mb-1.5">{c.title}</h3>
|
|
||||||
<p class="text-text-muted leading-relaxed">{c.body}</p>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.showcase-shot {
|
|
||||||
box-shadow:
|
|
||||||
0 1px 0 rgba(240, 180, 41, 0.08) inset,
|
|
||||||
0 40px 80px -30px rgba(0, 0, 0, 0.7),
|
|
||||||
0 15px 35px -15px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
.showcase-shot-glow {
|
|
||||||
position: absolute;
|
|
||||||
inset: -5% -8% -5% -8%;
|
|
||||||
background: radial-gradient(
|
|
||||||
55% 55% at 45% 50%,
|
|
||||||
rgba(92, 200, 255, 0.10),
|
|
||||||
rgba(240, 180, 41, 0.08) 40%,
|
|
||||||
transparent 75%
|
|
||||||
);
|
|
||||||
filter: blur(50px);
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
38
src/components/sections/SocialProofStrip.astro
Normal file
38
src/components/sections/SocialProofStrip.astro
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
import { getAuthConfig } from '../../config/auth';
|
||||||
|
|
||||||
|
const auth = getAuthConfig();
|
||||||
|
|
||||||
|
// PENDING — must be filled in before publish:
|
||||||
|
// 1. [Founder Name] placeholder below.
|
||||||
|
// 2. The "ex-nJAMS" mention is gated on Hendrik's trademark review
|
||||||
|
// (same pattern as WhyUs.astro §10 caveat). If the review is not
|
||||||
|
// cleared by publish time, drop the " · ex-nJAMS" suffix from the
|
||||||
|
// attribution line.
|
||||||
|
const founderName = '[Founder Name]';
|
||||||
|
const designPartnerSubject = 'Design partner enquiry — Cameleer';
|
||||||
|
const designPartnerHref = `mailto:${auth.salesEmail}?subject=${encodeURIComponent(designPartnerSubject)}`;
|
||||||
|
---
|
||||||
|
<section class="border-b border-border">
|
||||||
|
<div class="max-w-content mx-auto px-6 py-16 md:py-20">
|
||||||
|
<div class="max-w-3xl mx-auto">
|
||||||
|
<p class="text-accent font-mono text-xs tracking-[0.25em] uppercase mb-4">
|
||||||
|
// Built by people who've done this before
|
||||||
|
</p>
|
||||||
|
<blockquote class="border-l-[3px] border-accent pl-5 max-w-[62ch]">
|
||||||
|
<p class="text-lg md:text-xl text-text italic leading-relaxed mb-3">
|
||||||
|
“We spent 15 years building integration monitoring for banks that couldn’t afford downtime. Cameleer is what we’d build today — purpose-built for Apache Camel, no retrofit.”
|
||||||
|
</p>
|
||||||
|
<footer class="text-sm font-mono text-text-muted">
|
||||||
|
— <span class="text-text">{founderName}</span>, co-founder · ex-nJAMS
|
||||||
|
</footer>
|
||||||
|
</blockquote>
|
||||||
|
<a
|
||||||
|
href={designPartnerHref}
|
||||||
|
class="inline-flex items-center gap-2 mt-7 font-mono text-sm text-cyan hover:text-accent transition-colors"
|
||||||
|
>
|
||||||
|
Apply to the design-partner program <span aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
104
src/components/sections/ThreeAmWalkthrough.astro
Normal file
104
src/components/sections/ThreeAmWalkthrough.astro
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
---
|
||||||
|
import Lightbox from '../Lightbox.astro';
|
||||||
|
|
||||||
|
interface Callout {
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const callouts: Callout[] = [
|
||||||
|
{
|
||||||
|
title: 'Cross-service correlation.',
|
||||||
|
body: 'Every exchange carries its correlation ID forward. One click jumps to what the downstream route did with the same message.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Runtime detail, not guesswork.',
|
||||||
|
body: 'Circuit breaker tripped. Fallback path ran. Request tried backend:80. The pieces a 3 AM page actually needs — already captured.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'The whole story of a failure.',
|
||||||
|
body: 'Exception class, message, stack trace, headers, payload — all pinned to the exchange. No log-grepping tour.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
---
|
||||||
|
<section id="walkthrough" class="border-b border-border bg-bg">
|
||||||
|
<div class="max-w-content mx-auto px-6 py-24 md:py-32">
|
||||||
|
<div class="max-w-3xl mb-14 md:mb-20">
|
||||||
|
<p class="text-cyan font-mono text-xs tracking-[0.25em] uppercase mb-4">// When something breaks</p>
|
||||||
|
<h2 class="text-hero font-bold text-text mb-5">
|
||||||
|
The 3 AM page. With and without Cameleer.
|
||||||
|
</h2>
|
||||||
|
<p class="text-lg text-text-muted leading-relaxed">
|
||||||
|
Same Camel app. Same failed exchange. Different night.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid md:grid-cols-2 gap-6 md:gap-8 items-stretch">
|
||||||
|
<div class="without-card relative rounded-lg border border-dashed border-border-strong bg-bg overflow-hidden">
|
||||||
|
<div class="px-5 pt-5 pb-3 font-mono text-[11px] tracking-[0.2em] uppercase text-text-faint border-b border-border">
|
||||||
|
Without Cameleer · 03:12 AM
|
||||||
|
</div>
|
||||||
|
<pre class="font-mono text-[13px] leading-[1.65] text-text-muted px-5 py-5 overflow-x-auto whitespace-pre"><code><span class="text-text">$</span> kubectl logs camel-router-7d4f8c
|
||||||
|
<span class="text-rose">ERROR</span> org.apache.camel.CamelExecutionException
|
||||||
|
at org.apache.camel.processor.SendProcessor.process
|
||||||
|
at org.apache.camel.processor.Pipeline.process
|
||||||
|
...
|
||||||
|
|
||||||
|
<span class="text-text">$</span> grep "order-842" *.log
|
||||||
|
router-3.log: <span class="text-accent">WARN</span> exchange order-842 stuck in saga-fulfillment
|
||||||
|
router-3.log: <span class="text-rose">ERROR</span> processor backend:80 → connect timeout
|
||||||
|
|
||||||
|
<span class="text-text">$</span> ssh prod-integration-3
|
||||||
|
prod-integration-3 $ kubectl logs ...
|
||||||
|
|
||||||
|
> <span class="text-cyan">slack #integration-team</span>
|
||||||
|
"anyone know why order-842 is stuck??"
|
||||||
|
<span class="text-text-faint">[3 of 4 reactions, no answer]</span>
|
||||||
|
|
||||||
|
<span class="text-accent">~47 min later: someone wakes up an SRE.</span></code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<figure class="with-card relative rounded-lg border border-border-strong bg-bg-elevated overflow-hidden">
|
||||||
|
<div class="px-5 pt-5 pb-3 font-mono text-[11px] tracking-[0.2em] uppercase text-accent border-b border-border">
|
||||||
|
With Cameleer · 30 sec
|
||||||
|
</div>
|
||||||
|
<Lightbox
|
||||||
|
src="/product/error-detail.png"
|
||||||
|
alt="Cameleer Mission Control — failed exchange order-842 with full execution context"
|
||||||
|
width={1920}
|
||||||
|
height={945}
|
||||||
|
loading="lazy"
|
||||||
|
imgClass="block w-full h-auto"
|
||||||
|
/>
|
||||||
|
<div class="px-5 py-4 font-mono text-[13px] leading-[1.6] text-text-muted border-t border-border">
|
||||||
|
<span class="text-accent">▸</span> Open exchange <span class="text-text">order-842</span> → see the failure pinned → click <span class="text-text">Replay</span> after fix.
|
||||||
|
</div>
|
||||||
|
<div class="absolute inset-0 ring-1 ring-inset ring-accent/10 pointer-events-none rounded-lg"></div>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="grid md:grid-cols-3 gap-6 md:gap-8 mt-14">
|
||||||
|
{callouts.map((c, i) => (
|
||||||
|
<li class="relative pl-10">
|
||||||
|
<span
|
||||||
|
class="absolute left-0 top-0 inline-flex items-center justify-center w-7 h-7 rounded-full border border-accent/40 bg-accent/10 text-accent font-mono text-xs"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{String(i + 1).padStart(2, '0')}
|
||||||
|
</span>
|
||||||
|
<h3 class="text-text font-semibold mb-1.5">{c.title}</h3>
|
||||||
|
<p class="text-text-muted leading-relaxed">{c.body}</p>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.with-card {
|
||||||
|
box-shadow:
|
||||||
|
0 1px 0 rgba(240, 180, 41, 0.08) inset,
|
||||||
|
0 30px 60px -25px rgba(0, 0, 0, 0.7),
|
||||||
|
0 12px 30px -12px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,32 +20,15 @@
|
|||||||
So when you ask "why did this exchange fail?", you get an answer, not a log tail. And you can reach back into a running app to replay a message, deep-trace a correlation ID, or toggle recording — observability that does things, not just shows them.
|
So when you ask "why did this exchange fail?", you get an answer, not a log tail. And you can reach back into a running app to replay a message, deep-trace a correlation ID, or toggle recording — observability that does things, not just shows them.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative overflow-hidden rounded-lg border border-border bg-bg-elevated p-8 transition-all duration-200 ease-out hover:-translate-y-0.5 hover:border-accent/40 hover:shadow-[0_12px_32px_-12px_rgba(240,180,41,0.18)]">
|
<div class="rounded-lg border border-border bg-bg-elevated p-8 transition-all duration-200 ease-out hover:-translate-y-0.5 hover:border-accent/40 hover:shadow-[0_12px_32px_-12px_rgba(240,180,41,0.18)]">
|
||||||
<div
|
<h3 class="text-xl font-bold text-text mb-4">Built by people who've operated integration in production for 15 years.</h3>
|
||||||
aria-hidden="true"
|
|
||||||
class="pointer-events-none select-none absolute -top-6 -right-4 font-mono font-bold leading-none tracking-tight text-accent/[0.04] text-[7rem] md:text-[9rem]"
|
|
||||||
>
|
|
||||||
03:00
|
|
||||||
</div>
|
|
||||||
<div class="relative">
|
|
||||||
<div class="flex items-center gap-2.5 mb-5 font-mono text-xs">
|
|
||||||
<span aria-hidden="true" class="relative inline-flex w-1.5 h-1.5">
|
|
||||||
<span class="absolute inset-0 rounded-full bg-accent"></span>
|
|
||||||
<span class="absolute inset-0 rounded-full bg-accent/60 animate-ping [animation-duration:2.4s]"></span>
|
|
||||||
</span>
|
|
||||||
<span class="text-accent tabular-nums tracking-wide">03:00:47.218</span>
|
|
||||||
<span class="text-text-faint">·</span>
|
|
||||||
<span class="text-text-faint uppercase tracking-[0.2em]">ops desk</span>
|
|
||||||
</div>
|
|
||||||
<h3 class="text-xl font-bold text-text mb-4">Built by people who know what 3 AM looks like.</h3>
|
|
||||||
<p class="text-text-muted leading-relaxed mb-4">
|
<p class="text-text-muted leading-relaxed mb-4">
|
||||||
We spent years building integration monitoring for banks, insurers, and logistics operators — the kind of shops where a stuck exchange at 3 AM means someone's phone is ringing. We know what integration teams actually need then, and what they never use.
|
We spent over a decade building integration monitoring for banks, insurers, and logistics operators — the kind of shops where a stuck exchange is a regulatory event, not just an inconvenience.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-text-muted leading-relaxed">
|
<p class="text-text-muted leading-relaxed">
|
||||||
Cameleer is what we would build today, purpose-built for Apache Camel. No legacy, no retrofit, no assumptions about a generic middleware platform.
|
Cameleer is what we'd build today, purpose-built for Apache Camel. No legacy, no retrofit, no assumptions about a generic middleware platform.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -3,22 +3,22 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
|||||||
import SiteHeader from '../components/SiteHeader.astro';
|
import SiteHeader from '../components/SiteHeader.astro';
|
||||||
import SiteFooter from '../components/SiteFooter.astro';
|
import SiteFooter from '../components/SiteFooter.astro';
|
||||||
import Hero from '../components/sections/Hero.astro';
|
import Hero from '../components/sections/Hero.astro';
|
||||||
import DualValueProps from '../components/sections/DualValueProps.astro';
|
import SocialProofStrip from '../components/sections/SocialProofStrip.astro';
|
||||||
import ProductShowcase from '../components/sections/ProductShowcase.astro';
|
import ThreeAmWalkthrough from '../components/sections/ThreeAmWalkthrough.astro';
|
||||||
import HowItWorks from '../components/sections/HowItWorks.astro';
|
import HowItWorks from '../components/sections/HowItWorks.astro';
|
||||||
import WhyUs from '../components/sections/WhyUs.astro';
|
import WhyUs from '../components/sections/WhyUs.astro';
|
||||||
import PricingTeaser from '../components/sections/PricingTeaser.astro';
|
import PricingTeaser from '../components/sections/PricingTeaser.astro';
|
||||||
import FinalCTA from '../components/sections/FinalCTA.astro';
|
import FinalCTA from '../components/sections/FinalCTA.astro';
|
||||||
---
|
---
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
title="Cameleer — Run Apache Camel without running Apache Camel"
|
title="Cameleer — Ship Camel integrations. Sleep through the night."
|
||||||
description="The hosted home for your Camel integrations — with deep tracing, replay, and live control built in. Because you chose Camel to stay free, not to stay up all night."
|
description="The hosted runtime and observability platform for Apache Camel. Auto-traced, replay-ready, cross-service correlated — so the 3 AM page becomes a 30-second answer."
|
||||||
>
|
>
|
||||||
<SiteHeader />
|
<SiteHeader />
|
||||||
<main>
|
<main>
|
||||||
<Hero />
|
<Hero />
|
||||||
<DualValueProps />
|
<SocialProofStrip />
|
||||||
<ProductShowcase />
|
<ThreeAmWalkthrough />
|
||||||
<HowItWorks />
|
<HowItWorks />
|
||||||
<WhyUs />
|
<WhyUs />
|
||||||
<PricingTeaser />
|
<PricingTeaser />
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const tiers: FullTier[] = [
|
|||||||
cta: 'Start free trial',
|
cta: 'Start free trial',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'MID',
|
name: 'Starter',
|
||||||
price: '20 €',
|
price: '20 €',
|
||||||
priceNote: 'per month',
|
priceNote: 'per month',
|
||||||
envs: '2 environments',
|
envs: '2 environments',
|
||||||
@@ -41,28 +41,28 @@ const tiers: FullTier[] = [
|
|||||||
retention: '7-day retention',
|
retention: '7-day retention',
|
||||||
features: ['Everything in Trial', 'Data flow lineage', 'Cross-service correlation'],
|
features: ['Everything in Trial', 'Data flow lineage', 'Cross-service correlation'],
|
||||||
href: auth.signUpUrl,
|
href: auth.signUpUrl,
|
||||||
cta: 'Start on MID',
|
cta: 'Start on Starter',
|
||||||
highlight: true,
|
highlight: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'HIGH',
|
name: 'Scale',
|
||||||
price: 'Contact',
|
price: 'Contact',
|
||||||
priceNote: 'sales',
|
priceNote: 'sales',
|
||||||
envs: 'Unlimited environments',
|
envs: 'Unlimited environments',
|
||||||
apps: '50 apps',
|
apps: '50 apps',
|
||||||
retention: '90-day retention',
|
retention: '90-day retention',
|
||||||
features: ['Everything in MID', 'Live debugger', 'Exchange replay', 'Live tap'],
|
features: ['Everything in Starter', 'Live debugger', 'Exchange replay', 'Live tap'],
|
||||||
href: auth.salesMailto,
|
href: auth.salesMailto,
|
||||||
cta: 'Talk to sales',
|
cta: 'Talk to sales',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'BUSINESS',
|
name: 'Enterprise',
|
||||||
price: 'Contact',
|
price: 'Contact',
|
||||||
priceNote: 'sales',
|
priceNote: 'sales',
|
||||||
envs: 'Unlimited environments',
|
envs: 'Unlimited environments',
|
||||||
apps: 'Unlimited apps',
|
apps: 'Unlimited apps',
|
||||||
retention: '365-day retention',
|
retention: '365-day retention',
|
||||||
features: ['Everything in HIGH', 'Priority support', 'SLA', 'Dedicated success contact'],
|
features: ['Everything in Scale', 'Priority support', 'SLA', 'Dedicated success contact'],
|
||||||
href: auth.salesMailto,
|
href: auth.salesMailto,
|
||||||
cta: 'Talk to sales',
|
cta: 'Talk to sales',
|
||||||
},
|
},
|
||||||
@@ -117,7 +117,7 @@ const tiers: FullTier[] = [
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p class="text-center text-text-faint text-sm mt-10">
|
<p class="text-center text-text-faint text-sm mt-10">
|
||||||
Prices in EUR, excluding VAT. Billed monthly. Annual billing available for HIGH and BUSINESS — <a href={auth.salesMailto} class="text-accent hover:underline">talk to sales</a>.
|
Prices in EUR, excluding VAT. Billed monthly. Annual billing available for Scale and Enterprise — <a href={auth.salesMailto} class="text-accent hover:underline">talk to sales</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user