/**
 * Page generator, schema v2: a full, declarative extension homepage.
 *
 * A v1 form (no `schemaVersion`) is still rendered by `buildLandingPage` in
 * landing-page.ts, unchanged and byte-identical — `renderLandingForm` below
 * only takes the v2 path when `schemaVersion === 2`. Everything v2 adds is an
 * optional field on the same LandingForm, so an old row, an old client and an
 * old server all keep working (see landing-page-v2.test.ts).
 *
 * What v2 adds, all declarative data — never markup:
 *   - ordered sections with `hidden` (hero, about, features, screenshots,
 *     how-it-works, pricing, faq, testimonials, store-badges, changelog, links);
 *   - theme tokens: palette per light/dark (or `auto` = both, by
 *     prefers-color-scheme), fonts from LANDING_FONTS, radius, width;
 *   - per-locale content (`locales`) rendered as one page per locale with
 *     hreflang alternates and `dir="rtl"` for right-to-left languages;
 *   - SEO / Open Graph fields and JSON-LD (SoftwareApplication + FAQPage),
 *     whose `offers` carry the real prices of plans bound to Pay offers.
 *
 * Output rules are the v1 rules: no JavaScript, every string HTML-escaped,
 * links scheme-clamped (safeHref), images clamped (safeImageSrc), CSS built
 * only from allowlists and validated hex colours. The hosted CSP
 * (`default-src 'none'`, inline styles only) therefore holds unchanged.
 *
 * Testimonials must be attributed: a quote without an author and a public
 * https source is refused, never rendered. Drafting tools never write
 * testimonials — only the developer supplies them.
 *
 * SINGLE SOURCE, THREE COPIES — same scheme as landing-page.ts:
 *   shared-types/landing-sections.ts            (canonical — edit here)
 *   tools/extenshi-mcp/src/landing-sections.ts  (copy)
 *   tools/extenshi-cli/src/landing-sections.ts  (copy)
 * After editing, run:
 *   sed "s#from 'shared-types\/#from './#" shared-types/landing-sections.ts > tools/extenshi-mcp/src/landing-sections.ts
 *   sed "s#from 'shared-types\/#from './#" shared-types/landing-sections.ts > tools/extenshi-cli/src/landing-sections.ts
 */
