Add comprehensive /features page + allow Unsplash imagery
This commit is contained in:
@@ -15,8 +15,8 @@ Reference: [Wix Design Assets & Guidelines](https://www.wix.com/about/design-ass
|
|||||||
1. **Confident & editorial.** Big, bold, tightly‑tracked headlines. Generous whitespace.
|
1. **Confident & editorial.** Big, bold, tightly‑tracked headlines. Generous whitespace.
|
||||||
Let one idea own each section. Marketing reads like a magazine, not a SaaS dashboard.
|
Let one idea own each section. Marketing reads like a magazine, not a SaaS dashboard.
|
||||||
2. **High contrast, mostly monochrome.** Near‑black on white is the base. Color is used
|
2. **High contrast, mostly monochrome.** Near‑black on white is the base. Color is used
|
||||||
deliberately — almost always the single **Wix Blue** accent — never decoratively.
|
deliberately — almost always the single **orange** accent — never decoratively.
|
||||||
3. **Black is the primary action.** Primary buttons are solid black pills. Blue is the
|
3. **Black is the primary action.** Primary buttons are solid black pills. Orange is the
|
||||||
*interactive / accent* color (links, focus rings, active nav, highlights). This is the
|
*interactive / accent* color (links, focus rings, active nav, highlights). This is the
|
||||||
defining Wix.com marketing combination.
|
defining Wix.com marketing combination.
|
||||||
4. **Soft, rounded, friendly.** Pill buttons, large card radii, gentle shadows. Nothing
|
4. **Soft, rounded, friendly.** Pill buttons, large card radii, gentle shadows. Nothing
|
||||||
@@ -36,8 +36,8 @@ Reference: [Wix Design Assets & Guidelines](https://www.wix.com/about/design-ass
|
|||||||
| ---------------- | ---------- | ------------------ | --- |
|
| ---------------- | ---------- | ------------------ | --- |
|
||||||
| **Ink / black** | `#0B0B0B` | `0 0% 5%` | Primary buttons, headlines, default text |
|
| **Ink / black** | `#0B0B0B` | `0 0% 5%` | Primary buttons, headlines, default text |
|
||||||
| **White** | `#FFFFFF` | `0 0% 100%` | Page background, cards |
|
| **White** | `#FFFFFF` | `0 0% 100%` | Page background, cards |
|
||||||
| **Wix Blue** | `#116DFF` | `217 100% 53%` | Links, focus rings, active states, highlights, brand mark |
|
| **Brand Orange** | `#E65100` | `21 100% 45%` | Links, focus rings, active states, highlights, brand mark |
|
||||||
| **Blue (hover)** | `#0B57E0` | `219 90% 46%` | Blue hover / pressed |
|
| **Orange (hover)**| `#B84500` | `21 100% 39%` | Orange hover / pressed |
|
||||||
| **Surface** | `#F4F4F4` | `0 0% 96%` | Section backgrounds, muted fills, inputs‑on‑white |
|
| **Surface** | `#F4F4F4` | `0 0% 96%` | Section backgrounds, muted fills, inputs‑on‑white |
|
||||||
| **Border** | `#E4E4E4` | `0 0% 89%` | Hairline dividers, card borders, input borders |
|
| **Border** | `#E4E4E4` | `0 0% 89%` | Hairline dividers, card borders, input borders |
|
||||||
| **Muted text** | `#6A6A6A` | `0 0% 42%` | Secondary / supporting copy |
|
| **Muted text** | `#6A6A6A` | `0 0% 42%` | Secondary / supporting copy |
|
||||||
@@ -65,7 +65,7 @@ status, and small decorative moments — never for primary UI chrome.
|
|||||||
--card-foreground 0 0% 7%
|
--card-foreground 0 0% 7%
|
||||||
--primary 0 0% 5% /* INK — black buttons */
|
--primary 0 0% 5% /* INK — black buttons */
|
||||||
--primary-foreground 0 0% 100%
|
--primary-foreground 0 0% 100%
|
||||||
--brand 217 100% 53% /* WIX BLUE — links, accents, active */
|
--brand 21 100% 45% /* ORANGE — links, accents, active */
|
||||||
--brand-foreground 0 0% 100%
|
--brand-foreground 0 0% 100%
|
||||||
--secondary 0 0% 96% /* surface gray fill */
|
--secondary 0 0% 96% /* surface gray fill */
|
||||||
--secondary-foreground 0 0% 7%
|
--secondary-foreground 0 0% 7%
|
||||||
@@ -78,14 +78,14 @@ status, and small decorative moments — never for primary UI chrome.
|
|||||||
--warning 24 100% 50%
|
--warning 24 100% 50%
|
||||||
--border 0 0% 89%
|
--border 0 0% 89%
|
||||||
--input 0 0% 89%
|
--input 0 0% 89%
|
||||||
--ring 217 100% 53% /* focus ring = Wix Blue */
|
--ring 21 100% 45% /* focus ring = brand orange */
|
||||||
```
|
```
|
||||||
|
|
||||||
**Rules**
|
**Rules**
|
||||||
- Default text = `foreground`. Secondary text = `muted-foreground`. Never lighter than `#6A6A6A` on white.
|
- Default text = `foreground`. Secondary text = `muted-foreground`. Never lighter than `#6A6A6A` on white.
|
||||||
- Links and any "interactive accent" = **Wix Blue** (`text-brand`). Hover → underline or darker blue.
|
- Links and any "interactive accent" = **orange** (`text-brand`). Hover → underline or darker orange.
|
||||||
- Primary CTA = **black** pill. Never blue and black competing in the same button group.
|
- Primary CTA = **black** pill. Never orange and black competing in the same button group.
|
||||||
- Focus is always a **2px Wix‑Blue ring** with a 2px offset. No exceptions.
|
- Focus is always a **2px orange ring** with a 2px offset. No exceptions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ System fallback stack: `ui-sans-serif, system-ui, -apple-system, "Segoe UI", san
|
|||||||
| Lead paragraph | `1.125–1.25rem` (18–20px)| 400 | normal | `text-muted-foreground` |
|
| Lead paragraph | `1.125–1.25rem` (18–20px)| 400 | normal | `text-muted-foreground` |
|
||||||
| Body | `1rem` (16px) | 400 | normal | base UI = 14–16px |
|
| Body | `1rem` (16px) | 400 | normal | base UI = 14–16px |
|
||||||
| Small / meta | `0.875rem` (14px) | 500 | normal | |
|
| Small / meta | `0.875rem` (14px) | 500 | normal | |
|
||||||
| Eyebrow / label | `0.8125rem` (13px) | 600 | `0.04em` UPPERCASE | Use **Wix Blue** or muted |
|
| Eyebrow / label | `0.8125rem` (13px) | 600 | `0.04em` UPPERCASE | Use **orange** or muted |
|
||||||
|
|
||||||
**Rules**
|
**Rules**
|
||||||
- Headlines are tight: always pair big display sizes with `tracking-tight` and `leading-[1.05–1.1]`.
|
- Headlines are tight: always pair big display sizes with `tracking-tight` and `leading-[1.05–1.1]`.
|
||||||
@@ -154,14 +154,14 @@ Pill shape, Madefor, weight 600, `transition`. Sizes: `sm h-9`, `default h-11`,
|
|||||||
| Variant | Style |
|
| Variant | Style |
|
||||||
| ------------ | ----- |
|
| ------------ | ----- |
|
||||||
| `default` | **Black** fill, white text, `rounded-full`, `hover:bg-black/85`, soft shadow. Primary CTA. |
|
| `default` | **Black** fill, white text, `rounded-full`, `hover:bg-black/85`, soft shadow. Primary CTA. |
|
||||||
| `brand` | **Wix Blue** fill, white text, `rounded-full`, `hover:bg-[#0B57E0]`. Use for the single hero/marketing accent action when black isn't wanted. |
|
| `brand` | **orange** fill, white text, `rounded-full`, `hover:bg-brand-hover`. Use for the single hero/marketing accent action when black isn't wanted. |
|
||||||
| `secondary` | `secondary` gray fill, ink text, `rounded-full`, `hover:bg-black/[.06]`. |
|
| `secondary` | `secondary` gray fill, ink text, `rounded-full`, `hover:bg-black/[.06]`. |
|
||||||
| `outline` | White fill, `border` (1.5px) ink/border color, ink text, `rounded-full`, hover gray fill. |
|
| `outline` | White fill, `border` (1.5px) ink/border color, ink text, `rounded-full`, hover gray fill. |
|
||||||
| `ghost` | Transparent, ink text, `hover:bg-secondary`. |
|
| `ghost` | Transparent, ink text, `hover:bg-secondary`. |
|
||||||
| `link` | Wix‑Blue text, underline on hover, no padding. |
|
| `link` | orange text, underline on hover, no padding. |
|
||||||
| `destructive`| `destructive` fill, white text, `rounded-full`. |
|
| `destructive`| `destructive` fill, white text, `rounded-full`. |
|
||||||
|
|
||||||
Icon buttons: `rounded-full`, square. Always include the blue focus ring.
|
Icon buttons: `rounded-full`, square. Always include the orange focus ring.
|
||||||
|
|
||||||
### Cards (`<Card>`)
|
### Cards (`<Card>`)
|
||||||
`rounded-2xl border bg-card shadow-sm`. Interactive cards add `transition hover:shadow-md hover:-translate-y-0.5`.
|
`rounded-2xl border bg-card shadow-sm`. Interactive cards add `transition hover:shadow-md hover:-translate-y-0.5`.
|
||||||
@@ -174,22 +174,22 @@ Focus: `ring-2 ring-brand border-transparent`. Labels: 14px weight 600, `mb-2`.
|
|||||||
### Badges / chips
|
### Badges / chips
|
||||||
`rounded-full px-3 py-1 text-xs font-semibold`.
|
`rounded-full px-3 py-1 text-xs font-semibold`.
|
||||||
- `default` → ink fill / white text.
|
- `default` → ink fill / white text.
|
||||||
- `brand` → blue tint `bg-brand/10 text-brand`.
|
- `brand` → orange tint `bg-brand/10 text-brand`.
|
||||||
- `secondary` → gray fill.
|
- `secondary` → gray fill.
|
||||||
- `success / warning / destructive` → tinted (`bg-x/12 text-x`).
|
- `success / warning / destructive` → tinted (`bg-x/12 text-x`).
|
||||||
Eyebrow chips on marketing: `bg-secondary` pill with a blue dot/`Sparkles` and uppercase label.
|
Eyebrow chips on marketing: `bg-secondary` pill with an orange dot/`Sparkles` and uppercase label.
|
||||||
|
|
||||||
### Navigation
|
### Navigation
|
||||||
- **Marketing header:** white, hairline bottom border, `backdrop-blur`. Black wordmark
|
- **Marketing header:** white, hairline bottom border, `backdrop-blur`. Black wordmark
|
||||||
with blue mic mark. Nav links muted → ink on hover. Right side: `ghost` "Log in" +
|
with orange mic mark. Nav links muted → ink on hover. Right side: `ghost` "Log in" +
|
||||||
black pill "Get Started".
|
black pill "Get Started".
|
||||||
- **App sidebar:** white, hairline right border. Item = `rounded-full px-4 py-2.5`.
|
- **App sidebar:** white, hairline right border. Item = `rounded-full px-4 py-2.5`.
|
||||||
Active = `bg-brand/10 text-brand font-semibold`; idle = muted, `hover:bg-secondary`.
|
Active = `bg-brand/10 text-brand font-semibold`; idle = muted, `hover:bg-secondary`.
|
||||||
- **Active link accent is always Wix Blue.**
|
- **Active link accent is always orange.**
|
||||||
|
|
||||||
### Pricing cards
|
### Pricing cards
|
||||||
White cards, big display price (weight 800). Highlighted plan: `ring-2 ring-brand` +
|
White cards, big display price (weight 800). Highlighted plan: `ring-2 ring-brand` +
|
||||||
floating blue "Most popular" pill + `shadow-lg`. Feature list uses blue `Check` icons.
|
floating orange "Most popular" pill + `shadow-lg`. Feature list uses orange `Check` icons.
|
||||||
|
|
||||||
### Footer
|
### Footer
|
||||||
`bg-secondary` (`#F4F4F4`), hairline top border, multi‑column link lists, muted labels,
|
`bg-secondary` (`#F4F4F4`), hairline top border, multi‑column link lists, muted labels,
|
||||||
@@ -198,10 +198,10 @@ ink wordmark. Calm and quiet.
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 6. Iconography & imagery
|
## 6. Iconography & imagery
|
||||||
- **Icons:** `lucide-react`, `1.5` stroke, `size-4`/`size-5`. Inherit text color; use blue
|
- **Icons:** `lucide-react`, `1.5` stroke, `size-4`/`size-5`. Inherit text color; use orange
|
||||||
only for accent/active. Icon "chips" = `rounded-2xl bg-secondary` (neutral) or
|
only for accent/active. Icon "chips" = `rounded-2xl bg-secondary` (neutral) or
|
||||||
`bg-brand/10 text-brand` (accent) tile.
|
`bg-brand/10 text-brand` (accent) tile.
|
||||||
- **Brand mark:** mic glyph in a `rounded-2xl` tile — blue tile / white glyph on light, or
|
- **Brand mark:** mic glyph in a `rounded-2xl` tile — orange tile / white glyph on light, or
|
||||||
black tile on accent surfaces.
|
black tile on accent surfaces.
|
||||||
- **Imagery:** large, rounded (`rounded-3xl`), full‑bleed where possible. Generous.
|
- **Imagery:** large, rounded (`rounded-3xl`), full‑bleed where possible. Generous.
|
||||||
|
|
||||||
@@ -215,9 +215,9 @@ ink wordmark. Calm and quiet.
|
|||||||
---
|
---
|
||||||
|
|
||||||
## 8. Do / Don't
|
## 8. Do / Don't
|
||||||
- ✅ Black primary pill + Wix‑Blue accents. ❌ Purple/gradient primary buttons.
|
- ✅ Black primary pill + orange accents. ❌ Purple/gradient primary buttons.
|
||||||
- ✅ One blue accent per view. ❌ Rainbow of brand colors in core UI.
|
- ✅ One orange accent per view. ❌ Rainbow of brand colors in core UI.
|
||||||
- ✅ Big tight headlines in Madefor Display. ❌ Small timid headings.
|
- ✅ Big tight headlines in Madefor Display. ❌ Small timid headings.
|
||||||
- ✅ Hairline borders + soft shadows + whitespace. ❌ Heavy borders, boxes‑in‑boxes.
|
- ✅ Hairline borders + soft shadows + whitespace. ❌ Heavy borders, boxes‑in‑boxes.
|
||||||
- ✅ Pill buttons & chips, `rounded-2xl` cards. ❌ Sharp corners / `rounded-md` chrome.
|
- ✅ Pill buttons & chips, `rounded-2xl` cards. ❌ Sharp corners / `rounded-md` chrome.
|
||||||
- ✅ Blue focus ring everywhere. ❌ Default browser outline or no focus state.
|
- ✅ Orange focus ring everywhere. ❌ Default browser outline or no focus state.
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import Link from "next/link";
|
||||||
|
import {
|
||||||
|
Sparkles,
|
||||||
|
Wand2,
|
||||||
|
ShieldCheck,
|
||||||
|
Globe,
|
||||||
|
Zap,
|
||||||
|
HeartHandshake,
|
||||||
|
ArrowRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "About",
|
||||||
|
description:
|
||||||
|
"PodcastYes is an AI studio that turns a single idea into a finished, publishable podcast — script, voices, and cover art — in minutes. Learn why we built it and what we believe.",
|
||||||
|
};
|
||||||
|
|
||||||
|
const STATS = [
|
||||||
|
{ value: "3", label: "AI models in one workflow" },
|
||||||
|
{ value: "14+", label: "Realistic narrator voices" },
|
||||||
|
{ value: "13+", label: "Languages supported" },
|
||||||
|
{ value: "Minutes", label: "From idea to finished episode" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const VALUES = [
|
||||||
|
{
|
||||||
|
icon: Sparkles,
|
||||||
|
title: "Creativity for everyone",
|
||||||
|
body: "Powerful production shouldn't require gear, a budget, or technical skill. If you can describe an idea, you can turn it into a finished episode.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Wand2,
|
||||||
|
title: "Quality you'd actually publish",
|
||||||
|
body: "AI drafts are a starting point, not the finish line. Every script and voice is editable, so the final cut always sounds like you.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ShieldCheck,
|
||||||
|
title: "Built responsibly",
|
||||||
|
body: "We screen content against a clear policy, never use your private work to train our models, and make sure you own everything you create.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Globe,
|
||||||
|
title: "Global from day one",
|
||||||
|
body: "Stories aren't only told in English. With 13+ languages, you can produce for an audience anywhere — no re-recording required.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Zap,
|
||||||
|
title: "Fast, but never careless",
|
||||||
|
body: "Minutes from idea to episode, with the controls to get the details right before you hit publish. Speed and craft aren't a trade-off.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: HeartHandshake,
|
||||||
|
title: "Honest and transparent",
|
||||||
|
body: "Simple plans, clear limits, cancel anytime. No dark patterns and no surprises — just a tool that respects your time and your work.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function AboutPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Hero */}
|
||||||
|
<section className="bg-hero-wash">
|
||||||
|
<div className="container max-w-4xl py-24 text-center md:py-32">
|
||||||
|
<p className="text-[13px] font-semibold uppercase tracking-[0.04em] text-brand">About us</p>
|
||||||
|
<h1 className="mt-4 text-balance font-display text-5xl font-extrabold leading-[1.05] tracking-tight md:text-6xl">
|
||||||
|
Making great podcasts possible for everyone
|
||||||
|
</h1>
|
||||||
|
<p className="mx-auto mt-6 max-w-2xl text-lg text-muted-foreground sm:text-xl">
|
||||||
|
PodcastYes is an AI studio that turns a single idea into a finished, publishable episode —
|
||||||
|
script, voices, and cover art — in minutes. No microphone, no editing suite, no production
|
||||||
|
budget.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<section className="border-y border-border bg-card">
|
||||||
|
<div className="container grid grid-cols-2 gap-8 py-14 lg:grid-cols-4">
|
||||||
|
{STATS.map((s) => (
|
||||||
|
<div key={s.label} className="text-center">
|
||||||
|
<p className="font-display text-4xl font-extrabold tracking-tight text-brand md:text-5xl">
|
||||||
|
{s.value}
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 text-sm text-muted-foreground">{s.label}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Mission */}
|
||||||
|
<section className="py-24 md:py-28">
|
||||||
|
<div className="container max-w-3xl text-center">
|
||||||
|
<p className="text-[13px] font-semibold uppercase tracking-[0.04em] text-brand">Our mission</p>
|
||||||
|
<p className="mt-5 text-balance font-display text-3xl font-bold leading-tight tracking-tight md:text-4xl">
|
||||||
|
To remove every barrier between a great idea and a published podcast — so anyone with
|
||||||
|
something to say can be{" "}
|
||||||
|
<span className="text-brand">heard</span>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Story */}
|
||||||
|
<section className="border-t border-border bg-secondary py-24 md:py-28">
|
||||||
|
<div className="container max-w-3xl">
|
||||||
|
<p className="text-[13px] font-semibold uppercase tracking-[0.04em] text-brand">Our story</p>
|
||||||
|
<h2 className="mt-3 font-display text-3xl font-extrabold tracking-tight md:text-4xl">
|
||||||
|
Why we built PodcastYes
|
||||||
|
</h2>
|
||||||
|
<div className="mt-8 space-y-5 text-lg leading-relaxed text-muted-foreground">
|
||||||
|
<p>
|
||||||
|
Podcasting is one of the most personal ways to reach an audience — and one of the
|
||||||
|
hardest to start. Scripting, recording, editing, designing artwork, and publishing can
|
||||||
|
take days and a stack of tools and skills most people simply don't have. Brilliant
|
||||||
|
ideas die in that gap between “I should make a podcast” and actually
|
||||||
|
shipping one.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We thought the modern AI stack could collapse all of that into a single workflow. GPT-4
|
||||||
|
can write a tight, on-brand script. ElevenLabs can voice it with realistic, multi-speaker
|
||||||
|
narration. DALL·E can design cover art that matches the topic. Stitched together, they
|
||||||
|
turn “I have a topic” into “I have an episode.”
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
So we built PodcastYes: a studio that lives in your browser. Describe an idea, fine-tune
|
||||||
|
the result in a real editor, download the MP3 — then repurpose it into a blog post, a
|
||||||
|
social thread, or a newsletter. We're a small team of engineers, audio nerds, and
|
||||||
|
storytellers obsessed with making production effortless without making it feel generic.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Values */}
|
||||||
|
<section className="py-24 md:py-28">
|
||||||
|
<div className="container">
|
||||||
|
<div className="mx-auto max-w-2xl text-center">
|
||||||
|
<p className="text-[13px] font-semibold uppercase tracking-[0.04em] text-brand">
|
||||||
|
What we believe
|
||||||
|
</p>
|
||||||
|
<h2 className="mt-3 font-display text-3xl font-extrabold tracking-tight md:text-4xl">
|
||||||
|
The principles behind the product
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="mt-16 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{VALUES.map((v) => (
|
||||||
|
<div
|
||||||
|
key={v.title}
|
||||||
|
className="rounded-2xl border border-border bg-card p-7 transition-all hover:-translate-y-0.5 hover:shadow-md"
|
||||||
|
>
|
||||||
|
<span className="flex h-12 w-12 items-center justify-center rounded-2xl bg-brand/10 text-brand">
|
||||||
|
<v.icon className="h-6 w-6" />
|
||||||
|
</span>
|
||||||
|
<h3 className="mt-5 font-display text-lg font-bold tracking-tight">{v.title}</h3>
|
||||||
|
<p className="mt-2 leading-relaxed text-muted-foreground">{v.body}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<section className="pb-24 md:pb-28">
|
||||||
|
<div className="container">
|
||||||
|
<div className="relative overflow-hidden rounded-3xl bg-primary px-8 py-20 text-center text-primary-foreground">
|
||||||
|
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(60%_80%_at_50%_0%,hsl(var(--brand)/0.35),transparent_70%)]" />
|
||||||
|
<div className="relative">
|
||||||
|
<h2 className="font-display text-4xl font-extrabold tracking-tight sm:text-5xl">
|
||||||
|
Come make something worth listening to
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto mt-4 max-w-xl text-lg text-primary-foreground/75">
|
||||||
|
Spin up a fully produced episode on the free plan in a couple of minutes — then decide.
|
||||||
|
</p>
|
||||||
|
<div className="mt-9 flex flex-col justify-center gap-3 sm:flex-row">
|
||||||
|
<Button asChild size="lg" variant="brand">
|
||||||
|
<Link href="/sign-up">
|
||||||
|
Start free <ArrowRight className="h-4 w-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="border-white/25 bg-transparent text-primary-foreground hover:bg-white/10 hover:text-primary-foreground"
|
||||||
|
>
|
||||||
|
<Link href="/pricing">See pricing</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,763 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import Link from "next/link";
|
||||||
|
import {
|
||||||
|
ArrowRight,
|
||||||
|
FileText,
|
||||||
|
AudioLines,
|
||||||
|
ImageIcon,
|
||||||
|
Languages,
|
||||||
|
Repeat,
|
||||||
|
ListMusic,
|
||||||
|
Share2,
|
||||||
|
Users,
|
||||||
|
KeyRound,
|
||||||
|
CreditCard,
|
||||||
|
Edit3,
|
||||||
|
Download,
|
||||||
|
Sparkles,
|
||||||
|
Mic2,
|
||||||
|
Radio,
|
||||||
|
MessagesSquare,
|
||||||
|
Palette,
|
||||||
|
Globe2,
|
||||||
|
Workflow,
|
||||||
|
Gauge,
|
||||||
|
ShieldCheck,
|
||||||
|
Wand2,
|
||||||
|
Mail,
|
||||||
|
Hash,
|
||||||
|
Clock,
|
||||||
|
Check,
|
||||||
|
Rocket,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Features",
|
||||||
|
description:
|
||||||
|
"Everything PodcastYes does — AI scriptwriting, realistic multi-voice audio, cover art, repurposing, a season generator, 13+ languages, team white-label, an API, and more.",
|
||||||
|
};
|
||||||
|
|
||||||
|
const HERO_IMG =
|
||||||
|
"https://images.unsplash.com/photo-1590602847861-f357a9332bbc?auto=format&fit=crop&w=1600&q=80";
|
||||||
|
const AUDIO_IMG =
|
||||||
|
"https://images.unsplash.com/photo-1598488035139-bdbb2231ce04?auto=format&fit=crop&w=1400&q=80";
|
||||||
|
const TEAM_IMG =
|
||||||
|
"https://images.unsplash.com/photo-1521737604893-d14cc237f11d?auto=format&fit=crop&w=1400&q=80";
|
||||||
|
|
||||||
|
export default function FeaturesPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 1 — Hero */}
|
||||||
|
<section className="relative overflow-hidden bg-hero-wash">
|
||||||
|
<div className="container grid items-center gap-12 py-20 md:grid-cols-2 md:py-28">
|
||||||
|
<div className="space-y-7">
|
||||||
|
<Eyebrow>Every feature, one workflow</Eyebrow>
|
||||||
|
<h1 className="text-balance font-display text-4xl font-extrabold leading-[1.05] tracking-tight sm:text-5xl md:text-6xl">
|
||||||
|
The entire podcast studio, <span className="text-brand">powered by AI</span>
|
||||||
|
</h1>
|
||||||
|
<p className="max-w-xl text-lg text-muted-foreground">
|
||||||
|
Write, narrate, illustrate, edit, repurpose, and publish — PodcastYes replaces a
|
||||||
|
writer, a voice actor, an editor, and a designer with one clean, end-to-end workflow.
|
||||||
|
No microphone, no software, no skills required.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col gap-3 sm:flex-row">
|
||||||
|
<Button asChild size="lg">
|
||||||
|
<Link href="/sign-up">
|
||||||
|
Start free <ArrowRight className="h-4 w-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild size="lg" variant="outline">
|
||||||
|
<Link href="/pricing">See pricing</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Free plan — 3 scripts & 1 audio generation / month. No card required.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="overflow-hidden rounded-3xl border border-border shadow-xl">
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={HERO_IMG}
|
||||||
|
alt="Studio condenser microphone"
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
width={1600}
|
||||||
|
height={1067}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="absolute -bottom-5 -left-5 hidden rounded-2xl border border-border bg-card p-4 shadow-lg sm:block">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="flex h-10 w-10 items-center justify-center rounded-xl bg-brand/10 text-brand">
|
||||||
|
<Sparkles className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold">Topic → episode</p>
|
||||||
|
<p className="text-xs text-muted-foreground">in minutes, not hours</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 2 — Model strip */}
|
||||||
|
<section className="border-y border-border bg-secondary/60 py-10">
|
||||||
|
<div className="container flex flex-col items-center gap-6 text-center">
|
||||||
|
<p className="text-[13px] font-semibold uppercase tracking-[0.04em] text-muted-foreground">
|
||||||
|
Best-in-class AI, stitched into one product
|
||||||
|
</p>
|
||||||
|
<div className="grid w-full gap-4 sm:grid-cols-3">
|
||||||
|
<ModelChip icon={FileText} name="GPT-4" role="Scriptwriting" />
|
||||||
|
<ModelChip icon={AudioLines} name="ElevenLabs" role="Realistic voices" />
|
||||||
|
<ModelChip icon={ImageIcon} name="DALL·E" role="Cover art" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 3 — AI script generation */}
|
||||||
|
<FeatureBand
|
||||||
|
eyebrow="AI scriptwriting"
|
||||||
|
icon={FileText}
|
||||||
|
title="Scripts that actually sound good out loud"
|
||||||
|
body="Describe a topic and GPT-4 writes a structured, on-brand episode — intro, segments, and outro — tailored to your tone, audience, and target length. It writes for the ear, not the page: natural, spoken-word dialogue with no stage directions to clean up."
|
||||||
|
bullets={[
|
||||||
|
"Set tone, audience, length and format up front",
|
||||||
|
"Structured sections you can edit and re-order",
|
||||||
|
"Per-section regeneration when you want a fresh take",
|
||||||
|
"13+ languages, written natively (not translated)",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 4 — Multi-voice audio (with image) */}
|
||||||
|
<FeatureBand
|
||||||
|
reverse
|
||||||
|
tinted
|
||||||
|
eyebrow="Realistic multi-voice audio"
|
||||||
|
icon={AudioLines}
|
||||||
|
title="14+ lifelike voices, multi-speaker dialogue"
|
||||||
|
body="ElevenLabs turns your script into broadcast-quality audio. Cast a different voice for every speaker, and PodcastYes synthesizes the dialogue, stitches the segments, and loudness-normalizes the whole episode into one clean MP3 — automatically."
|
||||||
|
bullets={[
|
||||||
|
"14+ premium voices across accents and styles",
|
||||||
|
"True multi-speaker conversations, not one narrator",
|
||||||
|
"Automatic segmentation, stitching & normalization",
|
||||||
|
"Download a publish-ready MP3 in a click",
|
||||||
|
]}
|
||||||
|
image={AUDIO_IMG}
|
||||||
|
imageAlt="Audio mixing console"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 5 — Formats */}
|
||||||
|
<section className="py-20 md:py-24">
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeading
|
||||||
|
eyebrow="Any format"
|
||||||
|
title="Solo, interview, or a full panel"
|
||||||
|
subtitle="Pick a format and PodcastYes casts the right number of voices and writes for it."
|
||||||
|
/>
|
||||||
|
<div className="mt-14 grid gap-6 md:grid-cols-3">
|
||||||
|
<FormatCard
|
||||||
|
icon={Mic2}
|
||||||
|
title="Solo"
|
||||||
|
body="One host narrating directly to the listener — perfect for explainers, essays, and daily updates."
|
||||||
|
/>
|
||||||
|
<FormatCard
|
||||||
|
icon={MessagesSquare}
|
||||||
|
title="Interview"
|
||||||
|
body="A host and a guest in natural back-and-forth, each with their own distinct voice."
|
||||||
|
/>
|
||||||
|
<FormatCard
|
||||||
|
icon={Radio}
|
||||||
|
title="Multi-host"
|
||||||
|
body="A lively panel of co-hosts who react to each other — banter, debate, and chemistry included."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 6 — Cover art */}
|
||||||
|
<FeatureBand
|
||||||
|
tinted
|
||||||
|
eyebrow="AI cover art"
|
||||||
|
icon={Palette}
|
||||||
|
title="Scroll-stopping artwork, generated for you"
|
||||||
|
body="Every episode gets bespoke cover art from DALL·E, matched to your topic and tone — bold, modern, and square-format ready for every directory. Don't love it? Regenerate until it's perfect."
|
||||||
|
bullets={[
|
||||||
|
"On-topic, on-brand art in one click",
|
||||||
|
"One-tap regeneration for new directions",
|
||||||
|
"Square album-cover format, podcast-directory ready",
|
||||||
|
"Downloaded with your episode assets",
|
||||||
|
]}
|
||||||
|
visual={<ArtVisual />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 7 — Languages */}
|
||||||
|
<section className="py-20 md:py-24">
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeading
|
||||||
|
eyebrow="Go global"
|
||||||
|
title="Publish in 13+ languages"
|
||||||
|
subtitle="Reach a worldwide audience without re-recording anything. Scripts are written natively and narrated by multilingual voices."
|
||||||
|
/>
|
||||||
|
<div className="mx-auto mt-12 flex max-w-3xl flex-wrap justify-center gap-2.5">
|
||||||
|
{[
|
||||||
|
"English",
|
||||||
|
"Spanish",
|
||||||
|
"French",
|
||||||
|
"German",
|
||||||
|
"Italian",
|
||||||
|
"Portuguese",
|
||||||
|
"Dutch",
|
||||||
|
"Polish",
|
||||||
|
"Hindi",
|
||||||
|
"Japanese",
|
||||||
|
"Korean",
|
||||||
|
"Chinese",
|
||||||
|
"Arabic",
|
||||||
|
"Turkish",
|
||||||
|
"Russian",
|
||||||
|
].map((l) => (
|
||||||
|
<span
|
||||||
|
key={l}
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-4 py-2 text-sm font-medium shadow-sm"
|
||||||
|
>
|
||||||
|
<Globe2 className="h-3.5 w-3.5 text-brand" />
|
||||||
|
{l}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 8 — Workflow */}
|
||||||
|
<section className="border-y border-border bg-secondary/60 py-20 md:py-24">
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeading
|
||||||
|
eyebrow="How it flows"
|
||||||
|
title="Three steps to a finished episode"
|
||||||
|
subtitle="Configure once. The AI does the heavy lifting. You fine-tune and publish."
|
||||||
|
/>
|
||||||
|
<div className="mt-14 grid gap-6 md:grid-cols-3">
|
||||||
|
<StepCard
|
||||||
|
n="01"
|
||||||
|
icon={Workflow}
|
||||||
|
title="Configure"
|
||||||
|
body="Topic, tone, format, length, audience, language, and your cast of voices."
|
||||||
|
/>
|
||||||
|
<StepCard
|
||||||
|
n="02"
|
||||||
|
icon={Wand2}
|
||||||
|
title="Generate"
|
||||||
|
body="GPT-4 writes, ElevenLabs records, DALL·E illustrates — with a live progress stepper."
|
||||||
|
/>
|
||||||
|
<StepCard
|
||||||
|
n="03"
|
||||||
|
icon={Rocket}
|
||||||
|
title="Publish"
|
||||||
|
body="Fine-tune, download the MP3 and assets, share a public link, or repurpose it."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 9 — Script editor */}
|
||||||
|
<FeatureBand
|
||||||
|
eyebrow="Full control"
|
||||||
|
icon={Edit3}
|
||||||
|
title="A real editor, not a black box"
|
||||||
|
body="Generated doesn't mean final. Tweak any line, regenerate a single section, then re-record just the audio that changed — so a small edit never costs a full re-render. Live word counts and spoken-duration estimates keep you on target."
|
||||||
|
bullets={[
|
||||||
|
"Edit every line; dirty-state save bar",
|
||||||
|
"Regenerate one section at a time",
|
||||||
|
"Re-record audio for only what you changed",
|
||||||
|
"Copy the full transcript in one click",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 10 — Player & downloads */}
|
||||||
|
<FeatureBand
|
||||||
|
reverse
|
||||||
|
tinted
|
||||||
|
eyebrow="Studio-grade output"
|
||||||
|
icon={Download}
|
||||||
|
title="Listen, scrub, and export everything"
|
||||||
|
body="A built-in waveform player lets you preview and scrub your episode in the browser. When you're ready, download the MP3 — or grab a single ZIP with the audio, cover art, transcript, and show notes, ready to publish anywhere."
|
||||||
|
bullets={[
|
||||||
|
"In-browser waveform player with volume + scrub",
|
||||||
|
"One-click MP3 download",
|
||||||
|
"Download everything as a tidy .zip",
|
||||||
|
"Auto-generated transcript & show notes",
|
||||||
|
]}
|
||||||
|
visual={<PlayerVisual />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 11 — Repurposing */}
|
||||||
|
<section className="py-20 md:py-24">
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeading
|
||||||
|
eyebrow="Do more with every episode"
|
||||||
|
title="One recording, a week of content"
|
||||||
|
subtitle="Turn any episode into the formats your audience already reads — instantly."
|
||||||
|
/>
|
||||||
|
<div className="mt-14 grid gap-6 md:grid-cols-3">
|
||||||
|
<FormatCard icon={FileText} title="Blog post" body="An SEO-friendly article with headings, drawn straight from your episode." />
|
||||||
|
<FormatCard icon={Hash} title="Social thread" body="A punchy, numbered thread with a strong hook, ready to post." />
|
||||||
|
<FormatCard icon={Mail} title="Newsletter" body="A friendly email edition with takeaways and a call-to-action to listen." />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 12 — Series generator */}
|
||||||
|
<FeatureBand
|
||||||
|
tinted
|
||||||
|
eyebrow="Think in seasons"
|
||||||
|
icon={ListMusic}
|
||||||
|
title="Plan an entire season from one prompt"
|
||||||
|
body="The series generator maps out a cohesive season — titles, topics, and summaries for every episode — from a single theme. Then generate each episode straight from the plan. Available on Pro and Agency."
|
||||||
|
bullets={[
|
||||||
|
"A full season outline from one idea",
|
||||||
|
"Per-episode titles, topics & summaries",
|
||||||
|
"Generate any planned episode in a click",
|
||||||
|
"Keep a whole show consistent and on-theme",
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 13 — Share & publish */}
|
||||||
|
<FeatureBand
|
||||||
|
eyebrow="Share instantly"
|
||||||
|
icon={Share2}
|
||||||
|
title="A public link for every episode"
|
||||||
|
body="Flip on sharing and get a clean, no-login public page where anyone can listen — with the cover, an embedded player, and show notes. Perfect for a quick preview, a client review, or distribution before it hits the directories."
|
||||||
|
bullets={[
|
||||||
|
"Beautiful public listen page, zero login",
|
||||||
|
"Embedded streaming player with seek support",
|
||||||
|
"Carries your white-label branding (Agency)",
|
||||||
|
"Toggle on or off anytime",
|
||||||
|
]}
|
||||||
|
visual={<ShareVisual />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 14 — Teams / white-label (with image) */}
|
||||||
|
<FeatureBand
|
||||||
|
reverse
|
||||||
|
tinted
|
||||||
|
eyebrow="For studios & agencies"
|
||||||
|
icon={Users}
|
||||||
|
title="A workspace built for teams"
|
||||||
|
body="The Agency plan adds a 5-seat shared workspace, white-label mode, and custom branding — so you can produce podcasts for clients under your own brand, with your color and logo flowing through the app and every public share page."
|
||||||
|
bullets={[
|
||||||
|
"5-seat team workspace with invitations",
|
||||||
|
"White-label mode — remove PodcastYes branding",
|
||||||
|
"Custom brand color & logo across the app",
|
||||||
|
"Branded public share pages for clients",
|
||||||
|
]}
|
||||||
|
image={TEAM_IMG}
|
||||||
|
imageAlt="A team collaborating around a laptop"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 15 — API */}
|
||||||
|
<FeatureBand
|
||||||
|
eyebrow="Build on it"
|
||||||
|
icon={KeyRound}
|
||||||
|
title="Generate episodes from your own apps"
|
||||||
|
body="Pro and Agency unlock a developer API with scoped keys, so you can create and fetch episodes programmatically — wire PodcastYes into your CMS, automate a daily show, or build it into your own product."
|
||||||
|
bullets={[
|
||||||
|
"Scoped, revocable API keys",
|
||||||
|
"Create + list episodes over REST",
|
||||||
|
"Same plan limits and quotas enforced",
|
||||||
|
"Rate-limited and ready for automation",
|
||||||
|
]}
|
||||||
|
visual={<ApiVisual />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 16 — Plans, usage & billing */}
|
||||||
|
<section className="border-y border-border bg-secondary/60 py-20 md:py-24">
|
||||||
|
<div className="container grid items-center gap-10 md:grid-cols-2">
|
||||||
|
<div className="space-y-5">
|
||||||
|
<Eyebrow>Transparent & flexible</Eyebrow>
|
||||||
|
<h2 className="font-display text-3xl font-extrabold tracking-tight sm:text-4xl">
|
||||||
|
Clear limits, your choice of payment
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
Start free and upgrade as you grow. A live usage dashboard shows exactly where you
|
||||||
|
stand against your plan, with friendly nudges before you hit a limit. Pay with a card
|
||||||
|
via Stripe or with PayPal — whichever you prefer.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<FeatureChip icon={Gauge} label="Live usage meters" />
|
||||||
|
<FeatureChip icon={CreditCard} label="Stripe & PayPal" />
|
||||||
|
<FeatureChip icon={ShieldCheck} label="Cancel anytime" />
|
||||||
|
</div>
|
||||||
|
<Button asChild>
|
||||||
|
<Link href="/pricing">
|
||||||
|
Compare plans <ArrowRight className="h-4 w-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<MiniStat value="4" label="Plans, Free → Agency" />
|
||||||
|
<MiniStat value="2" label="Payment providers" />
|
||||||
|
<MiniStat value="14+" label="Voices included" />
|
||||||
|
<MiniStat value="13+" label="Languages" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 17 — Everything included grid */}
|
||||||
|
<section className="py-20 md:py-24">
|
||||||
|
<div className="container">
|
||||||
|
<SectionHeading
|
||||||
|
eyebrow="The whole toolkit"
|
||||||
|
title="Everything that comes in the box"
|
||||||
|
subtitle="One subscription, the complete production pipeline."
|
||||||
|
/>
|
||||||
|
<div className="mt-14 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{EVERYTHING.map((f) => (
|
||||||
|
<div key={f.title} className="flex gap-4 rounded-2xl border border-border bg-card p-5">
|
||||||
|
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-brand/10 text-brand">
|
||||||
|
<f.icon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="font-semibold">{f.title}</p>
|
||||||
|
<p className="text-sm text-muted-foreground">{f.body}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* 18 — CTA */}
|
||||||
|
<section className="pb-24">
|
||||||
|
<div className="container">
|
||||||
|
<div className="overflow-hidden rounded-3xl bg-primary px-8 py-16 text-center text-primary-foreground">
|
||||||
|
<h2 className="font-display text-3xl font-extrabold tracking-tight sm:text-4xl">
|
||||||
|
See it produce your first episode
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto mt-3 max-w-xl text-primary-foreground/75">
|
||||||
|
Spin up a fully produced episode on the free plan in a couple of minutes — then decide.
|
||||||
|
</p>
|
||||||
|
<div className="mt-8 flex flex-col justify-center gap-3 sm:flex-row">
|
||||||
|
<Button asChild size="lg" variant="secondary">
|
||||||
|
<Link href="/sign-up">
|
||||||
|
Get started free <ArrowRight className="h-4 w-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
size="lg"
|
||||||
|
variant="outline"
|
||||||
|
className="border-primary-foreground/30 bg-transparent text-primary-foreground hover:bg-primary-foreground/10"
|
||||||
|
>
|
||||||
|
<Link href="/pricing">View pricing</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─────────────────────────── helpers ─────────────────────────── */
|
||||||
|
|
||||||
|
function Eyebrow({ children }: { children: React.ReactNode }) {
|
||||||
|
return <p className="text-[13px] font-semibold uppercase tracking-[0.04em] text-brand">{children}</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SectionHeading({
|
||||||
|
eyebrow,
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
}: {
|
||||||
|
eyebrow: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-2xl text-center">
|
||||||
|
<Eyebrow>{eyebrow}</Eyebrow>
|
||||||
|
<h2 className="mt-3 font-display text-3xl font-extrabold tracking-tight sm:text-4xl">{title}</h2>
|
||||||
|
<p className="mt-4 text-lg text-muted-foreground">{subtitle}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FeatureBand({
|
||||||
|
eyebrow,
|
||||||
|
icon: Icon,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
bullets,
|
||||||
|
image,
|
||||||
|
imageAlt,
|
||||||
|
visual,
|
||||||
|
reverse,
|
||||||
|
tinted,
|
||||||
|
}: {
|
||||||
|
eyebrow: string;
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
bullets?: string[];
|
||||||
|
image?: string;
|
||||||
|
imageAlt?: string;
|
||||||
|
visual?: React.ReactNode;
|
||||||
|
reverse?: boolean;
|
||||||
|
tinted?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<section className={tinted ? "border-y border-border bg-secondary/60 py-20 md:py-24" : "py-20 md:py-24"}>
|
||||||
|
<div className="container grid items-center gap-12 md:grid-cols-2">
|
||||||
|
<div className={reverse ? "md:order-2" : ""}>
|
||||||
|
<span className="mb-5 inline-flex h-11 w-11 items-center justify-center rounded-2xl bg-brand/10 text-brand">
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
<Eyebrow>{eyebrow}</Eyebrow>
|
||||||
|
<h2 className="mt-3 font-display text-3xl font-extrabold tracking-tight sm:text-4xl">{title}</h2>
|
||||||
|
<p className="mt-4 text-lg text-muted-foreground">{body}</p>
|
||||||
|
{bullets && (
|
||||||
|
<ul className="mt-6 space-y-3">
|
||||||
|
{bullets.map((b) => (
|
||||||
|
<li key={b} className="flex items-start gap-3">
|
||||||
|
<span className="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-brand/10 text-brand">
|
||||||
|
<Check className="h-3.5 w-3.5" />
|
||||||
|
</span>
|
||||||
|
<span className="text-sm">{b}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={reverse ? "md:order-1" : ""}>
|
||||||
|
{image ? (
|
||||||
|
<div className="overflow-hidden rounded-3xl border border-border shadow-xl">
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img src={image} alt={imageAlt ?? ""} className="h-full w-full object-cover" width={1400} height={933} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
visual
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ModelChip({
|
||||||
|
icon: Icon,
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
}: {
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center gap-3 rounded-2xl border border-border bg-card px-5 py-4 shadow-sm">
|
||||||
|
<span className="flex h-10 w-10 items-center justify-center rounded-xl bg-brand/10 text-brand">
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
<div className="text-left">
|
||||||
|
<p className="font-display font-bold leading-none">{name}</p>
|
||||||
|
<p className="text-xs text-muted-foreground">{role}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormatCard({
|
||||||
|
icon: Icon,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
}: {
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Card className="transition-all hover:-translate-y-0.5 hover:shadow-md">
|
||||||
|
<CardContent className="space-y-3 p-6">
|
||||||
|
<span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-brand/10 text-brand">
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
<h3 className="font-display text-lg font-bold">{title}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">{body}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StepCard({
|
||||||
|
n,
|
||||||
|
icon: Icon,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
}: {
|
||||||
|
n: string;
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Card className="relative overflow-hidden">
|
||||||
|
<CardContent className="space-y-3 p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-brand/10 text-brand">
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
</span>
|
||||||
|
<span className="font-display text-4xl font-extrabold text-muted-foreground/15">{n}</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="font-display text-lg font-bold">{title}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">{body}</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FeatureChip({
|
||||||
|
icon: Icon,
|
||||||
|
label,
|
||||||
|
}: {
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
label: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<span className="inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium">
|
||||||
|
<Icon className="h-3.5 w-3.5 text-brand" /> {label}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MiniStat({ value, label }: { value: string; label: string }) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-2xl border border-border bg-card p-5 text-center shadow-sm">
|
||||||
|
<p className="font-display text-4xl font-extrabold tracking-tight text-brand">{value}</p>
|
||||||
|
<p className="mt-1 text-xs text-muted-foreground">{label}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lightweight, dependency-free "product" visuals for image-less bands */
|
||||||
|
|
||||||
|
function ArtVisual() {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{["from-brand/30 to-brand/5", "from-foreground/20 to-foreground/5", "from-amber-400/30 to-amber-400/5", "from-emerald-400/30 to-emerald-400/5"].map(
|
||||||
|
(g, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`flex aspect-square items-center justify-center rounded-2xl border border-border bg-gradient-to-br ${g}`}
|
||||||
|
>
|
||||||
|
<ImageIcon className="h-8 w-8 text-foreground/40" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayerVisual() {
|
||||||
|
return (
|
||||||
|
<div className="rounded-3xl border border-border bg-card p-6 shadow-xl">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<span className="flex h-12 w-12 items-center justify-center rounded-2xl bg-brand text-brand-foreground">
|
||||||
|
<AudioLines className="h-6 w-6" />
|
||||||
|
</span>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<p className="truncate font-semibold">The history of coffee, ep. 1</p>
|
||||||
|
<p className="text-xs text-muted-foreground">12:04 · solo · EN</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 flex items-end gap-[3px]">
|
||||||
|
{Array.from({ length: 48 }).map((_, i) => (
|
||||||
|
<span
|
||||||
|
key={i}
|
||||||
|
className="w-full rounded-full bg-brand/60"
|
||||||
|
style={{ height: `${10 + Math.abs(Math.sin(i * 0.7)) * 38}px` }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 flex gap-2">
|
||||||
|
<Button size="sm" className="flex-1">
|
||||||
|
<Download className="h-4 w-4" /> MP3
|
||||||
|
</Button>
|
||||||
|
<Button size="sm" variant="outline" className="flex-1">
|
||||||
|
<Download className="h-4 w-4" /> .zip
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShareVisual() {
|
||||||
|
return (
|
||||||
|
<div className="rounded-3xl border border-border bg-card p-6 shadow-xl">
|
||||||
|
<div className="flex items-center gap-2 border-b border-border pb-3">
|
||||||
|
<span className="h-3 w-3 rounded-full bg-destructive/40" />
|
||||||
|
<span className="h-3 w-3 rounded-full bg-warning/50" />
|
||||||
|
<span className="h-3 w-3 rounded-full bg-success/50" />
|
||||||
|
<span className="ml-3 truncate rounded-full bg-secondary px-3 py-1 text-xs text-muted-foreground">
|
||||||
|
podcastyes.app/p/aZ9k…
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 flex gap-4">
|
||||||
|
<div className="flex h-20 w-20 shrink-0 items-center justify-center rounded-2xl bg-gradient-to-br from-brand/30 to-brand/5">
|
||||||
|
<Mic2 className="h-8 w-8 text-brand" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Badge variant="success" className="gap-1">
|
||||||
|
<Share2 className="h-3 w-3" /> Public
|
||||||
|
</Badge>
|
||||||
|
<p className="font-display font-bold leading-tight">Your episode, live for anyone</p>
|
||||||
|
<p className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
|
<Clock className="h-3 w-3" /> no login required
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApiVisual() {
|
||||||
|
return (
|
||||||
|
<div className="overflow-hidden rounded-3xl border border-border bg-foreground shadow-xl">
|
||||||
|
<div className="flex items-center gap-2 border-b border-white/10 px-4 py-3">
|
||||||
|
<KeyRound className="h-4 w-4 text-background/70" />
|
||||||
|
<span className="font-mono text-xs text-background/70">POST /api/v1/episodes</span>
|
||||||
|
</div>
|
||||||
|
<pre className="overflow-x-auto p-5 font-mono text-xs leading-relaxed text-background/90">
|
||||||
|
<code>{`curl -X POST https://podcastyes.app/api/v1/episodes \\
|
||||||
|
-H "Authorization: Bearer pky_live_…" \\
|
||||||
|
-d '{
|
||||||
|
"topic": "Why deep work matters",
|
||||||
|
"format": "SOLO",
|
||||||
|
"language": "en"
|
||||||
|
}'
|
||||||
|
|
||||||
|
→ 201 { "id": "ep_8fk2…", "status": "QUEUED" }`}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EVERYTHING = [
|
||||||
|
{ icon: FileText, title: "GPT-4 scripts", body: "Structured, spoken-word scripts in seconds." },
|
||||||
|
{ icon: AudioLines, title: "Multi-voice audio", body: "14+ realistic voices, multi-speaker." },
|
||||||
|
{ icon: ImageIcon, title: "AI cover art", body: "Bespoke DALL·E artwork per episode." },
|
||||||
|
{ icon: Languages, title: "13+ languages", body: "Native scripts and narration." },
|
||||||
|
{ icon: Edit3, title: "Script editor", body: "Edit, regenerate sections, re-record." },
|
||||||
|
{ icon: Download, title: "MP3 + ZIP export", body: "Audio, art, transcript & notes." },
|
||||||
|
{ icon: Repeat, title: "Repurposing", body: "Blog, social thread, newsletter." },
|
||||||
|
{ icon: ListMusic, title: "Series generator", body: "Plan whole seasons from one prompt." },
|
||||||
|
{ icon: Share2, title: "Public share pages", body: "No-login listen links, branded." },
|
||||||
|
{ icon: Users, title: "Team workspace", body: "5 seats, invitations, roles." },
|
||||||
|
{ icon: Palette, title: "White-label", body: "Your brand color, logo, no PodcastYes." },
|
||||||
|
{ icon: KeyRound, title: "Developer API", body: "Generate episodes programmatically." },
|
||||||
|
{ icon: Gauge, title: "Usage dashboard", body: "Live meters and limit nudges." },
|
||||||
|
{ icon: CreditCard, title: "Stripe & PayPal", body: "Pay your way; cancel anytime." },
|
||||||
|
{ icon: Sparkles, title: "Live generation", body: "Watch each stage stream in real time." },
|
||||||
|
{ icon: ShieldCheck, title: "Yours to keep", body: "Download and publish anywhere." },
|
||||||
|
];
|
||||||
+11
-10
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
/* Wix-inspired: white base, near-black ink, single Wix-Blue accent */
|
/* White base, near-black ink, single ORANGE accent */
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 0 0% 7%;
|
--foreground: 0 0% 7%;
|
||||||
--card: 0 0% 100%;
|
--card: 0 0% 100%;
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
--primary: 0 0% 5%;
|
--primary: 0 0% 5%;
|
||||||
--primary-foreground: 0 0% 100%;
|
--primary-foreground: 0 0% 100%;
|
||||||
|
|
||||||
/* WIX BLUE — links, focus, active states, accents */
|
/* ORANGE — links, focus, active states, accents (#E65100) */
|
||||||
--brand: 217 100% 53%;
|
--brand: 21 100% 45%;
|
||||||
--brand-foreground: 0 0% 100%;
|
--brand-foreground: 0 0% 100%;
|
||||||
--brand-hover: 219 90% 46%;
|
--brand-hover: 21 100% 39%;
|
||||||
|
|
||||||
--secondary: 0 0% 96%;
|
--secondary: 0 0% 96%;
|
||||||
--secondary-foreground: 0 0% 7%;
|
--secondary-foreground: 0 0% 7%;
|
||||||
@@ -31,11 +31,12 @@
|
|||||||
--destructive: 4 86% 58%;
|
--destructive: 4 86% 58%;
|
||||||
--destructive-foreground: 0 0% 100%;
|
--destructive-foreground: 0 0% 100%;
|
||||||
--success: 157 100% 30%;
|
--success: 157 100% 30%;
|
||||||
--warning: 24 100% 50%;
|
/* amber — kept distinct from the orange brand accent */
|
||||||
|
--warning: 38 100% 50%;
|
||||||
|
|
||||||
--border: 0 0% 89%;
|
--border: 0 0% 89%;
|
||||||
--input: 0 0% 89%;
|
--input: 0 0% 89%;
|
||||||
--ring: 217 100% 53%;
|
--ring: 21 100% 45%;
|
||||||
--radius: 0.875rem;
|
--radius: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +51,9 @@
|
|||||||
--primary: 0 0% 100%;
|
--primary: 0 0% 100%;
|
||||||
--primary-foreground: 0 0% 5%;
|
--primary-foreground: 0 0% 5%;
|
||||||
|
|
||||||
--brand: 217 100% 60%;
|
--brand: 24 100% 58%;
|
||||||
--brand-foreground: 0 0% 100%;
|
--brand-foreground: 0 0% 100%;
|
||||||
--brand-hover: 217 100% 66%;
|
--brand-hover: 24 100% 64%;
|
||||||
|
|
||||||
--secondary: 0 0% 13%;
|
--secondary: 0 0% 13%;
|
||||||
--secondary-foreground: 0 0% 98%;
|
--secondary-foreground: 0 0% 98%;
|
||||||
@@ -64,11 +65,11 @@
|
|||||||
--destructive: 4 80% 58%;
|
--destructive: 4 80% 58%;
|
||||||
--destructive-foreground: 0 0% 100%;
|
--destructive-foreground: 0 0% 100%;
|
||||||
--success: 157 70% 45%;
|
--success: 157 70% 45%;
|
||||||
--warning: 24 100% 55%;
|
--warning: 40 100% 55%;
|
||||||
|
|
||||||
--border: 0 0% 16%;
|
--border: 0 0% 16%;
|
||||||
--input: 0 0% 16%;
|
--input: 0 0% 16%;
|
||||||
--ring: 217 100% 60%;
|
--ring: 24 100% 58%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export function CostChart({ data }: { data: CostPoint[] }) {
|
|||||||
contentStyle={{ fontSize: 12, borderRadius: 8 }}
|
contentStyle={{ fontSize: 12, borderRadius: 8 }}
|
||||||
/>
|
/>
|
||||||
<Legend wrapperStyle={{ fontSize: 12 }} />
|
<Legend wrapperStyle={{ fontSize: 12 }} />
|
||||||
{/* Wix-palette data series: Wix Blue + Deep Purple */}
|
{/* Data series: brand orange + deep purple */}
|
||||||
<Bar dataKey="openai" name="OpenAI" stackId="a" fill="#116DFF" radius={[0, 0, 0, 0]} />
|
<Bar dataKey="openai" name="OpenAI" stackId="a" fill="#E65100" radius={[0, 0, 0, 0]} />
|
||||||
<Bar dataKey="elevenlabs" name="ElevenLabs" stackId="a" fill="#3910ED" radius={[6, 6, 0, 0]} />
|
<Bar dataKey="elevenlabs" name="ElevenLabs" stackId="a" fill="#3910ED" radius={[6, 6, 0, 0]} />
|
||||||
</BarChart>
|
</BarChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// Wix-aligned chart palette. Series 1 = Wix Blue, series 2 = deep purple,
|
// Chart palette. Series 1 = brand orange, series 2 = deep purple, plus
|
||||||
// plus success/warning for status-coded series. Kept hex (Recharts needs concrete
|
// success/amber for status-coded series. Kept hex (Recharts needs concrete
|
||||||
// colors), mirroring the design tokens in app/globals.css.
|
// colors), mirroring the design tokens in app/globals.css.
|
||||||
export const CHART = {
|
export const CHART = {
|
||||||
brand: "#116DFF",
|
brand: "#E65100",
|
||||||
brand2: "#3910ED",
|
brand2: "#3910ED",
|
||||||
success: "#00C271",
|
success: "#00C271",
|
||||||
warning: "#FF5500",
|
warning: "#F59E0B",
|
||||||
ink: "#0B0B0B",
|
ink: "#0B0B0B",
|
||||||
muted: "#6A6A6A",
|
muted: "#6A6A6A",
|
||||||
grid: "#E4E4E4",
|
grid: "#E4E4E4",
|
||||||
@@ -14,7 +14,7 @@ export const CHART = {
|
|||||||
// Tier colors for the plan-distribution donut.
|
// Tier colors for the plan-distribution donut.
|
||||||
export const TIER_COLORS: Record<string, string> = {
|
export const TIER_COLORS: Record<string, string> = {
|
||||||
free: "#9AA0A6",
|
free: "#9AA0A6",
|
||||||
creator: "#116DFF",
|
creator: "#E65100",
|
||||||
pro: "#3910ED",
|
pro: "#3910ED",
|
||||||
agency: "#0B0B0B",
|
agency: "#0B0B0B",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ function BrandingCard({ orgId, branding }: { orgId: string; branding: Branding |
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{primaryColor.trim() && !previewHsl && (
|
{primaryColor.trim() && !previewHsl && (
|
||||||
<p className="text-xs text-warning">Enter a 6-digit hex colour (e.g. #116DFF).</p>
|
<p className="text-xs text-warning">Enter a 6-digit hex colour (e.g. #E65100).</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export function WaveformPlayer({
|
|||||||
ctx.clearRect(0, 0, w, h);
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
|
||||||
const styles = getComputedStyle(canvas);
|
const styles = getComputedStyle(canvas);
|
||||||
const played = `hsl(${styles.getPropertyValue("--brand").trim() || "217 100% 53%"})`;
|
const played = `hsl(${styles.getPropertyValue("--brand").trim() || "21 100% 45%"})`;
|
||||||
const unplayed = `hsl(${styles.getPropertyValue("--border").trim() || "0 0% 89%"})`;
|
const unplayed = `hsl(${styles.getPropertyValue("--border").trim() || "0 0% 89%"})`;
|
||||||
|
|
||||||
const n = waveform.length;
|
const n = waveform.length;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export function SiteFooter() {
|
|||||||
["Features", "/#features"],
|
["Features", "/#features"],
|
||||||
["Pricing", "/pricing"],
|
["Pricing", "/pricing"],
|
||||||
["FAQ", "/faq"],
|
["FAQ", "/faq"],
|
||||||
|
["About", "/about"],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<FooterCol
|
<FooterCol
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ export function SiteHeader() {
|
|||||||
|
|
||||||
<nav className="hidden items-center gap-8 text-sm font-medium text-muted-foreground md:flex">
|
<nav className="hidden items-center gap-8 text-sm font-medium text-muted-foreground md:flex">
|
||||||
<Link href="/#how-it-works" className="transition-colors hover:text-foreground">How it works</Link>
|
<Link href="/#how-it-works" className="transition-colors hover:text-foreground">How it works</Link>
|
||||||
<Link href="/#features" className="transition-colors hover:text-foreground">Features</Link>
|
<Link href="/features" className="transition-colors hover:text-foreground">Features</Link>
|
||||||
<Link href="/pricing" className="transition-colors hover:text-foreground">Pricing</Link>
|
<Link href="/pricing" className="transition-colors hover:text-foreground">Pricing</Link>
|
||||||
<Link href="/faq" className="transition-colors hover:text-foreground">FAQ</Link>
|
<Link href="/faq" className="transition-colors hover:text-foreground">FAQ</Link>
|
||||||
|
<Link href="/about" className="transition-colors hover:text-foreground">About</Link>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
+3
-1
@@ -12,6 +12,8 @@ const nextConfig = {
|
|||||||
// DALL·E asset URLs (OpenAI blob storage) used before they are persisted to local disk.
|
// DALL·E asset URLs (OpenAI blob storage) used before they are persisted to local disk.
|
||||||
// Scoped to the single OpenAI host actually used — no broad *.blob.core.windows.net wildcard.
|
// Scoped to the single OpenAI host actually used — no broad *.blob.core.windows.net wildcard.
|
||||||
{ protocol: "https", hostname: "oaidalleapiprodscus.blob.core.windows.net" },
|
{ protocol: "https", hostname: "oaidalleapiprodscus.blob.core.windows.net" },
|
||||||
|
// Marketing imagery (features/about pages).
|
||||||
|
{ protocol: "https", hostname: "images.unsplash.com" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Server-only packages that should be required at runtime, not bundled by webpack.
|
// Server-only packages that should be required at runtime, not bundled by webpack.
|
||||||
@@ -24,7 +26,7 @@ const nextConfig = {
|
|||||||
// to per-request nonces (and drop 'unsafe-*') once the app is migrated.
|
// to per-request nonces (and drop 'unsafe-*') once the app is migrated.
|
||||||
const csp = [
|
const csp = [
|
||||||
"default-src 'self'",
|
"default-src 'self'",
|
||||||
"img-src 'self' data: https://*.blob.core.windows.net",
|
"img-src 'self' data: https://*.blob.core.windows.net https://images.unsplash.com",
|
||||||
"media-src 'self'",
|
"media-src 'self'",
|
||||||
"style-src 'self' 'unsafe-inline'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
|
||||||
|
|||||||
Reference in New Issue
Block a user