"""Pre-built landing page components for marketing websites and public-facing pages"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_web_pages.ipynb.

# %% auto 0
__all__ = ['SECTION_STYLE', 'SECTION_MARGIN', 'STANDARD_FOOTER_COLUMNS', 'HeroSection', 'FeatureShowcase', 'FeaturesGrid',
           'PricingSection', 'FAQSection', 'PageFooter', 'LandingNavBar', 'LandingPageSimple', 'MarkdownSection',
           'ContentPage']

# %% ../nbs/04_web_pages.ipynb 3
import importlib
import markdown
from .components import *
from .core import *
from fasthtml.common import *
from .app_pages import *
from .foundations import *
from fastcore.utils import partial
from fasthtml.common import *
from fasthtml.jupyter import FastHTML, fast_app, JupyUvi, HTMX
from fastlite import *
import fasthtml.components as fc
from fasthtml.common import A, Button as FhButton, I, Span


# %% ../nbs/04_web_pages.ipynb 6
def HeroSection(
    title: str,              # Main hero title
    subtitle: str,           # Hero subtitle/description
    primary_cta_text: str,   # Primary CTA button text
    primary_cta_href: str,   # Primary CTA button href
    secondary_cta_text: str = None,  # Secondary CTA button text (optional)
    secondary_cta_href: str = None,  # Secondary CTA button href (optional)
    background: str = "primary-container",  # BeerCSS background class (without bg- prefix)
    cls: str = "",  # Additional classes
    extra_css: str | None = None,  # Optional custom CSS (minimal; opt-in)
    extra_js: str | None = None,  # Optional custom JS (minimal; opt-in)
):
    """Hero section with title, subtitle, and CTA buttons.
    
    BeerCSS-first styling. Takes up 75vh by default with gradient overlay.
    Designed for full-width layout - background extends edge-to-edge naturally.
    Content is centered using responsive class internally.
    """
    # Hero styling: 75vh height + gradient overlay
    # No width hacks needed - parent layout uses "max" for full-width
    hero_css = """
    .hero-section {
        min-height: 75vh;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        background: linear-gradient(135deg, 
            var(--primary-container) 0%, 
            var(--surface-container) 50%,
            var(--secondary-container) 100%);
    }
    .hero-section::before {
        content: '';
        position: absolute;
        inset: 0;
        background: radial-gradient(circle at 20% 80%, var(--primary) 0%, transparent 50%),
                    radial-gradient(circle at 80% 20%, var(--secondary) 0%, transparent 50%);
        opacity: 0.1;
        pointer-events: none;
    }
    .hero-content {
        position: relative;
        z-index: 1;
    }
    """
    
    hooks = [Style(hero_css)]
    if extra_css:
        hooks.append(Style(extra_css))
    if extra_js:
        hooks.append(Script(extra_js))
    
    ctas = [Button(primary_cta_text, href=primary_cta_href, variant="raised")]
    if secondary_cta_text and secondary_cta_href:
        ctas.append(Button(secondary_cta_text, href=secondary_cta_href, variant="outlined"))
    
    stack = DivVStacked(
        H1(title, cls="no-margin"),
        P(subtitle, cls="secondary-text large-text"),
        Div(*ctas, cls="row middle-align center-align small-space"),
        cls="center-align large-padding hero-content",
    )
    # Content centered with responsive class
    content = Div(stack, cls="responsive")
    return Section(
        *hooks,
        content,
        cls=f"hero-section {cls}".strip(),
    )

# %% ../nbs/04_web_pages.ipynb 10
def FeatureShowcase(
    features: list,  # List of dicts: {title, description, image_src?, icon?, image_alt?}
    title: str = None,  # Optional section title
    subtitle: str = None,  # Optional section subtitle
    cls: str = "",
):
    """Alternating text/image feature showcase with card styling.
    
    Each feature row alternates: text-left/media-right, then text-right/media-left.
    On mobile, stacks vertically. Supports either image_src or icon for media.
    Each row is wrapped in an Article card with elevation for visual distinction.
    
    Args:
        features: List of feature dicts with keys:
            - title: Feature title (required)
            - description: Feature description (required)
            - image_src: URL/path to image (optional)
            - icon: Material icon name (optional, used if no image_src)
            - image_alt: Alt text for image (optional)
        title: Section heading
        subtitle: Section subheading
        cls: Additional CSS classes
    
    Example:
        FeatureShowcase(
            title="Why Choose Us",
            features=[
                {'title': 'Fast', 'description': 'Lightning quick', 'icon': 'bolt'},
                {'title': 'Secure', 'description': 'Bank-level security', 'image_src': '/img/secure.png'},
            ]
        )
    """
    rows = []
    
    for idx, feature in enumerate(features):
        # Text column
        text_col = Div(
            H3(feature['title'], cls="no-margin"),
            P(feature['description'], cls="secondary-text large-text"),
            cls="s12 m6 l6 padding",
        )
        
        # Media column - image or icon
        if feature.get('image_src'):
            media_content = Img(
                src=feature['image_src'],
                alt=feature.get('image_alt', feature['title']),
                cls="responsive round",
            )
        else:
            # Fallback to icon with large display
            icon_name = feature.get('icon', 'star')
            media_content = Icon(icon_name, size="extra", cls="primary-text")
        
        media_col = Div(
            Div(media_content, cls="center-align"),
            cls="s12 m6 l6 padding",
        )
        
        # Alternate layout: even rows = text-left, odd rows = text-right
        if idx % 2 == 0:
            row_content = Div(text_col, media_col, cls="grid middle-align")
        else:
            row_content = Div(media_col, text_col, cls="grid middle-align")
        
        # Wrap each row in Article card with elevation styling
        row = Article(
            row_content,
            cls="surface-container round border large-padding medium-margin",
        )
        rows.append(row)
    
    # Build section
    content = []
    if title:
        content.append(H2(title, cls="center-align"))
    if subtitle:
        content.append(P(subtitle, cls="center-align secondary-text large-text bottom-margin"))
    content.extend(rows)
    
    return Section(*content, cls=f"responsive {cls}".strip())

# %% ../nbs/04_web_pages.ipynb 12
def FeaturesGrid(
    features: list,  # List of dicts with 'icon', 'title', 'description'
    title: str = None,  # Optional section title
    subtitle: str = None,  # Optional section subtitle
    cols: int = 3,  # Number of columns (1-4)
    cls: str = ""  # Additional classes
):
    """Grid of feature cards using BeerCSS grid helpers."""
    import fh_matui.components as _cmp
    
    GridCell = getattr(_cmp, 'GridCell', None)
    ResponsiveGrid = getattr(_cmp, 'ResponsiveGrid', None)
    
    if GridCell is None or ResponsiveGrid is None:
        # Fallback: keep previews working even if the notebook kernel has a stale module import.
        def GridCell(*c, span=(), cls='', **kwargs):
            cell_cls = []
            cell_cls.extend(normalize_tokens(span))
            cell_cls.extend(normalize_tokens(cls))
            cell_cls = [t for t in cell_cls if t]
            return Div(*c, cls=stringify(dedupe_preserve_order(cell_cls)), **kwargs)
        
        def ResponsiveGrid(*cells, space='medium-space', cls: str = '', **kwargs):
            cls_tokens = normalize_tokens(cls)
            grid_cls = ['grid']
            if space and space not in cls_tokens:
                grid_cls.extend(normalize_tokens(space))
            grid_cls.extend(cls_tokens)
            grid_cls = [t for t in grid_cls if t]
            return Div(*cells, cls=stringify(dedupe_preserve_order(grid_cls)), **kwargs)
    
    cols = max(1, min(int(cols), 4))
    col_width = 12 // cols  # 12-col grid
    
    s_span = "s12"
    m_span = "m12" if cols == 1 else "m6"
    l_span = f"l{col_width}"
    cell_span = f"{s_span} {m_span} {l_span}".strip()
    
    cells = [
        GridCell(
            Card(
                Div(Icon(f["icon"], size="medium", cls="primary-text"), cls="center-align"),
                H5(f["title"], cls="center-align small-margin"),
                P(f["description"], cls="secondary-text center-align"),
                cls="center-align padding",
            ),
            span=cell_span,
        )
        for f in features
    ]
    
    content = []
    if title:
        content.append(H1(title, cls="center-align bottom-margin"))
    if subtitle:
        content.append(P(subtitle, cls="center-align secondary-text large-margin large-text"))
    
    content.append(ResponsiveGrid(*cells, space="medium-space"))
    # Use responsive for centered max-width layout
    return Section(*content, cls=f"responsive padding {cls}".strip())

# %% ../nbs/04_web_pages.ipynb 16
def _pricing_toggle_js():
    """Returns Script + Style for pricing toggle functionality."""
    css = """
    @keyframes subtle-pulse {
        0%, 100% { transform: scale(1); }
        50% { transform: scale(1.05); }
    }
    .savings-chip { display: none; }
    .savings-chip.active { 
        display: inline-flex; 
        animation: subtle-pulse 0.3s ease-out;
    }
    .pricing-monthly { display: block; }
    .pricing-yearly { display: none; }
    .pricing-yearly.active { display: block; }
    .pricing-monthly.active { display: block; }
    .pricing-yearly:not(.active) { display: none; }
    .pricing-monthly:not(.active) { display: none; }
    """
    
    js = """
    function togglePricing(period) {
        const monthly = document.querySelectorAll('.pricing-monthly');
        const yearly = document.querySelectorAll('.pricing-yearly');
        const chip = document.querySelector('.savings-chip');
        const btnMonthly = document.querySelector('.toggle-monthly');
        const btnYearly = document.querySelector('.toggle-yearly');
        
        if (period === 'yearly') {
            monthly.forEach(el => el.classList.remove('active'));
            yearly.forEach(el => el.classList.add('active'));
            chip?.classList.add('active');
            btnMonthly?.classList.remove('fill');
            btnMonthly?.classList.add('border');
            btnYearly?.classList.add('fill');
            btnYearly?.classList.remove('border');
        } else {
            yearly.forEach(el => el.classList.remove('active'));
            monthly.forEach(el => el.classList.add('active'));
            chip?.classList.remove('active');
            btnYearly?.classList.remove('fill');
            btnYearly?.classList.add('border');
            btnMonthly?.classList.add('fill');
            btnMonthly?.classList.remove('border');
        }
    }
    """
    return [Style(css), Script(js)]


def _calc_savings_pct(monthly_price: float, yearly_price: float) -> int:
    """Calculate savings percentage for yearly vs monthly billing."""
    if monthly_price <= 0:
        return 0
    annual_monthly = monthly_price * 12
    savings = round(100 - (yearly_price / annual_monthly) * 100)
    return max(0, savings)


def _pricing_card(
    plan: dict,  # {name, monthly_price, yearly_price, features, cta_text, cta_href, highlight?}
    span_cls: str = "",  # Grid span classes (optional)
):
    """Render a single pricing card with dual price display."""
    name = plan["name"]
    monthly = plan["monthly_price"]
    yearly = plan["yearly_price"]
    features = plan["features"]
    cta_text = plan["cta_text"]
    cta_href = plan["cta_href"]
    highlight = plan.get("highlight", False)
    
    # Feature list with checkmarks (centered)
    feature_items = [
        Li(Icon("check", cls="primary-text"), " ", f)
        for f in features
    ]
    feature_list = Div(
        Ul(*feature_items, style="list-style: none; padding-left: 0; text-align: left; display: inline-block;"),
        cls="center-align",
    )
    
    # Format prices
    monthly_fmt = f"${monthly:.2f}" if isinstance(monthly, (int, float)) else monthly
    yearly_fmt = f"${yearly:.2f}" if isinstance(yearly, (int, float)) else yearly
    
    # Price display: monthly shown by default, yearly hidden
    price_display = Div(
        Div(
            H2(monthly_fmt, cls="center-align no-margin"),
            P("per month", cls="center-align secondary-text"),
            cls="pricing-monthly active",
        ),
        Div(
            H2(yearly_fmt, cls="center-align no-margin"),
            P("per year", cls="center-align secondary-text"),
            cls="pricing-yearly",
        ),
    )
    
    # Card styling - highlight adds primary-container background
    card_cls = "padding round"
    if highlight:
        card_cls += " primary-container"
    
    card = Card(
        H3(name, cls="center-align"),
        price_display,
        feature_list,
        Button(cta_text, href=cta_href, cls="responsive"),
        cls=card_cls,
    )
    
    # Only wrap in Div with span class if provided (for grid layout)
    if span_cls:
        return Div(card, cls=span_cls)
    return card


def PricingSection(
    title: str,              # Section title
    plans: list,             # List of plan dicts: {name, monthly_price, yearly_price, features, cta_text, cta_href, highlight?}
    cls: str = "",           # Additional classes
):
    """Multi-tier pricing section with monthly/yearly toggle.
    
    Supports 1-3 pricing tiers with automatic responsive layout.
    User provides prices; component handles toggle, savings display, and grid.
    
    Args:
        title: Section heading (e.g., "Simple Pricing")
        plans: List of plan dicts, each containing:
            - name: Plan name (e.g., "Starter", "Pro", "Enterprise")
            - monthly_price: Monthly price as float (e.g., 9.99)
            - yearly_price: Yearly price as float (e.g., 99.99)
            - features: List of feature strings
            - cta_text: Button text (e.g., "Get Started")
            - cta_href: Button link
            - highlight: Optional bool to emphasize this tier (default False)
        cls: Additional CSS classes
    
    Example:
        PricingSection(
            title="Choose Your Plan",
            plans=[
                {"name": "Starter", "monthly_price": 9.99, "yearly_price": 99.99,
                 "features": ["5 users", "Basic support"], "cta_text": "Start Free", "cta_href": "/signup"},
                {"name": "Pro", "monthly_price": 29.99, "yearly_price": 299.99,
                 "features": ["Unlimited users", "Priority support"], "cta_text": "Get Pro", "cta_href": "/signup", "highlight": True},
            ]
        )
    """
    # Calculate max savings across all plans for the chip
    max_savings = 0
    for plan in plans:
        savings = _calc_savings_pct(plan["monthly_price"], plan["yearly_price"])
        max_savings = max(max_savings, savings)
    
    # Toggle bar: Monthly | Yearly + Savings chip
    savings_chip = Span(
        f"Save {max_savings}%", 
        cls="chip small green white-text savings-chip",
    ) if max_savings > 0 else ""
    
    toggle = Div(
        Nav(
            FhButton(
                "Monthly",
                cls="toggle-monthly left-round fill small",
                onclick="togglePricing('monthly')",
            ),
            FhButton(
                "Yearly",
                cls="toggle-yearly right-round border small",
                onclick="togglePricing('yearly')",
            ),
            cls="",
        ),
        savings_chip,
        cls="row center-align middle-align small-space bottom-margin",
    )
    
    # Determine layout based on number of plans
    num_plans = len(plans)
    
    if num_plans == 1:
        # Single card: use flexbox centering, no grid
        card = _pricing_card(plans[0], span_cls="")
        grid = Div(
            Div(card, cls="medium-width"),
            cls="row center-align",
        )
    elif num_plans == 2:
        # Two cards: use grid with s12 m6
        cards = [_pricing_card(plan, span_cls="s12 m6") for plan in plans]
        grid = Div(*cards, cls="grid medium-space")
    else:
        # Three or more cards: use grid with s12 m6 l4
        cards = [_pricing_card(plan, span_cls="s12 m6 l4") for plan in plans]
        grid = Div(*cards, cls="grid medium-space")
    
    # Assemble section - use responsive for centered max-width layout
    return Section(
        *_pricing_toggle_js(),
        H2(title, cls="center-align bottom-margin"),
        toggle,
        grid,
        cls=f"responsive padding {cls}".strip(),
    )

# %% ../nbs/04_web_pages.ipynb 20
def FAQSection(title: str, faqs: list, cls: str = ""):
    """FAQ section using FAQItem from components.
    
    Wraps multiple FAQItem components with a title and consistent spacing.
    Uses FAQItem from fh_matui.components for the actual collapsible Q&A.
    
    Args:
        title: Section heading
        faqs: List of dicts with 'question' and 'answer' keys
        cls: Additional CSS classes
    """
    from fh_matui.components import FAQItem
    
    items = [
        Div(FAQItem(faq['question'], faq['answer']), cls="small-margin")
        for faq in faqs
    ]
    
    return Section(
        H2(title, cls="center-align bottom-margin"),
        Div(*items, cls="column"),
        cls=f"responsive column {cls}".strip(),
    )

# %% ../nbs/04_web_pages.ipynb 23
# CSS for decorative sine wave border on footer
_FOOTER_WAVE_CSS = """
.footer-wave {
    position: relative;
}
.footer-wave::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 12px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 12' preserveAspectRatio='none'%3E%3Cpath d='M0,6 Q25,0 50,6 T100,6 T150,6 T200,6 T250,6 T300,6 T350,6 T400,6 T450,6 T500,6 T550,6 T600,6 T650,6 T700,6 T750,6 T800,6 T850,6 T900,6 T950,6 T1000,6 T1050,6 T1100,6 T1150,6 T1200,6' fill='none' stroke='%23888' stroke-width='1'/%3E%3C/svg%3E");
    background-size: 100% 100%;
    background-repeat: no-repeat;
    opacity: 0.5;
}
"""

def PageFooter(
    columns: list,         # List of dicts with 'title' and 'links' (list of dicts with 'text' and 'href')
    copyright: str,        # Copyright text
    social_links: list = None,  # List of dicts with 'icon' and 'href'
    logo: str = None,      # Optional logo text
    wave_border: bool = True,   # Show decorative wave border at top
    cls: str = ""          # Additional classes
):
    """Footer with multiple columns, social links, and copyright.
    
    Features a decorative sine wave border at the top for elegant visual separation.
    Blends with page background (no container fill) for seamless appearance.
    """
    footer_cols = []
    
    # Logo column if provided
    if logo:
        footer_cols.append(
            Div(
                H6(logo, cls='no-margin bold'),
                cls='padding'
            )
        )
    
    # Link columns - vertically stacked
    for col in columns:
        links = [A(link['text'], href=link['href'], cls='grey-text') for link in col.get('links', [])]
        footer_cols.append(
            Div(
                H6(col['title'], cls='no-margin bold'),
                DivVStacked(*links, cls='small-space'),
                cls='padding'
            )
        )
    
    # Footer row with full spacing between columns
    footer_row = DivFullySpaced(*footer_cols)
    
    # Bottom section with copyright and social links
    bottom_left = Div(P(copyright, cls='small-text grey-text no-margin'))
    
    bottom_right = Div()
    if social_links:
        social_icons = [A(Icon(s['icon']), href=s['href'], cls='grey-text') for s in social_links]
        bottom_right = Div(*social_icons, cls='row small-space')
    
    bottom_section = DivFullySpaced(bottom_left, bottom_right)
    
    # Footer blends with page (no surface-container), wave provides visual separation
    footer_cls = f'footer-wave {cls}'.strip() if wave_border else cls
    
    # Include wave CSS if enabled
    wave_style = Style(_FOOTER_WAVE_CSS) if wave_border else ""
    
    return ft_hx('footer')(
        wave_style,
        Div(
            footer_row,
            Hr(),
            bottom_section,
            cls="responsive padding",  # Content centered
        ),
        cls=footer_cls,
        style="padding-top: 1.5rem;" if wave_border else "",  # Space for wave
    )

# %% ../nbs/04_web_pages.ipynb 25
def LandingNavBar(
    brand_name: str,        # Brand name for the navbar
    links: list = None,     # List of dicts with 'text' and 'href'
    actions: list = None,   # List of action buttons (Buttons/Links)
    sticky: bool = True,    # Whether navbar sticks to top
    cls: str = ""           # Additional classes
):
    """
    Landing page navigation bar with frosted glass effect.
    
    Uses BeerCSS blur classes for translucent glass appearance.
    Sticky by default to stay visible while scrolling.
    Content is centered with max-width using 'responsive' class.
    Brand name links to home page.
    
    Args:
        brand_name: Brand name/logo text
        links: List of navigation links [{'text': 'Features', 'href': '#features'}, ...]
        actions: List of action elements (Buttons, etc.) for CTA
        sticky: Whether navbar sticks to top while scrolling (default: True)
        cls: Additional CSS classes
    """
    nav_items = []
    
    # Brand - links to home page
    nav_items.append(A(H5(brand_name, cls="no-margin bold"), href="/"))
    
    # Spacer to push links right
    nav_items.append(Div(cls="max"))
    
    # Navigation links
    if links:
        for link in links:
            nav_items.append(A(link['text'], href=link['href'], cls="padding"))
    
    # Action buttons
    if actions:
        nav_items.extend(actions)
    
    # Inner content: responsive centers it, row for horizontal layout
    inner = Div(*nav_items, cls="responsive row middle-align")
    
    # Sticky positioning + BeerCSS blur/glass classes
    sticky_cls = "fixed top left right" if sticky else ""
    toolbar_cls = f"blur large-blur surface-container-low {sticky_cls} {cls}".strip()
    
    return Header(inner, cls=toolbar_cls)

# %% ../nbs/04_web_pages.ipynb 27
# Section spacing: padding for internal space, margin for breathing room between sections
SECTION_STYLE = "large-padding"  # Internal padding
SECTION_MARGIN = "extra-margin bottom-margin"  # Vertical spacing between sections

# Standard footer columns - consistent across all pages (3 items each for balanced width)
STANDARD_FOOTER_COLUMNS = [
    {"title": "Features", "links": [
        {"text": "Tracking", "href": "#features"},
        {"text": "Budgeting", "href": "#features"},
        {"text": "Planning", "href": "#features"},
    ]},
    {"title": "Support", "links": [
        {"text": "Help Center", "href": "/help"},
        {"text": "Contact", "href": "/contact"},
        {"text": "FAQ", "href": "#faq"},
    ]},
    {"title": "Legal", "links": [
        {"text": "Terms of Use", "href": "/terms"},
        {"text": "Privacy Policy", "href": "/privacy"},
        {"text": "Security", "href": "/security"},
    ]},
]

# CSS for smooth scrolling and sticky navbar offset (5rem = ~4rem navbar + 1rem breathing room)
_LANDING_PAGE_CSS = """
html {
    scroll-behavior: smooth;
}
/* Offset anchor targets to account for sticky navbar */
#benefits, #features, #pricing, #faq {
    scroll-margin-top: 5rem;
}
/* Centered mini-wave separator between sections */
.section-wave {
    display: flex;
    justify-content: center;
    padding: 1.5rem 0;
}
.section-wave::before {
    content: '';
    width: 150px;
    height: 12px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 150 12' preserveAspectRatio='none'%3E%3Cpath d='M0,6 Q12.5,0 25,6 T50,6 T75,6 T100,6 T125,6 T150,6' fill='none' stroke='%23888' stroke-width='1.5'/%3E%3C/svg%3E");
    background-size: 100% 100%;
    background-repeat: no-repeat;
    opacity: 0.4;
}
"""

# Internal helper - centered wave separator between sections
def _SectionWave(): return Div(cls="section-wave")

def LandingPageSimple(
    # Required
    brand_name: str,
    hero_title: str,
    hero_subtitle: str,
    hero_primary_cta: dict,           # {'text': 'Get Started', 'href': '/signup'}
    footer_copyright: str,            # Required copyright text
    # Optional Hero
    hero_secondary_cta: dict = None,  # Optional secondary CTA
    # Optional Benefits (FeatureShowcase - alternating rows)
    benefits: list = None,            # List of dicts: {title, description, icon?, image_src?}
    benefits_title: str = None,
    benefits_subtitle: str = None,
    # Optional Features (FeaturesGrid - icon cards)
    features: list = None,            # List of dicts: {icon, title, description}
    features_title: str = None,
    features_subtitle: str = None,
    # Optional Pricing
    pricing_plans: list = None,       # List of plan dicts for PricingSection
    pricing_title: str = "Pricing",
    # Optional FAQ
    faqs: list = None,                # List of FAQ dicts
    faq_title: str = "Frequently Asked Questions",
    # Optional Footer extras
    footer_columns: list = None,
    footer_social_links: list = None,
    # Nav customization
    nav_links: list = None,           # Override default nav links
    nav_actions: list = None,         # CTA buttons in navbar
    cls: str = "",
):
    """Landing page component - supply your content, get a complete page.
    
    This is a reusable template component. Pass your brand info, hero content,
    features, pricing, and FAQs - it assembles a complete marketing landing page.
    
    Includes smooth scrolling and proper anchor offset to prevent sticky navbar
    from hiding section headings when navigating via anchor links.
    
    Layout approach: Main uses "max" for full-width backgrounds.
    Each section handles its own content centering with "responsive" wrapper.
    This allows gradients/backgrounds to extend edge-to-edge naturally.
    
    Section order: Hero → Benefits → Features → Pricing → FAQ → Footer
    
    Required: brand_name, hero_title, hero_subtitle, hero_primary_cta, footer_copyright
    Optional: Benefits, Features, Pricing, FAQ (pass None to skip any section)
    """
    # Static nav links - convention: all sections available
    default_nav_links = [
        {"text": "Benefits", "href": "#benefits"},
        {"text": "Features", "href": "#features"},
        {"text": "Pricing", "href": "#pricing"},
        {"text": "FAQ", "href": "#faq"},
    ]
    navbar = LandingNavBar(
        brand_name=brand_name,
        links=nav_links or default_nav_links,
        actions=nav_actions,
        sticky=True,
    )
    
    main_sections = []
    
    # 1. Hero (required) - full-width, content centered internally
    hero = HeroSection(
        title=hero_title,
        subtitle=hero_subtitle,
        primary_cta_text=hero_primary_cta['text'],
        primary_cta_href=hero_primary_cta['href'],
        secondary_cta_text=hero_secondary_cta.get('text') if hero_secondary_cta else None,
        secondary_cta_href=hero_secondary_cta.get('href') if hero_secondary_cta else None,
        cls=SECTION_MARGIN,
    )
    main_sections.append(hero)
    
    # 2. Benefits - FeatureShowcase (optional) - has responsive internally
    if benefits:
        main_sections.append(_SectionWave())
        benefits_section = FeatureShowcase(
            features=benefits,
            title=benefits_title,
            subtitle=benefits_subtitle,
            cls=SECTION_STYLE,
        )
        main_sections.append(Div(benefits_section, id="benefits", cls=SECTION_MARGIN))
    
    # 3. Features - FeaturesGrid (optional) - has responsive internally
    if features:
        main_sections.append(_SectionWave())
        features_section = FeaturesGrid(
            features=features,
            title=features_title,
            subtitle=features_subtitle,
            cols=3,
            cls=SECTION_STYLE,
        )
        main_sections.append(Div(features_section, id="features", cls=SECTION_MARGIN))
    
    # 4. Pricing (optional) - has responsive internally
    if pricing_plans:
        main_sections.append(_SectionWave())
        pricing_section = PricingSection(
            title=pricing_title,
            plans=pricing_plans,
            cls=SECTION_STYLE,
        )
        main_sections.append(Div(pricing_section, id="pricing", cls=SECTION_MARGIN))
    
    # 5. FAQ (optional) - has responsive internally
    if faqs:
        main_sections.append(_SectionWave())
        faq_section = FAQSection(
            title=faq_title,
            faqs=faqs,
            cls=SECTION_STYLE,
        )
        main_sections.append(Div(faq_section, id="faq", cls=SECTION_MARGIN))
    
    # Footer (required) - use standard columns if none provided
    footer_el = PageFooter(
        columns=footer_columns or STANDARD_FOOTER_COLUMNS,
        copyright=footer_copyright,
        social_links=footer_social_links,
        logo=brand_name,
        cls="large-padding",
    )
    
    # Main uses "max" for full-width - sections extend edge-to-edge
    # Each section handles its own content centering with responsive
    main_content = Main(*main_sections, cls="max")
    
    # Include CSS for smooth scrolling and anchor offset
    return Div(
        Style(_LANDING_PAGE_CSS),
        navbar, 
        main_content, 
        footer_el, 
        cls=f"column {cls}".strip()
    )

# %% ../nbs/04_web_pages.ipynb 30
def MarkdownSection(
    content: str,           # Markdown text to render
    title: str = None,      # Optional section title (rendered as H5)
    cls: str = "",          # Additional classes
):
    """Renders markdown content server-side for SEO compatibility.
    
    Uses python-markdown to convert markdown to HTML on the server.
    Search engines see fully rendered HTML (no JavaScript required).
    Parent ContentPage uses 'min' class to center the page layout.
    
    Great for text-heavy pages like Privacy Policy, Terms, About, Blog posts, etc.
    
    Args:
        content: Markdown text string (can include headers, lists, links, code blocks, tables)
        title: Optional page title (rendered before markdown content)
        cls: Additional CSS classes
    
    Example:
        MarkdownSection(
            title="Privacy Policy",
            content='''
## Introduction
We value your privacy...

## Data Collection
- We collect minimal data
- We never sell your data
            '''
        )
    """
    import markdown
    
    # Server-side render markdown to HTML (SEO-friendly)
    html_content = markdown.markdown(
        content, 
        extensions=['tables', 'fenced_code', 'nl2br']
    )
    
    elements = []
    if title:
        elements.append(H5(title, cls="center-align"))
    
    # NotStr tells FastHTML to render raw HTML without escaping
    elements.append(NotStr(html_content))
    
    return Article(*elements, cls=f"large-padding {cls}".strip())

# %% ../nbs/04_web_pages.ipynb 31
def ContentPage(
    brand_name: str,                  # Brand name for navbar
    footer_copyright: str,            # Copyright text for footer
    *sections,                        # Content sections (MarkdownSection, custom Divs, etc.)
    nav_links: list = None,           # Override default nav links
    nav_actions: list = None,         # CTA buttons in navbar
    footer_columns: list = None,      # Footer link columns (uses STANDARD_FOOTER_COLUMNS by default)
    footer_social_links: list = None, # Footer social icons
    cls: str = "",
):
    """Generic content page with navbar, flexible content area, and footer.
    
    A shell template for text-heavy pages like Privacy Policy, Terms of Service,
    Security, About, Blog posts, etc. Developer passes any number of content sections.
    
    Layout: Navbar (sticky) -> Content sections (centered with 'min') -> Footer
    
    Uses STANDARD_FOOTER_COLUMNS by default for consistent footer across all pages.
    
    Args:
        brand_name: Brand name for navbar
        footer_copyright: Copyright text
        *sections: One or more content sections (MarkdownSection, custom Divs, etc.)
        nav_links: Navigation links [{'text': 'Home', 'href': '/'}, ...]
        nav_actions: Action buttons for navbar
        footer_columns: Footer link columns (defaults to STANDARD_FOOTER_COLUMNS)
        footer_social_links: Social media icons for footer
        cls: Additional CSS classes
    
    Example:
        ContentPage(
            brand_name="MyBrand",
            footer_copyright="2026 MyBrand",
            MarkdownSection(title="Privacy Policy", content="...markdown..."),
            nav_links=[{'text': 'Home', 'href': '/'}],
        )
    """
    # Default nav links for content pages - just Home, no anchor links
    default_nav_links = [
        {"text": "Home", "href": "/"},
    ]
    
    navbar = LandingNavBar(
        brand_name=brand_name,
        links=nav_links or default_nav_links,
        actions=nav_actions,
        sticky=True,
    )
    
    footer_el = PageFooter(
        columns=footer_columns or STANDARD_FOOTER_COLUMNS,
        copyright=footer_copyright,
        social_links=footer_social_links,
        logo=brand_name,
        cls="large-padding",
    )
    
    # Main content area - use "min" to center content (unlike landing page which uses "max")
    main_content = Main(*sections, cls="min large-padding")
    
    return Div(navbar, main_content, footer_el, cls=f"column {cls}".strip())