import { type LandingBrowser, type LandingForm } from './landing-page.js';
export declare const LANDING_SCHEMA_VERSION: 2;
export declare const LANDING_SECTION_TYPES: readonly ["hero", "about", "features", "screenshots", "how-it-works", "pricing", "faq", "testimonials", "store-badges", "changelog", "links"];
export type LandingSectionType = (typeof LANDING_SECTION_TYPES)[number];
export declare const LANDING_V2_LIMITS: {
    readonly sections: 20;
    readonly sectionId: 40;
    readonly title: 120;
    readonly intro: 600;
    readonly body: 6000;
    readonly items: 24;
    readonly screenshots: 12;
    readonly steps: 8;
    readonly plans: 6;
    readonly planFeatures: 12;
    readonly faq: 20;
    readonly testimonials: 12;
    readonly quote: 600;
    readonly changelog: 20;
    readonly changelogNotes: 12;
    readonly links: 12;
    readonly shortText: 120;
    readonly text: 500;
    readonly locales: 10;
    readonly seoTitle: 70;
    readonly seoDescription: 300;
};
/** A section id: lowercase slug, used as the HTML id and as the locale override key. */
export declare const LANDING_SECTION_ID: RegExp;
interface SectionBase<T extends LandingSectionType> {
    id: string;
    type: T;
    /** Kept in the form, left out of the page. */
    hidden?: boolean;
    /** Section heading; each type has a default (LANDING_UI_DEFAULTS). */
    title?: string;
    intro?: string;
}
export interface LandingHeroSection extends SectionBase<'hero'> {
    /** Defaults to extensionName. */
    headline?: string;
    /** Defaults to tagline. */
    subheadline?: string;
    /** Store buttons under the headline. Default true. */
    showBadges?: boolean;
}
export interface LandingAboutSection extends SectionBase<'about'> {
    /** Plain text; blank lines separate paragraphs. */
    body: string;
}
export interface LandingFeaturesSection extends SectionBase<'features'> {
    items: {
        title: string;
        description?: string;
    }[];
}
export interface LandingScreenshotsSection extends SectionBase<'screenshots'> {
    items: {
        url: string;
        alt?: string;
        caption?: string;
    }[];
}
export interface LandingHowItWorksSection extends SectionBase<'how-it-works'> {
    steps: {
        title: string;
        description?: string;
    }[];
}
export type LandingPlanPeriod = 'one-time' | 'month' | 'year' | 'lifetime';
export interface LandingPricingPlan {
    name?: string;
    /** Display price ("$4.99", "Free"). Overwritten from the Pay offer when offerSku is set. */
    price?: string;
    period?: LandingPlanPeriod;
    description?: string;
    features?: string[];
    highlighted?: boolean;
    /**
     * SKU of an Extenshi Pay offer of this project. When set, the hosted page
     * takes price, period and — when not given here — name, description and
     * features from the live offer, so the page cannot advertise a price the
     * checkout does not charge.
     */
    offerSku?: string;
    ctaLabel?: string;
    /** Where the plan button goes. Default: the first store listing. */
    ctaUrl?: string;
}
export interface LandingPricingSection extends SectionBase<'pricing'> {
    plans: LandingPricingPlan[];
    note?: string;
}
export interface LandingFaqSection extends SectionBase<'faq'> {
    items: {
        question: string;
        answer: string;
    }[];
}
export interface LandingTestimonial {
    quote: string;
    /** The person's name as published at the source. Required. */
    author: string;
    role?: string;
    /** Public https URL where they said it (store review, post). Required. */
    sourceUrl: string;
    sourceLabel?: string;
}
export interface LandingTestimonialsSection extends SectionBase<'testimonials'> {
    items: LandingTestimonial[];
}
export interface LandingStoreBadgesSection extends SectionBase<'store-badges'> {
    /** Order and subset of stores; default every store with a URL. */
    browsers?: LandingBrowser[];
}
export interface LandingChangelogSection extends SectionBase<'changelog'> {
    entries: {
        version: string;
        date?: string;
        notes: string[];
    }[];
}
export interface LandingLinksSection extends SectionBase<'links'> {
    items?: {
        label: string;
        url: string;
    }[];
    /** Also list privacy / support / feedback / terms from the form. Default true. */
    includeProjectLinks?: boolean;
}
export type LandingSection = LandingHeroSection | LandingAboutSection | LandingFeaturesSection | LandingScreenshotsSection | LandingHowItWorksSection | LandingPricingSection | LandingFaqSection | LandingTestimonialsSection | LandingStoreBadgesSection | LandingChangelogSection | LandingLinksSection;
/**
 * System font stacks only (after modern-font-stacks, CC0). A web font would
 * need a font-src exception in the hosted CSP and a third-party request from
 * every visitor; a stack renders instantly and requests nothing.
 */
