You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
6.6 KiB
175 lines
6.6 KiB
<script lang="ts">
|
|
/**
|
|
* The hero mini-page: the block with a page worth of product content, so the
|
|
* demo can render it FULL-BLEED (the hero at the real top of a real page) and
|
|
* the `preview` route can serve the same thing as a standalone document for
|
|
* the device-width frame.
|
|
*
|
|
* The media is a framed screenshot placeholder composed from canon components
|
|
* — no external asset, no raw CSS — so it reads as a product shot in every
|
|
* theme.
|
|
*/
|
|
import { Hero } from '$blocks/hero';
|
|
import { Section } from '$uix/eidos/components/section';
|
|
import { Container } from '$uix/eidos/components/container';
|
|
import { Stack } from '$uix/eidos/components/stack';
|
|
import { Group } from '$uix/eidos/components/group';
|
|
import { Grid } from '$uix/eidos/components/grid';
|
|
import { Box } from '$uix/eidos/components/box';
|
|
import { Wrap } from '$uix/eidos/components/wrap';
|
|
import { Surface } from '$uix/eidos/components/surface';
|
|
import { Mockup } from '$uix/eidos/components/mockup';
|
|
import { MediaPlayer } from '$uix/eidos/components/media-player';
|
|
import { Heading } from '$uix/eidos/components/heading';
|
|
import { Text } from '$uix/eidos/components/text';
|
|
import { Badge } from '$uix/eidos/components/badge';
|
|
import { Button } from '$uix/eidos/components/button';
|
|
import { Link } from '$uix/eidos/components/link';
|
|
import * as Icon from '$uix/eidos/components/icon';
|
|
|
|
type ContainerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
type SectionSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
type Layout = 'center' | 'split' | 'background';
|
|
type Decor = 'glow' | 'mesh' | 'grid' | 'dots' | 'none';
|
|
type MediaKind = 'dashboard' | 'video';
|
|
|
|
let {
|
|
layout = 'split' as Layout,
|
|
level = 1,
|
|
container = 'lg' as ContainerSize,
|
|
size = 'xl' as SectionSize,
|
|
withMedia = true,
|
|
withEyebrow = true,
|
|
mediaKind = 'dashboard' as MediaKind,
|
|
decor = 'glow' as Decor
|
|
}: {
|
|
layout?: Layout;
|
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
container?: ContainerSize;
|
|
size?: SectionSize;
|
|
withMedia?: boolean;
|
|
withEyebrow?: boolean;
|
|
mediaKind?: MediaKind;
|
|
decor?: Decor;
|
|
} = $props();
|
|
|
|
// Faux chart bars for the screenshot placeholder — fixed heights in rem so
|
|
// the card is exactly as tall as its content, no empty frame.
|
|
const bars = ['3rem', '5rem', '3.6rem', '6.4rem', '4.4rem', '7.4rem', '5.6rem'];
|
|
const centered = $derived(layout !== 'split');
|
|
</script>
|
|
|
|
<Hero {layout} {level} containerSize={container} sectionSize={size} {decor}>
|
|
{#snippet backdrop()}
|
|
<!-- The backdrop is a real Surface with the aurora gradient finish: the
|
|
identity's `primary` solid painted by `--gradient-aurora` (derived from
|
|
the theme's own colour roles). No hand-picked colour — swap the palette
|
|
and the backdrop follows. -->
|
|
<Surface variant="solid" color="primary" gradient width="100%" height="100%" />
|
|
{/snippet}
|
|
|
|
{#snippet eyebrow()}
|
|
{#if withEyebrow}
|
|
<Badge size="sm" variant="soft" color="primary">Beta pública</Badge>
|
|
{/if}
|
|
{/snippet}
|
|
|
|
{#snippet title()}
|
|
Analítica de producto sin sorpresas en la factura
|
|
{/snippet}
|
|
|
|
{#snippet description()}
|
|
Eventos crudos, cohortes al día y automatizaciones que reaccionan solas. Sin muestreo, sin
|
|
reprocesar y sin un contrato que crece con cada pico de tráfico.
|
|
{/snippet}
|
|
|
|
{#snippet actions()}
|
|
<!-- `intent="fulfill"`: the page's completion, carried as ANTICIPATORY
|
|
WEIGHT (colour and form). It does NOT load the Button's
|
|
`contact-activate` with intent — the morfo says so explicitly — so it
|
|
produces no sound or haptic by itself; the evaluative consequence is
|
|
the `commit` the app emits when the outcome lands (see PricingSite). -->
|
|
<Button variant="solid" size="lg" intent="fulfill">
|
|
{#snippet endIcon()}<Icon.ArrowRight size="xs" />{/snippet}
|
|
{#snippet child({ props, content })}
|
|
<a href="#pricing" {...props}>{@render content()}</a>
|
|
{/snippet}
|
|
Empezar gratis
|
|
</Button>
|
|
<!-- No ink override here: the block sets it for the whole copy cluster under
|
|
`background`. This used to pass `--color-content-on-solid` by hand, which
|
|
was inert anyway (`subtle` resolves to `color: inherit`). -->
|
|
<Link href="#docs" variant="subtle" size="lg">Ver la documentación</Link>
|
|
{/snippet}
|
|
|
|
{#snippet media()}
|
|
{#if withMedia && mediaKind === 'video'}
|
|
<!-- A hero with VIDEO: the canon `MediaPlayer` inside the same `Mockup`
|
|
chrome. Nothing bespoke — the block's `media` slot takes whatever the
|
|
app composes. -->
|
|
<Mockup chrome="browser" url="acme.app/demo">
|
|
<MediaPlayer media="video">
|
|
<MediaPlayer.Media
|
|
src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_1MB.mp4"
|
|
loop
|
|
muted
|
|
autoplay
|
|
playsinline
|
|
/>
|
|
<MediaPlayer.DefaultControls />
|
|
</MediaPlayer>
|
|
</Mockup>
|
|
{:else if withMedia}
|
|
<!-- Dogfoods the canon `Mockup`: the browser chrome (dots + URL) is the
|
|
primitive's, so the faux dashboard inside is just the content. -->
|
|
<Mockup chrome="browser" url="acme.app/analytics">
|
|
<Box padding={4}>
|
|
<Stack gap={5}>
|
|
<Group gap={2} align="center" justify="end">
|
|
<Badge size="xs" variant="soft" color="primary">en vivo</Badge>
|
|
</Group>
|
|
<!-- Grid gives each bar an equal 1fr track — no reliance on flex-grow. -->
|
|
<Grid columns={7} gap={3} align="end">
|
|
{#each bars as h, i (i)}
|
|
<Surface
|
|
variant="solid"
|
|
color={i === 5 ? 'primary' : 'neutral'}
|
|
rounded
|
|
height={h}
|
|
/>
|
|
{/each}
|
|
</Grid>
|
|
<Grid templateColumns="1fr 20%" gap={3} align="center">
|
|
<Surface variant="solid" color="neutral" rounded height="0.5rem" />
|
|
<Surface variant="solid" color="neutral" rounded height="0.5rem" />
|
|
</Grid>
|
|
</Stack>
|
|
</Box>
|
|
</Mockup>
|
|
{/if}
|
|
{/snippet}
|
|
</Hero>
|
|
|
|
<!-- A floor so the page reads as a page, not a lone band. -->
|
|
<Section size="lg">
|
|
<Container size={container}>
|
|
<Stack gap={6}>
|
|
<Text size="sm" weight="medium" color="muted" align={centered ? 'center' : 'start'}>
|
|
Con la confianza de equipos de producto en
|
|
</Text>
|
|
<Wrap gap={8} justify={centered ? 'center' : 'start'}>
|
|
{#each ['Northwind', 'Umbra', 'Kessel', 'Volta', 'Marisol'] as brand (brand)}
|
|
<Text size="lg" weight="semibold" color="muted">{brand}</Text>
|
|
{/each}
|
|
</Wrap>
|
|
<Heading level={level === 1 ? 2 : 3}>
|
|
Del evento crudo a la decisión, sin escala intermedia
|
|
</Heading>
|
|
<Text color="muted" measure="wide">
|
|
Los eventos llegan tal cual y salen en cohortes: quién hizo qué, cuándo dejó de hacerlo y
|
|
qué cambió la semana en que lanzaste. Consulta cualquier ventana sin reprocesar.
|
|
</Text>
|
|
</Stack>
|
|
</Container>
|
|
</Section>
|