frontend page fixes v1

This commit is contained in:
MatejGhub
2026-09-12 21:07:43 +02:00
parent 65e1fdd55b
commit 32c6c828b1
11 changed files with 7946 additions and 6444 deletions
@@ -46,8 +46,8 @@
{i18n.t("pricing.calc.lead")} {i18n.t("pricing.calc.lead")}
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent class="space-y-8"> <CardContent class="grid gap-8 lg:grid-cols-2">
<div class="grid gap-6 sm:grid-cols-3"> <div class="grid gap-6">
<div class="space-y-2 text-sm"> <div class="space-y-2 text-sm">
<label class="font-medium text-text" for="plan-calc-feeds" <label class="font-medium text-text" for="plan-calc-feeds"
>{i18n.t("pricing.calc.feeds")}</label >{i18n.t("pricing.calc.feeds")}</label
@@ -102,7 +102,7 @@
</div> </div>
{#if result.enterprise} {#if result.enterprise}
<div class="rounded-lg border border-border bg-background px-4 py-5 text-center sm:text-left"> <div class="h-full rounded-lg border border-border bg-background px-4 py-5 text-center sm:text-left">
<p class="text-lg font-semibold text-text">{i18n.t("pricing.calc.enterpriseTitle")}</p> <p class="text-lg font-semibold text-text">{i18n.t("pricing.calc.enterpriseTitle")}</p>
<p class="mt-2 text-sm text-text-muted">{i18n.t("pricing.calc.enterpriseBody")}</p> <p class="mt-2 text-sm text-text-muted">{i18n.t("pricing.calc.enterpriseBody")}</p>
<a <a
@@ -117,8 +117,8 @@
{:else if result.recommended} {:else if result.recommended}
{@const rec = result.recommended} {@const rec = result.recommended}
{@const budget = result.budget} {@const budget = result.budget}
<div class="grid gap-4 lg:grid-cols-2"> <div class={budget ? "grid h-full gap-4" : "h-full"}>
<div class="rounded-lg border border-primary/30 bg-primary/5 px-4 py-5"> <div class="flex h-full flex-col rounded-lg border border-primary/30 bg-primary/5 px-4 py-5">
<p class="text-xs font-semibold uppercase tracking-wide text-primary"> <p class="text-xs font-semibold uppercase tracking-wide text-primary">
{i18n.t("pricing.calc.recommendedLabel")} {i18n.t("pricing.calc.recommendedLabel")}
</p> </p>
@@ -132,7 +132,7 @@
{formatMoney(rec.monthlyListUSD)} / {i18n.t("pricing.card.perMonth")} {formatMoney(rec.monthlyListUSD)} / {i18n.t("pricing.card.perMonth")}
{/if} {/if}
</p> </p>
<ul class="mt-3 space-y-1 text-sm text-text-muted"> <ul class="mt-3 mb-4 space-y-1 text-sm text-text-muted">
<li> <li>
{i18n.t("pricing.calc.coversCredits", { {i18n.t("pricing.calc.coversCredits", {
count: result.creditsNeeded.toLocaleString() count: result.creditsNeeded.toLocaleString()
@@ -152,7 +152,7 @@
</ul> </ul>
<a <a
href={rec.plan.ctaHref} href={rec.plan.ctaHref}
class={buttonClasses("default", "default", "mt-4 inline-flex w-full sm:w-auto")} class={buttonClasses("default", "default", "mt-auto inline-flex w-full sm:w-auto")}
> >
{i18n.t("pricing.calc.ctaPlan", { name: rec.plan.name })} {i18n.t("pricing.calc.ctaPlan", { name: rec.plan.name })}
</a> </a>
@@ -195,14 +195,8 @@
{i18n.t("pricing.calc.ctaPlan", { name: budget.plan.name })} {i18n.t("pricing.calc.ctaPlan", { name: budget.plan.name })}
</a> </a>
</div> </div>
{:else}
<div class="rounded-lg border border-dashed border-border bg-background/50 px-4 py-5">
<p class="text-sm text-text-muted">{i18n.t("pricing.calc.noBudget")}</p>
</div>
{/if} {/if}
</div> </div>
{/if} {/if}
<p class="text-center text-xs text-text-muted sm:text-left">{i18n.t("pricing.calc.disclaimer")}</p>
</CardContent> </CardContent>
</Card> </Card>
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Check, Zap, Sparkles, ChevronDown, ChevronUp } from "@lucide/svelte"; import { Check, Zap, Sparkles, ChevronDown, ChevronUp } from "@lucide/svelte";
import { slide } from "svelte/transition";
import { import {
Badge, Badge,
Card, Card,
@@ -17,12 +18,18 @@
type Props = { type Props = {
plan: PricingPlan; plan: PricingPlan;
isYearly?: boolean; isYearly?: boolean;
expanded?: boolean;
onToggle?: () => void;
class?: string; class?: string;
}; };
let { plan, isYearly = false, class: className = "" }: Props = $props(); let {
plan,
let showAllFeatures = $state(false); isYearly = false,
expanded = false,
onToggle = () => {},
class: className = ""
}: Props = $props();
const yearlyPrice = $derived(Math.round(plan.pricePerMonth * 12 * (1 - ANNUAL_DISCOUNT))); const yearlyPrice = $derived(Math.round(plan.pricePerMonth * 12 * (1 - ANNUAL_DISCOUNT)));
const displayPrice = $derived(isYearly ? yearlyPrice : plan.pricePerMonth); const displayPrice = $derived(isYearly ? yearlyPrice : plan.pricePerMonth);
@@ -37,7 +44,7 @@
const initialFeaturesCount = 5; const initialFeaturesCount = 5;
const displayedFeatures = $derived( const displayedFeatures = $derived(
showAllFeatures ? plan.features : plan.features.slice(0, initialFeaturesCount) expanded ? plan.features : plan.features.slice(0, initialFeaturesCount)
); );
const hasMoreFeatures = $derived(plan.features.length > initialFeaturesCount); const hasMoreFeatures = $derived(plan.features.length > initialFeaturesCount);
@@ -98,7 +105,7 @@
</script> </script>
<!-- In-flow badge row keeps “Most Popular” clear of the plan title; spacer aligns siblings. --> <!-- In-flow badge row keeps “Most Popular” clear of the plan title; spacer aligns siblings. -->
<div class="flex h-full flex-col"> <div class="flex flex-col">
<div class="mb-4 flex min-h-8 shrink-0 items-center justify-center"> <div class="mb-4 flex min-h-8 shrink-0 items-center justify-center">
{#if plan.popular} {#if plan.popular}
<Badge <Badge
@@ -111,7 +118,7 @@
</div> </div>
<Card <Card
class="flex h-full flex-col {plan.popular class="flex flex-col sm:min-h-[32rem] {plan.popular
? 'border-primary shadow-xl ring-2 ring-primary/20' ? 'border-primary shadow-xl ring-2 ring-primary/20'
: 'border-border shadow-md'} {className}" : 'border-border shadow-md'} {className}"
> >
@@ -150,9 +157,9 @@
</div> </div>
</CardHeader> </CardHeader>
<CardContent class="flex flex-grow flex-col space-y-3"> <CardContent class="flex flex-grow flex-col space-y-3 pb-0">
{#each displayedFeatures as feature, index (index)} {#each displayedFeatures as feature, index (index)}
<div class="flex items-start space-x-3"> <div class="flex items-start space-x-3" transition:slide={{ duration: 220 }}>
<Check <Check
class="mt-0.5 h-4 w-4 shrink-0 {feature.included class="mt-0.5 h-4 w-4 shrink-0 {feature.included
? 'text-success' ? 'text-success'
@@ -171,32 +178,35 @@
{/each} {/each}
{#if hasMoreFeatures} {#if hasMoreFeatures}
<div class="mt-auto pt-2"> <div class="mt-auto flex justify-center pt-4">
<button <button
type="button" type="button"
class={buttonClasses( class="group inline-flex min-h-8 items-center justify-center gap-1.5 rounded-full border border-border/80 bg-background px-3.5 py-1.5 text-xs font-medium text-text-muted shadow-sm transition-all duration-200 hover:border-primary/30 hover:bg-accent hover:text-text hover:shadow focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.98]"
"ghost", aria-expanded={expanded}
"sm", onclick={onToggle}
"w-full text-xs text-text-muted hover:text-text"
)}
onclick={() => (showAllFeatures = !showAllFeatures)}
> >
{#if showAllFeatures} {#if expanded}
<ChevronUp class="mr-1 h-3 w-3" />
{i18n.t("pricing.card.showLess")} {i18n.t("pricing.card.showLess")}
<ChevronUp
class="h-3.5 w-3.5 transition-transform duration-200 group-hover:-translate-y-0.5"
aria-hidden="true"
/>
{:else} {:else}
<ChevronDown class="mr-1 h-3 w-3" />
{@const moreCount = plan.features.length - initialFeaturesCount} {@const moreCount = plan.features.length - initialFeaturesCount}
{moreCount === 1 {moreCount === 1
? i18n.t("pricing.card.showMoreFeature", { count: moreCount }) ? i18n.t("pricing.card.showMoreFeature", { count: moreCount })
: i18n.t("pricing.card.showMoreFeatures", { count: moreCount })} : i18n.t("pricing.card.showMoreFeatures", { count: moreCount })}
<ChevronDown
class="h-3.5 w-3.5 transition-transform duration-200 group-hover:translate-y-0.5"
aria-hidden="true"
/>
{/if} {/if}
</button> </button>
</div> </div>
{/if} {/if}
</CardContent> </CardContent>
<CardFooter class="pt-4"> <CardFooter class="pt-3">
<a <a
href={plan.ctaHref} href={plan.ctaHref}
target={isExternal ? "_blank" : undefined} target={isExternal ? "_blank" : undefined}
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
import { slide } from "svelte/transition";
import { CreditCard, Check, Calendar } from "@lucide/svelte"; import { CreditCard, Check, Calendar } from "@lucide/svelte";
import { import {
Badge, Badge,
@@ -44,6 +45,7 @@
let isYearly = $state(false); let isYearly = $state(false);
let openFaq = $state<number | null>(null); let openFaq = $state<number | null>(null);
let openPlanId = $state<number | null>(null);
let plans = $state<PricingPlan[]>(PRICING_PLANS); let plans = $state<PricingPlan[]>(PRICING_PLANS);
onMount(() => { onMount(() => {
@@ -118,15 +120,22 @@
</div> </div>
</div> </div>
<div class="grid items-stretch gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> <div class="grid items-start gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{#each plans as plan (plan.id)} {#each plans as plan (plan.id)}
<PlanCard {plan} {isYearly} /> <PlanCard
{plan}
{isYearly}
expanded={openPlanId === plan.id}
onToggle={() => (openPlanId = openPlanId === plan.id ? null : plan.id)}
/>
{/each} {/each}
</div> </div>
<div class="mt-12"> {#if hideIntro}
<PlanCalculator {plans} /> <div class="mt-12 mb-2">
</div> <PlanCalculator {plans} />
</div>
{/if}
<p class="mx-auto mt-6 max-w-3xl text-center text-sm text-text-muted"> <p class="mx-auto mt-6 max-w-3xl text-center text-sm text-text-muted">
{i18n.t("pricing.section.guestPublicBefore")} {i18n.t("pricing.section.guestPublicBefore")}
@@ -169,7 +178,7 @@
{i18n.t("pricing.section.faqTitle")} {i18n.t("pricing.section.faqTitle")}
</h2> </h2>
<div <div
class="mx-auto w-full max-w-3xl divide-y divide-border rounded-lg border border-border bg-surface" class="mx-auto w-full max-w-3xl overflow-hidden divide-y divide-border rounded-lg border border-border bg-surface"
> >
{#each PRICING_FAQ as item, index (index)} {#each PRICING_FAQ as item, index (index)}
<div> <div>
@@ -185,7 +194,10 @@
</span> </span>
</button> </button>
{#if openFaq === index} {#if openFaq === index}
<div class="px-4 pb-4 text-base text-text-muted"> <div
class="px-4 pt-3 pb-5 text-base leading-7 text-text-muted"
transition:slide={{ duration: 220 }}
>
{item.answerKey ? i18n.t(item.answerKey) : item.answer} {item.answerKey ? i18n.t(item.answerKey) : item.answer}
</div> </div>
{/if} {/if}
@@ -194,7 +206,7 @@
</div> </div>
</div> </div>
<div class="mt-16 text-center"> <div class="mt-20 text-center">
<div class="rounded-2xl border border-border bg-surface px-6 py-10 shadow-sm sm:px-10"> <div class="rounded-2xl border border-border bg-surface px-6 py-10 shadow-sm sm:px-10">
<h3 class="mb-2 text-2xl font-bold text-text"> <h3 class="mb-2 text-2xl font-bold text-text">
{i18n.t("pricing.section.readyTitle")} {i18n.t("pricing.section.readyTitle")}
File diff suppressed because it is too large Load Diff
@@ -13,7 +13,7 @@
const featureKeys = ["home.cta.f1", "home.cta.f2", "home.cta.f3"] as const; const featureKeys = ["home.cta.f1", "home.cta.f2", "home.cta.f3"] as const;
</script> </script>
<section {id} class={`w-full bg-muted py-16 md:py-24 ${className}`}> <section {id} class={`w-full bg-muted pb-16 md:pb-24 ${className}`}>
<div class="container mx-auto max-w-[1216px] px-4 md:px-6"> <div class="container mx-auto max-w-[1216px] px-4 md:px-6">
<div class="overflow-hidden rounded-2xl bg-secondary text-secondary-foreground shadow-xl"> <div class="overflow-hidden rounded-2xl bg-secondary text-secondary-foreground shadow-xl">
<div class="grid grid-cols-1 items-center overflow-hidden lg:grid-cols-2"> <div class="grid grid-cols-1 items-center overflow-hidden lg:grid-cols-2">
+23 -23
View File
@@ -27,33 +27,33 @@
} }
</script> </script>
<footer class={`w-full border-t border-border bg-background ${className}`} aria-label={i18n.t("site.footer.aria")}> <footer class={`w-full border-t border-border/80 bg-surface/30 ${className}`} aria-label={i18n.t("site.footer.aria")}>
<div class="container mx-auto max-w-[1216px] px-4 py-16 md:px-6"> <div class="container mx-auto max-w-[1216px] px-4 py-12 sm:py-16 md:px-6 lg:py-20">
<div class="grid grid-cols-1 gap-12 md:grid-cols-2 lg:grid-cols-4"> <div class="grid grid-cols-1 gap-10 md:grid-cols-3 md:gap-12 lg:gap-20">
<div class="lg:col-span-2"> <div class="md:col-span-2">
<a href="/" class="mb-6 flex items-center space-x-3" aria-label={i18n.t("app.home")}> <a href="/" class="mb-5 inline-flex items-center space-x-3" aria-label={i18n.t("app.home")}>
<div class="relative flex h-10 w-10 items-center justify-center"> <div class="relative flex h-11 w-11 items-center justify-center rounded-xl bg-background shadow-sm ring-1 ring-border/80">
<img <img
src="/descrybe_logo.png" src="/descrybe_logo.png"
alt="" alt=""
width="40" width="40"
height="40" height="40"
class="h-auto w-auto" class="h-9 w-9"
/> />
</div> </div>
<span class="text-xl font-bold text-text">{i18n.t("app.name")}</span> <span class="text-xl font-bold tracking-tight text-text">{i18n.t("app.name")}</span>
</a> </a>
<p class="mb-6 max-w-md text-text-muted"> <p class="mb-6 max-w-xl text-sm leading-7 text-text-muted sm:text-base">
{i18n.t("site.footer.description")} {i18n.t("site.footer.description")}
</p> </p>
{#if FOOTER_DATA.contactInfo} {#if FOOTER_DATA.contactInfo}
<div class="flex flex-col space-y-3"> <div class="flex flex-col items-start space-y-3">
{#if FOOTER_DATA.contactInfo.email} {#if FOOTER_DATA.contactInfo.email}
<div class="flex items-start gap-3"> <div class="inline-flex items-center gap-2.5 rounded-full border border-border/80 bg-background px-4 py-2.5 shadow-sm">
<svg <svg
class="mt-0.5 h-5 w-5 text-text-muted" class="h-4 w-4 text-primary"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
@@ -68,7 +68,7 @@
</svg> </svg>
<a <a
href={`mailto:${FOOTER_DATA.contactInfo.email}`} href={`mailto:${FOOTER_DATA.contactInfo.email}`}
class="text-text-muted transition-colors hover:text-link" class="text-sm font-medium text-text-muted transition-colors hover:text-link focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
> >
{FOOTER_DATA.contactInfo.email} {FOOTER_DATA.contactInfo.email}
</a> </a>
@@ -79,18 +79,18 @@
</div> </div>
{#each FOOTER_DATA.columns as column} {#each FOOTER_DATA.columns as column}
<div> <div class="border-t border-border/80 pt-8 md:border-t-0 md:border-l md:pt-1 md:pl-10">
<h2 class="mb-5 text-base font-semibold text-text"> <h2 class="mb-4 text-sm font-semibold tracking-wide text-text uppercase">
{columnTitleKeys[column.title] {columnTitleKeys[column.title]
? i18n.t(columnTitleKeys[column.title]) ? i18n.t(columnTitleKeys[column.title])
: column.title} : column.title}
</h2> </h2>
<ul class="space-y-3"> <ul class="grid grid-cols-2 gap-2 md:grid-cols-1">
{#each column.links as link} {#each column.links as link}
<li> <li>
<a <a
href={link.href} href={link.href}
class="text-text-muted transition-colors hover:text-link" class="block rounded-lg border border-transparent px-3 py-2.5 text-sm font-medium text-text-muted transition hover:border-border/80 hover:bg-background hover:text-link hover:shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
> >
{linkLabel(link.text)} {linkLabel(link.text)}
</a> </a>
@@ -102,10 +102,10 @@
</div> </div>
</div> </div>
<div class="w-full border-t border-border"> <div class="w-full border-t border-border/80 bg-background/60">
<div class="container mx-auto max-w-[1216px] px-4 py-6 md:px-6"> <div class="container mx-auto max-w-[1216px] px-4 py-6 md:px-6">
<div class="flex flex-col items-center justify-between md:flex-row"> <div class="flex flex-col items-start justify-between gap-5 md:flex-row md:items-center">
<div class="mb-4 text-sm text-text-muted md:mb-0"> <div class="text-sm text-text-muted">
{i18n.t("site.footer.rightsLine", { {i18n.t("site.footer.rightsLine", {
year: currentYear, year: currentYear,
name: i18n.t("app.name") name: i18n.t("app.name")
@@ -113,18 +113,18 @@
</div> </div>
{#if FOOTER_DATA.legalLinks.length > 0} {#if FOOTER_DATA.legalLinks.length > 0}
<div class="flex flex-wrap gap-6 text-sm"> <div class="flex flex-wrap gap-x-5 gap-y-3 text-sm md:justify-end">
{#each FOOTER_DATA.legalLinks as link} {#each FOOTER_DATA.legalLinks as link}
<a <a
href={link.href} href={link.href}
class="text-text-muted transition-colors hover:text-link" class="text-text-muted transition-colors hover:text-link focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
> >
{linkLabel(link.text)} {linkLabel(link.text)}
</a> </a>
{/each} {/each}
<button <button
type="button" type="button"
class="text-text-muted transition-colors hover:text-link" class="text-text-muted transition-colors hover:text-link focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
onclick={() => cookieConsent.openPreferences()} onclick={() => cookieConsent.openPreferences()}
> >
{i18n.t("site.cookieSettings")} {i18n.t("site.cookieSettings")}
@@ -10,7 +10,7 @@
aria-hidden="true" aria-hidden="true"
></div> ></div>
<div <div
class="bg-tiled-pattern dark:bg-tiled-pattern-dark pointer-events-none absolute inset-x-0 bottom-0 bg-repeat opacity-90" class="bg-tiled-pattern dark:bg-tiled-pattern-dark pointer-events-none absolute inset-x-0 bottom-0 bg-repeat opacity-90 dark:opacity-20"
style="top: 4rem; mask-image: linear-gradient(to bottom, black 0%, transparent 55%, transparent 100%); -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 55%, transparent 100%);" style="top: 4rem; mask-image: linear-gradient(to bottom, black 0%, transparent 55%, transparent 100%); -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 55%, transparent 100%);"
aria-hidden="true" aria-hidden="true"
></div> ></div>
@@ -46,9 +46,21 @@
</a> </a>
<a <a
href="/register" href="/register"
class="rounded-md border border-border bg-surface/70 px-4 py-2.5 text-sm leading-6 font-semibold text-text backdrop-blur-sm transition hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring" class="inline-flex items-center gap-2 rounded-md border border-border bg-surface/70 px-4 py-2.5 text-sm leading-6 font-semibold text-text backdrop-blur-sm transition hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
> >
{i18n.t("home.hero.apply")} <span aria-hidden="true"></span> {i18n.t("home.hero.apply")}
<svg
class="h-4 w-4 shrink-0"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M5 12h14M13 6l6 6-6 6" />
</svg>
</a> </a>
</div> </div>
<div class="mt-10 flex flex-col items-center justify-center"> <div class="mt-10 flex flex-col items-center justify-center">
@@ -23,9 +23,9 @@
}); });
function navTitle(href: string): string { function navTitle(href: string): string {
if (href === "/") return i18n.t("site.nav.home"); if (href === "/#how-it-works") return i18n.t("site.nav.howItWorks");
if (href === "/#benefits") return i18n.t("site.nav.benefits");
if (href === "/pricing") return i18n.t("site.nav.pricing"); if (href === "/pricing") return i18n.t("site.nav.pricing");
if (href === "/docs") return i18n.t("site.nav.apiDocs");
return href; return href;
} }
@@ -212,20 +212,24 @@
</div> </div>
{#if mobileMenuOpen} {#if mobileMenuOpen}
<div class="absolute top-full right-0 left-0 bg-background/95 shadow-md backdrop-blur-md lg:hidden"> <div
<div class="border-t border-border/80 px-4 pt-2 pb-6"> class="absolute top-full right-3 left-3 mt-2 overflow-hidden rounded-xl border border-border/80 bg-background/95 shadow-xl backdrop-blur-md sm:right-4 sm:left-auto sm:w-80 lg:hidden"
<nav class="mt-2 mb-4 flex flex-col space-y-1" aria-label={i18n.t("site.nav.mobile")}> >
<div class="p-3">
<nav class="flex flex-col gap-1" aria-label={i18n.t("site.nav.mobile")}>
{#each NAV_ITEMS as item} {#each NAV_ITEMS as item}
<a <a
href={item.href} href={item.href}
class="block py-2.5 text-base font-medium text-text transition-colors hover:text-link" class="rounded-lg px-3 py-2.5 text-sm font-medium text-text transition-colors hover:bg-accent hover:text-link"
onclick={(e) => onNavClick(e, item.href)} onclick={(e) => onNavClick(e, item.href)}
> >
{navTitle(item.href)} {navTitle(item.href)}
</a> </a>
{/each} {/each}
</nav> </nav>
<div class="mt-4 space-y-3"> <div
class={`mt-3 border-t border-border/80 pt-3 ${loggedIn ? "space-y-3" : "grid grid-cols-2 gap-2"}`}
>
{#if loggedIn} {#if loggedIn}
<p class="truncate text-center text-sm font-medium text-text-muted"> <p class="truncate text-center text-sm font-medium text-text-muted">
{accountLabel} {accountLabel}
@@ -242,7 +246,7 @@
<a <a
href="/login" href="/login"
class={buttonClasses( class={buttonClasses(
"ghost", "outline",
"default", "default",
"box-border h-10 w-full min-h-10 text-sm font-medium" "box-border h-10 w-full min-h-10 text-sm font-medium"
)}>{i18n.t("site.logIn")}</a )}>{i18n.t("site.logIn")}</a
+12 -12
View File
@@ -41,7 +41,7 @@ export {
export const PRODUCT_EXPLANATION = { export const PRODUCT_EXPLANATION = {
id: "product", id: "product",
eyebrow: "What Descrybe does", eyebrow: "What Descrybe does",
title: "Supplier feeds in. Ready listings out — export, WooCommerce, or API.", title: "Turn supplier feeds into listings you can publish anywhere.",
description: description:
"Descrybe helps ecommerce teams turn supplier CSV and XML feeds into clean product catalogs. Map fields to your categories, enrich attributes and listing copy, then ship data through export feeds, WooCommerce sync, or the public API.", "Descrybe helps ecommerce teams turn supplier CSV and XML feeds into clean product catalogs. Map fields to your categories, enrich attributes and listing copy, then ship data through export feeds, WooCommerce sync, or the public API.",
pipeline: [ pipeline: [
@@ -56,12 +56,12 @@ export const PRODUCT_EXPLANATION = {
export const PROCESS_STEPS: ProcessStep[] = [ export const PROCESS_STEPS: ProcessStep[] = [
{ {
number: 1, number: 1,
title: "Import Your Taxonomy", title: "Set up your catalog",
description: "Set up the categories and attributes your store already uses", description: "Organize and enrich products for your store",
features: [ features: [
{ text: "Define required attributes for each category" }, { text: "Choose product details for each category" },
{ text: "Set per-category title formulas" }, { text: "Set per-category title formulas" },
{ text: "Set product description and search-snippet templates" } { text: "Decide how product descriptions should be written" }
], ],
image: "/import_taxonomy.png" image: "/import_taxonomy.png"
}, },
@@ -104,7 +104,7 @@ export const PROCESS_STEPS: ProcessStep[] = [
export const MAIN_BENEFIT = { export const MAIN_BENEFIT = {
title: "Faster path from feed to listing", title: "Faster path from feed to listing",
description: description:
"Descrybe cuts the manual work of turning raw supplier data into store-ready product pages — categorize, fill attributes, and write listing copy at catalog scale.", "Descrybe cuts the manual work of turning raw supplier data into store-ready product pages. Categorize products, fill attributes, and write listing copy at catalog scale.",
stats: [ stats: [
{ {
label: "Manual Process", label: "Manual Process",
@@ -144,7 +144,7 @@ export const STANDARD_BENEFITS: Benefit[] = [
icon: "chart", icon: "chart",
title: "Clearer listings, stronger conversion", title: "Clearer listings, stronger conversion",
description: description:
"Titles and descriptions that highlight what shoppers care about — benefits, specs, and search terms that match your catalog." "Create titles and descriptions around the benefits, specifications, and search terms your customers actually care about."
}, },
{ {
icon: "database", icon: "database",
@@ -162,7 +162,7 @@ export const STANDARD_BENEFITS: Benefit[] = [
icon: "cost", icon: "cost",
title: "Less manual data entry", title: "Less manual data entry",
description: description:
"Automate mapping, attribute fill, and listing copy so your team spends time on merchandising — not spreadsheet cleanup." "Automate mapping, attribute filling, and listing copy so your team can focus on merchandising instead of cleaning spreadsheets."
}, },
{ {
icon: "scale", icon: "scale",
@@ -217,7 +217,7 @@ export const PRICING_ENABLED = true;
export const FOOTER_DATA = { export const FOOTER_DATA = {
companyName: "Descrybe", companyName: "Descrybe",
companyDescription: companyDescription:
"Descrybe turns supplier feeds into clean, channel-ready product catalogs — map fields, enrich attributes and listing copy, then export or sync to WooCommerce.", "Descrybe turns supplier feeds into clean, channel-ready product catalogs. Map fields, enrich product data, then export your catalog or sync it to WooCommerce.",
columns: [ columns: [
{ {
title: "Navigation", title: "Navigation",
@@ -245,7 +245,7 @@ export const FOOTER_DATA = {
* Log in / Get started live in header actions (email auth in v2; Clerk in legacy). * Log in / Get started live in header actions (email auth in v2; Clerk in legacy).
*/ */
export const NAV_ITEMS = [ export const NAV_ITEMS = [
{ title: "Home", href: "/" }, { title: "How it works", href: "/#how-it-works" },
...(PRICING_ENABLED ? [{ title: "Pricing", href: "/pricing" }] : []), { title: "Benefits", href: "/#benefits" },
{ title: "API Docs", href: "/docs" } ...(PRICING_ENABLED ? [{ title: "Pricing", href: "/pricing" }] : [])
] as Array<{ title: string; href: string }>; ] as Array<{ title: string; href: string }>;
File diff suppressed because it is too large Load Diff
Binary file not shown.