export declare const LANDING_FONTS: {
    readonly default: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif";
    readonly 'system-ui': "system-ui, sans-serif";
    readonly 'neo-grotesque': "Inter, Roboto, \"Helvetica Neue\", \"Arial Nova\", \"Nimbus Sans\", Arial, sans-serif";
    readonly humanist: "Seravek, \"Gill Sans Nova\", Ubuntu, Calibri, \"DejaVu Sans\", source-sans-pro, sans-serif";
    readonly 'geometric-humanist': "Avenir, Montserrat, Corbel, \"URW Gothic\", source-sans-pro, sans-serif";
    readonly 'classical-humanist': "Optima, Candara, \"Noto Sans\", source-sans-pro, sans-serif";
    readonly 'rounded-sans': "ui-rounded, \"Hiragino Maru Gothic ProN\", Quicksand, Comfortaa, Manjari, \"Arial Rounded MT\", \"Arial Rounded MT Bold\", Calibri, source-sans-pro, sans-serif";
    readonly hyperlegible: "\"Atkinson Hyperlegible Next\", \"Atkinson Hyperlegible\", Lexend, Verdana, Tahoma, sans-serif";
    readonly industrial: "Bahnschrift, \"DIN Alternate\", \"Franklin Gothic Medium\", \"Nimbus Sans Narrow\", sans-serif-condensed, sans-serif";
    readonly transitional: "Charter, \"Bitstream Charter\", \"Sitka Text\", Cambria, serif";
    readonly 'old-style': "\"Iowan Old Style\", \"Palatino Linotype\", \"URW Palladio L\", P052, serif";
    readonly 'slab-serif': "Rockwell, \"Rockwell Nova\", \"Roboto Slab\", \"DejaVu Serif\", \"Sitka Small\", serif";
    readonly didone: "Didot, \"Bodoni MT\", \"Noto Serif Display\", \"URW Palladio L\", P052, Sylfaen, serif";
    readonly monospace: "ui-monospace, \"Cascadia Code\", \"Source Code Pro\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace";
};
export type LandingFontId = keyof typeof LANDING_FONTS;
export interface LandingPalette {
    accent?: string;
    /** Text on accent-coloured buttons. */
    accentText?: string;
    background?: string;
    surface?: string;
    text?: string;
    muted?: string;
    border?: string;
}
export interface LandingThemeTokens {
    /** `auto` ships both palettes and follows prefers-color-scheme. Default: form.theme. */
    mode?: 'light' | 'dark' | 'auto';
    light?: LandingPalette;
    dark?: LandingPalette;
    headingFont?: LandingFontId;
    bodyFont?: LandingFontId;
    radius?: 'none' | 'small' | 'medium' | 'large';
    width?: 'narrow' | 'normal' | 'wide';
}
export interface LandingSeo {
    /** <title> and og:title. Default "{name} — {tagline}". */
    title?: string;
    /** meta description and og:description. Default description or tagline. */
    description?: string;
    /** og:image; default the first screenshot. https only. */
    ogImageUrl?: string;
    /** Twitter/X handle for twitter:site. */
    twitterSite?: string;
    /** Publisher name for JSON-LD author. */
    authorName?: string;
    /** Adds robots noindex — for a page that must stay out of search. */
    noindex?: boolean;
}
export declare const LANDING_UI_DEFAULTS: {
    readonly addToChrome: "Add to Chrome";
    readonly getForFirefox: "Get for Firefox";
    readonly getForEdge: "Get for Edge";
    readonly screenshots: "Screenshots";
    readonly features: "Features";
    readonly howItWorks: "How it works";
    readonly pricing: "Pricing";
    readonly faq: "Frequently asked questions";
    readonly testimonials: "What people say";
    readonly storeBadges: "Get it for your browser";
    readonly changelog: "Changelog";
    readonly links: "Links";
    readonly privacy: "Privacy policy";
    readonly support: "Support";
    readonly feedback: "Share feedback";
    readonly terms: "License & refunds";
    readonly install: "Install";
    readonly popular: "Most popular";
    readonly perMonth: "/ month";
    readonly perYear: "/ year";
    readonly oneTime: "one-time";
    readonly lifetime: "lifetime";
    readonly source: "Source";
    readonly languages: "Languages";
    /** Alt text of the hero logo; `{name}` is replaced by the extension name. */
    readonly logoAlt: "{name} logo";
};
export type LandingUiKey = keyof typeof LANDING_UI_DEFAULTS;
export interface LandingLocaleContent {
    extensionName?: string;
    tagline?: string;
    description?: string;
    seo?: {
        title?: string;
        description?: string;
        ogImageUrl?: string;
    };
    ui?: Partial<Record<LandingUiKey, string>>;
    /**
     * Per-section overrides keyed by section id, in the section's own shape.
     * Only string values are taken (arrays merge by index), so a translation
     * can change words and localized screenshot URLs, never structure.
     */
    sections?: Record<string, Record<string, unknown>>;
}
/** The optional fields v2 adds to LandingForm. */
export interface LandingV2Fields {
    schemaVersion?: 2;
    sections?: LandingSection[];
    tokens?: LandingThemeTokens;
    seo?: LandingSeo;
    /** Locale of the base content. Default `en`. */
    defaultLocale?: string;
    locales?: Record<string, LandingLocaleContent>;
    ui?: Partial<Record<LandingUiKey, string>>;
    /** Hosted uninstall-feedback form (extenshi hosted artifact). */
    uninstallUrl?: string;
    /** License terms & refunds page (Extenshi Pay hosted artifact). */
    termsUrl?: string;
}
export interface LandingProblem {
    /** JSON path into the form, e.g. `sections[3].items[0].sourceUrl`. */
    path: string;
    severity: 'error' | 'warning';
    message: string;
    /** What to change to fix it. */
    fix: string;
}
/**
 * `pt_BR`, `pt-br`, `PT-BR` → `pt-BR` (BCP 47, as hreflang and <html lang>
 * want it). Accepts the Chrome `_locales` spelling used by the localization
 * workflow. Null when the value is not a language tag.
 */
