public/layout/New-UiCard.ps1

function New-UiCard {
    <#
    .SYNOPSIS
        Creates a bordered card container for content display.
    .DESCRIPTION
        Creates a styled card container with optional header, icon, and accent color.
        Ideal for dashboard tiles, summary boxes, status displays, and grouped information.
    .PARAMETER Header
        Optional header text displayed at the top of the card.
    .PARAMETER Content
        ScriptBlock containing the card's content controls.
    .PARAMETER Icon
        Optional icon name from Segoe MDL2 Assets to display in the header.
    .PARAMETER Accent
        Use the theme's accent color for the card header/border.
    .PARAMETER HeaderBackground
        Custom background color for the header (hex color like '#0078D4').
    .PARAMETER MinWidth
        Minimum width of the card. Default is 200.
    .PARAMETER MinHeight
        Minimum height of the card.
    .PARAMETER FullWidth
        When present, the card expands to fill the full width of its container.
    .PARAMETER Stretch
        When present, the card participates in the responsive column system.
        Cards will resize dynamically based on window width and MaxColumns setting.
    .PARAMETER WPFProperties
        Hashtable of additional WPF properties to set on the control.
    .EXAMPLE
        New-UiCard -Header "System Status" -Icon "Info" -Content {
            New-UiLabel -Text "All systems operational" -Style Body
        }
    .EXAMPLE
        New-UiCard -Header "Statistics" -Accent -Content {
            New-UiLabel -Text "Users: 142" -Style SubHeader
            New-UiLabel -Text "Active sessions: 28" -Style Body
        }
    .EXAMPLE
        New-UiCard -Content {
            New-UiLabel -Text "Simple card without header" -Style Body
        }
    #>

    [CmdletBinding()]
    param(
        [string]$Header,

        [Parameter(Mandatory)]
        [scriptblock]$Content,

        [switch]$Accent,

        [string]$HeaderBackground,

        [int]$MinWidth = 200,

        [int]$MinHeight,

        [switch]$FullWidth,

        [switch]$Stretch,

        [Parameter()]
        [hashtable]$WPFProperties
    )

    DynamicParam {
        Get-IconDynamicParameter -ParameterName 'Icon'
    }

    begin {
        $Icon = $PSBoundParameters['Icon']
    }

    process {

    $session = Assert-UiSession -CallerName 'New-UiCard'
    $colors  = Get-ThemeColors
    $parent  = $session.CurrentParent
    Write-Debug "Header: '$Header', Accent: $($Accent.IsPresent), Parent: $($parent.GetType().Name)"

    # Create outer border (the card container)
    $card = [System.Windows.Controls.Border]@{
        BorderThickness = [System.Windows.Thickness]::new(1)
        CornerRadius    = [System.Windows.CornerRadius]::new(4)
        Margin          = [System.Windows.Thickness]::new(4)
        MinWidth        = $MinWidth
        Background      = ConvertTo-UiBrush $colors.ControlBg
        BorderBrush     = ConvertTo-UiBrush $colors.Border
        SnapsToDevicePixels = $true
        Tag             = 'CardBorder'
    }

    if ($MinHeight -gt 0) {
        $card.MinHeight = $MinHeight
    }

    # Add subtle shadow effect
    try {
        $shadow = [System.Windows.Media.Effects.DropShadowEffect]@{
            BlurRadius  = 8
            ShadowDepth = 2
            Opacity     = 0.15
            Color       = [System.Windows.Media.Colors]::Black
        }
        $card.Effect = $shadow
    }
    catch {
        Write-Verbose "Failed to apply shadow effect: $_"
    }

    # Main content container
    $mainStack = [System.Windows.Controls.StackPanel]::new()

    # Add header if specified
    if ($Header) {
        # Store whether this is an accent header for theme updates
        $isAccentHeader = $Accent -or ($HeaderBackground -ne $null -and $HeaderBackground -ne '')

        $headerBorder = [System.Windows.Controls.Border]@{
            Padding = [System.Windows.Thickness]::new(12, 8, 12, 8)
            CornerRadius = [System.Windows.CornerRadius]::new(3, 3, 0, 0)
            Tag = @{ Type = 'CardHeader'; IsAccent = $isAccentHeader; CustomColor = $HeaderBackground }
        }

        # Determine header background color
        if ($HeaderBackground) {
            $headerBorder.Background = ConvertTo-UiBrush $HeaderBackground
        }
        elseif ($Accent) {
            $headerBorder.Background = ConvertTo-UiBrush $colors.Accent
        }
        else {
            # Subtle header background
            $headerBgColor = if ($colors.GroupBoxBg) { $colors.GroupBoxBg } else { $colors.WindowBg }
            $headerBorder.Background = ConvertTo-UiBrush $headerBgColor
        }

        # Header content (icon + text)
        $headerPanel = [System.Windows.Controls.StackPanel]@{
            Orientation = [System.Windows.Controls.Orientation]::Horizontal
        }

        # Add icon if specified
        if ($Icon) {
            # Get icon character from cached module context
            $iconChar = [PsUi.ModuleContext]::GetIcon($Icon)

            if ($iconChar) {
                # Icon color logic:
                # If Accent is used, the header background is the accent color.
                # Use AccentHeaderForegroundBrush for contrast against accent bg.
                # If NOT Accent, we use the Accent color for the icon to make it pop.
                
                $iconBrushKey = 'AccentBrush'
                $iconForeground = $null

                if ($Accent) {
                    # Header is Accent -> Icon needs contrast against accent background
                    $iconForeground = ConvertTo-UiBrush $colors.AccentHeaderFg
                    $iconBrushKey = 'AccentHeaderForegroundBrush'
                }
                else {
                    # Header is Neutral -> Icon is Accent
                    $iconForeground = ConvertTo-UiBrush $colors.Accent
                    $iconBrushKey = 'AccentBrush'
                }

                $iconBlock = [System.Windows.Controls.TextBlock]@{
                    Text       = $iconChar
                    FontFamily = [System.Windows.Media.FontFamily]::new('Segoe MDL2 Assets')
                    FontSize   = 16
                    Foreground = $iconForeground
                    VerticalAlignment = [System.Windows.VerticalAlignment]::Center
                    Margin     = [System.Windows.Thickness]::new(0, 0, 8, 0)
                    Tag        = $iconBrushKey
                }
                [PsUi.ThemeEngine]::RegisterElement($iconBlock)
                [void]$headerPanel.Children.Add($iconBlock)
            }
        }

        # Header text
        $headerBgForText = if ($HeaderBackground) { $HeaderBackground } else { $colors.Accent }
        
        # Determine text brush key for theme updates
        $textBrushKey = 'ControlFgBrush'
        $headerForeground = $null

        if ($Accent -and !$HeaderBackground) {
            # Accent Header -> Text needs contrast against accent background
            $headerForeground = ConvertTo-UiBrush $colors.AccentHeaderFg
            $textBrushKey = 'AccentHeaderForegroundBrush'
        }
        elseif ($HeaderBackground) {
            # Custom Header -> Calculate contrast
            $headerForeground = ConvertTo-UiBrush (Get-ContrastColor -HexColor $HeaderBackground)
            # No brush key for custom colors
        }
        else {
            # Standard Header -> Standard Text
            $headerForeground = ConvertTo-UiBrush $colors.ControlFg
            $textBrushKey = 'ControlForegroundBrush'
        }

        $headerText = [System.Windows.Controls.TextBlock]@{
            Text       = $Header
            FontSize   = 14
            FontWeight = [System.Windows.FontWeights]::SemiBold
            Foreground = $headerForeground
            VerticalAlignment = [System.Windows.VerticalAlignment]::Center
            Tag        = $textBrushKey
        }
        # Only register if not using custom color
        if (!$HeaderBackground) {
            [PsUi.ThemeEngine]::RegisterElement($headerText)
        }
        [void]$headerPanel.Children.Add($headerText)

        # Store accent info on header panel for theme updates
        $headerPanel.Tag = @{ Type = 'CardHeaderPanel'; IsAccent = $isAccentHeader; CustomColor = $HeaderBackground }

        $headerBorder.Child = $headerPanel
        
        # Register header border for theme updates (unless using custom color)
        if (!$HeaderBackground) {
            [PsUi.ThemeEngine]::RegisterElement($headerBorder)
        }
        [void]$mainStack.Children.Add($headerBorder)

        # Add separator line
        $separator = [System.Windows.Controls.Border]@{
            Height     = 1
            Background = ConvertTo-UiBrush $colors.Border
            Tag        = 'CardSeparator'
        }
        [PsUi.ThemeEngine]::RegisterElement($separator)
        [void]$mainStack.Children.Add($separator)
    }

    $contentPanel = [System.Windows.Controls.StackPanel]@{
        Margin = [System.Windows.Thickness]::new(12, 10, 12, 12)
    }

    # Set content panel as current parent and execute content
    $oldParent = $session.CurrentParent
    $session.CurrentParent = $contentPanel

    # Execute content - restore parent outside try/finally for PS 5.1 closure compatibility
    try {
        Invoke-UiContent -Content $Content -CallerName 'New-UiCard' -ErrorAction Stop
    }
    catch {
        # Restore parent before re-throwing
        $session.CurrentParent = $oldParent
        throw
    }
    
    # Restore parent after successful content execution
    $session.CurrentParent = $oldParent

    [void]$mainStack.Children.Add($contentPanel)
    $card.Child = $mainStack

    # Width constraints
    if ($parent -is [System.Windows.Controls.WrapPanel]) {
        $card.HorizontalAlignment = 'Stretch'

        # FullWidth in WrapPanel context means span the entire panel width
        if ($FullWidth) {
            Set-FullWidthConstraint -Control $card -Parent $parent -FullWidth
        }
    }
    elseif ($Stretch) {
        # Use responsive column system - card will resize with window
        Set-ResponsiveConstraints -Control $card -FullWidth:$FullWidth
    }
    elseif ($FullWidth) {
        # Full width only (no responsive columns)
        Set-FullWidthConstraint -Control $card -Parent $parent -FullWidth:$FullWidth
    }

    # Apply custom WPF properties if specified
    if ($WPFProperties) {
        Set-UiProperties -Control $card -Properties $WPFProperties
    }

    [void]$parent.Children.Add($card)
    Write-Debug "Card added to parent"

    # Register with ThemeEngine for theme updates
    try {
        [PsUi.ThemeEngine]::RegisterElement($card)
    }
    catch {
        Write-Verbose "Failed to register Card with ThemeEngine: $_"
    }
    }
}