export declare function normalizeLandingLocale(raw: unknown): string | null;
/** `<html dir>` for a locale page: `rtl` for Arabic, Hebrew, Persian, Urdu and other RTL scripts. */
export declare function landingLocaleDir(locale: string): 'ltr' | 'rtl';
/** URL path segment for a locale: lowercase BCP 47 (`pt-br`). */
export declare function landingLocaleSlug(locale: string): string;
export declare function landingDefaultLocale(form: LandingForm): string;
/** Default locale first, then every valid translated locale (deduplicated, capped). */
export declare function landingLocales(form: LandingForm): string[];
/** Absolute URL of a locale's page: the default locale lives at `base`, others at `base/{slug}`. */
export declare function landingLocaleUrl(base: string, locale: string, defaultLocale: string): string;
/** The form as it reads in `locale` (the base form for the default locale). */
export declare function localizeLandingForm(form: LandingForm, locale: string): LandingForm;
/** WCAG 2 contrast ratio of two hex colours, or null when either is not a hex colour. */
export declare function contrastRatio(a: string, b: string): number | null;
/**
 * Everything wrong with a form, as actionable problems. `error` means the
 * hosted publish refuses the form; `warning` means it renders but something
 * was dropped, defaulted or is likely unintended. For a v1 form this is
 * landingPageWarnings() as warnings — v1 validation is unchanged.
 */
export declare function landingFormProblems(form: LandingForm): LandingProblem[];
/** A live Extenshi Pay offer as the renderer needs it (the BFF's LandingPayOffer fits). */
export interface LandingRenderOffer {
    sku: string;
    /** Minor units, as stored on the offer (formatLandingPrice divides by 100). */
    amountMinor: number;
    currency: string;
}
export interface RenderLandingOptions {
    /** Locale to render; default the form's default locale. */
    locale?: string;
    /**
     * The project's active Pay offers (hosted rendering only). Plans bound to
     * one by offerSku get a JSON-LD Offer with its real price; without them a
     * page that sells something claims no price rather than a free one.
     */
    payOffers?: readonly LandingRenderOffer[];
}
/**
 * The one entry point every surface calls (dojo preview, BFF hosting, MCP and
 * CLI generate). A v1 form goes to buildLandingPage untouched.
 */
export declare function renderLandingForm(form: LandingForm, opts?: RenderLandingOptions): string;
/** Every locale's page: `[{ locale, path, html }]`, default locale first (path ''). */
export declare function renderLandingLocales(form: LandingForm, opts?: Omit<RenderLandingOptions, 'locale'>): {
    locale: string;
    slug: string;
    html: string;
}[];
/** Format a Pay offer amount for a pricing card ("$4.99", "4,99 €"). */
export declare function formatLandingPrice(amountMinor: number, currency: string, locale: string): string;
export {};
//# sourceMappingURL=landing-sections.d.ts.map