// <FILE>crates/mcu-dynamiccolor/src/color_spec_2025.rs</FILE> - <DESC>2025 Material Design 3 color spec delegate implementation</DESC>
// <VERS>VERSION: 1.2.0</VERS>
// <WCTX>Fix surface_dim dark mode tone to be distinct from surface</WCTX>
// <CLOG>Change surface_dim dark mode from tone 4 to tone 2; add upstream-surface-dim-tone feature flag for compatibility</CLOG>

use mcu_hct::Hct;
use mcu_palettes::TonalPalette;
use mcu_utils::math::clamp_double;

use crate::{
    ColorSpecDelegate, ContrastCurve, DeltaConstraint, DynamicColor, DynamicScheme, Platform,
    ToneDeltaPair, TonePolarity, Variant,
};

/// Helper function to find the maximum tone for a given chroma in the palette.
fn t_max_c(
    palette: &TonalPalette,
    lower_bound: f64,
    upper_bound: f64,
    chroma_multiplier: f64,
) -> f64 {
    let answer = find_best_tone_for_chroma(
        palette.hue(),
        palette.chroma() * chroma_multiplier,
        100.0,
        true,
    );
    clamp_double(lower_bound, upper_bound, answer)
}

/// Helper function to find the minimum tone for a given chroma in the palette.
fn t_min_c(palette: &TonalPalette, lower_bound: f64, upper_bound: f64) -> f64 {
    let answer = find_best_tone_for_chroma(palette.hue(), palette.chroma(), 0.0, false);
    clamp_double(lower_bound, upper_bound, answer)
}

/// Searches for the best tone with a given chroma from a given tone at a specific hue.
fn find_best_tone_for_chroma(
    hue: f64,
    chroma: f64,
    mut tone: f64,
    by_decreasing_tone: bool,
) -> f64 {
    let mut answer = tone;
    let mut best_candidate = Hct::from(hue, chroma, answer);

    while best_candidate.chroma() < chroma {
        if tone < 0.0 || tone > 100.0 {
            break;
        }
        tone += if by_decreasing_tone { -1.0 } else { 1.0 };
        let new_candidate = Hct::from(hue, chroma, tone);
        if best_candidate.chroma() < new_candidate.chroma() {
            best_candidate = new_candidate;
            answer = tone;
        }
    }

    answer
}

/// Returns the contrast curve for a given default contrast.
fn get_curve(default_contrast: f64) -> ContrastCurve {
    if (default_contrast - 1.5).abs() < 0.01 {
        ContrastCurve::new(1.5, 1.5, 3.0, 5.5)
    } else if (default_contrast - 3.0).abs() < 0.01 {
        ContrastCurve::new(3.0, 3.0, 4.5, 7.0)
    } else if (default_contrast - 4.5).abs() < 0.01 {
        ContrastCurve::new(4.5, 4.5, 7.0, 11.0)
    } else if (default_contrast - 6.0).abs() < 0.01 {
        ContrastCurve::new(6.0, 6.0, 7.0, 11.0)
    } else if (default_contrast - 7.0).abs() < 0.01 {
        ContrastCurve::new(7.0, 7.0, 11.0, 21.0)
    } else if (default_contrast - 9.0).abs() < 0.01 {
        ContrastCurve::new(9.0, 9.0, 11.0, 21.0)
    } else if (default_contrast - 11.0).abs() < 0.01 {
        ContrastCurve::new(11.0, 11.0, 21.0, 21.0)
    } else if (default_contrast - 21.0).abs() < 0.01 {
        ContrastCurve::new(21.0, 21.0, 21.0, 21.0)
    } else {
        // Fallback
        ContrastCurve::new(default_contrast, default_contrast, 7.0, 21.0)
    }
}

/// A delegate for the dynamic color spec of a DynamicScheme in the 2025 spec.
#[derive(Clone, Copy)]
pub struct ColorSpecDelegateImpl2025;

impl ColorSpecDelegateImpl2025 {
    /// Helper to get the highest surface color for a scheme.
    fn highest_surface(&self, scheme: &DynamicScheme) -> DynamicColor {
        if scheme.is_dark {
            self.surface_bright()
        } else {
            self.surface_dim()
        }
    }
}

impl ColorSpecDelegate for ColorSpecDelegateImpl2025 {
    // ================================================================
    // Main Palettes
    // ================================================================

    fn primary_palette_key_color(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "primary_palette_key_color",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| s.primary_palette.key_color().tone()),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn secondary_palette_key_color(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "secondary_palette_key_color",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|s: &DynamicScheme| s.secondary_palette.key_color().tone()),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn tertiary_palette_key_color(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "tertiary_palette_key_color",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|s: &DynamicScheme| s.tertiary_palette.key_color().tone()),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn neutral_palette_key_color(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "neutral_palette_key_color",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| s.neutral_palette.key_color().tone()),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn neutral_variant_palette_key_color(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "neutral_variant_palette_key_color",
            |s: &DynamicScheme| s.neutral_variant_palette.clone(),
            Some(|s: &DynamicScheme| s.neutral_variant_palette.key_color().tone()),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn error_palette_key_color(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "error_palette_key_color",
            |s: &DynamicScheme| s.error_palette.clone(),
            Some(|s: &DynamicScheme| s.error_palette.key_color().tone()),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Surfaces [S]
    // ================================================================

    fn background(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "background",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 6.0 } else { 98.0 }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn on_background(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_background",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    100.0
                } else {
                    delegate_ref.on_surface().tone.as_ref()(s)
                }
            }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.background())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(9.0))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    if s.is_dark {
                        4.0
                    } else if Hct::is_yellow(s.neutral_palette.hue()) {
                        99.0
                    } else if s.variant == Variant::Vibrant {
                        97.0
                    } else {
                        98.0
                    }
                } else {
                    0.0
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_dim(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_dim",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.is_dark {
                    // Upstream MCU uses tone 4, same as surface, which defeats the purpose
                    // of having a "dim" variant. We default to tone 2 for proper hierarchy:
                    // container_lowest(0) < surface_dim(2) < surface(4) < container_low(6)
                    #[cfg(feature = "upstream-surface-dim-tone")]
                    {
                        4.0
                    }
                    #[cfg(not(feature = "upstream-surface-dim-tone"))]
                    {
                        2.0
                    }
                } else if Hct::is_yellow(s.neutral_palette.hue()) {
                    90.0
                } else if s.variant == Variant::Vibrant {
                    85.0
                } else {
                    87.0
                }
            }),
            true,
            Some(|s: &DynamicScheme| {
                if !s.is_dark {
                    match s.variant {
                        Variant::Neutral => 2.5,
                        Variant::TonalSpot => 1.7,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                2.7
                            } else {
                                1.75
                            }
                        }
                        Variant::Vibrant => 1.36,
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_bright(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_bright",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.is_dark {
                    18.0
                } else if Hct::is_yellow(s.neutral_palette.hue()) {
                    99.0
                } else if s.variant == Variant::Vibrant {
                    97.0
                } else {
                    98.0
                }
            }),
            true,
            Some(|s: &DynamicScheme| {
                if s.is_dark {
                    match s.variant {
                        Variant::Neutral => 2.5,
                        Variant::TonalSpot => 1.7,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                2.7
                            } else {
                                1.75
                            }
                        }
                        Variant::Vibrant => 1.36,
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_container_lowest(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_container_lowest",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 0.0 } else { 100.0 }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_container_low(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_container_low",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    if s.is_dark {
                        6.0
                    } else if Hct::is_yellow(s.neutral_palette.hue()) {
                        98.0
                    } else if s.variant == Variant::Vibrant {
                        95.0
                    } else {
                        96.0
                    }
                } else {
                    15.0
                }
            }),
            true,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 1.3,
                        Variant::TonalSpot => 1.25,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                1.3
                            } else {
                                1.15
                            }
                        }
                        Variant::Vibrant => 1.08,
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_container(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_container",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    if s.is_dark {
                        9.0
                    } else if Hct::is_yellow(s.neutral_palette.hue()) {
                        96.0
                    } else if s.variant == Variant::Vibrant {
                        92.0
                    } else {
                        94.0
                    }
                } else {
                    20.0
                }
            }),
            true,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 1.6,
                        Variant::TonalSpot => 1.4,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                1.6
                            } else {
                                1.3
                            }
                        }
                        Variant::Vibrant => 1.15,
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_container_high(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_container_high",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    if s.is_dark {
                        12.0
                    } else if Hct::is_yellow(s.neutral_palette.hue()) {
                        94.0
                    } else if s.variant == Variant::Vibrant {
                        90.0
                    } else {
                        92.0
                    }
                } else {
                    25.0
                }
            }),
            true,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 1.9,
                        Variant::TonalSpot => 1.5,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                1.95
                            } else {
                                1.45
                            }
                        }
                        Variant::Vibrant => 1.22,
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_container_highest(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "surface_container_highest",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.is_dark {
                    15.0
                } else if Hct::is_yellow(s.neutral_palette.hue()) {
                    92.0
                } else if s.variant == Variant::Vibrant {
                    88.0
                } else {
                    90.0
                }
            }),
            true,
            Some(|s: &DynamicScheme| match s.variant {
                Variant::Neutral => 2.2,
                Variant::TonalSpot => 1.7,
                Variant::Expressive => {
                    if Hct::is_yellow(s.neutral_palette.hue()) {
                        2.3
                    } else {
                        1.6
                    }
                }
                Variant::Vibrant => 1.29,
                _ => 1.0,
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn on_surface(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_surface",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.variant == Variant::Vibrant {
                    t_max_c(&s.neutral_palette, 0.0, 100.0, 1.1)
                } else {
                    // For all other variants, use initial tone from background
                    if s.is_dark {
                        90.0
                    } else {
                        10.0
                    }
                }
            }),
            false,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 2.2,
                        Variant::TonalSpot => 1.7,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                if s.is_dark {
                                    3.0
                                } else {
                                    2.3
                                }
                            } else {
                                1.6
                            }
                        }
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.is_dark && s.platform == Platform::Phone {
                    Some(get_curve(11.0))
                } else {
                    Some(get_curve(9.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_variant(&self) -> DynamicColor {
        self.surface_container_highest()
    }

    fn on_surface_variant(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_surface_variant",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 80.0 } else { 30.0 }),
            false,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 2.2,
                        Variant::TonalSpot => 1.7,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                if s.is_dark {
                                    3.0
                                } else {
                                    2.3
                                }
                            } else {
                                1.6
                            }
                        }
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    if s.is_dark {
                        Some(get_curve(6.0))
                    } else {
                        Some(get_curve(4.5))
                    }
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn inverse_surface(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "inverse_surface",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 98.0 } else { 4.0 }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn inverse_on_surface(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "inverse_on_surface",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 90.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.inverse_surface())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn outline(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "outline",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 60.0 } else { 50.0 }),
            false,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 2.2,
                        Variant::TonalSpot => 1.7,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                if s.is_dark {
                                    3.0
                                } else {
                                    2.3
                                }
                            } else {
                                1.6
                            }
                        }
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(3.0))
                } else {
                    Some(get_curve(4.5))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn outline_variant(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "outline_variant",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 30.0 } else { 80.0 }),
            false,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    match s.variant {
                        Variant::Neutral => 2.2,
                        Variant::TonalSpot => 1.7,
                        Variant::Expressive => {
                            if Hct::is_yellow(s.neutral_palette.hue()) {
                                if s.is_dark {
                                    3.0
                                } else {
                                    2.3
                                }
                            } else {
                                1.6
                            }
                        }
                        _ => 1.0,
                    }
                } else {
                    1.0
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(1.5))
                } else {
                    Some(get_curve(3.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn shadow(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "shadow",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|_s: &DynamicScheme| 0.0),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn scrim(&self) -> DynamicColor {
        DynamicColor::from_palette(
            "scrim",
            |s: &DynamicScheme| s.neutral_palette.clone(),
            Some(|_s: &DynamicScheme| 0.0),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn surface_tint(&self) -> DynamicColor {
        self.primary()
    }

    // ================================================================
    // Primaries [P]
    // ================================================================

    fn primary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "primary",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| match s.variant {
                Variant::Neutral => {
                    if s.platform == Platform::Phone {
                        if s.is_dark {
                            80.0
                        } else {
                            40.0
                        }
                    } else {
                        90.0
                    }
                }
                Variant::TonalSpot => {
                    if s.platform == Platform::Phone {
                        if s.is_dark {
                            80.0
                        } else {
                            t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)
                        }
                    } else {
                        t_max_c(&s.primary_palette, 0.0, 90.0, 1.0)
                    }
                }
                Variant::Expressive => {
                    if s.platform == Platform::Phone {
                        let upper = if Hct::is_yellow(s.primary_palette.hue()) {
                            25.0
                        } else if Hct::is_cyan(s.primary_palette.hue()) {
                            88.0
                        } else {
                            98.0
                        };
                        t_max_c(&s.primary_palette, 0.0, upper, 1.0)
                    } else {
                        t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)
                    }
                }
                Variant::Vibrant => {
                    if s.platform == Platform::Phone {
                        let upper = if Hct::is_cyan(s.primary_palette.hue()) {
                            88.0
                        } else {
                            98.0
                        };
                        t_max_c(&s.primary_palette, 0.0, upper, 1.0)
                    } else {
                        t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)
                    }
                }
                _ => {
                    if s.is_dark {
                        80.0
                    } else {
                        40.0
                    }
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(4.5))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.primary_container(),
                        ColorSpecDelegateImpl2025.primary(),
                        5.0,
                        TonePolarity::Lighter,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn primary_dim(&self) -> Option<DynamicColor> {
        let delegate_ref = ColorSpecDelegateImpl2025;
        Some(DynamicColor::from_palette(
            "primary_dim",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| match s.variant {
                Variant::Neutral => 85.0,
                Variant::TonalSpot => t_max_c(&s.primary_palette, 0.0, 90.0, 1.0),
                _ => t_max_c(&s.primary_palette, 0.0, 100.0, 1.0),
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.primary_dim().unwrap(),
                    ColorSpecDelegateImpl2025.primary(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Farther,
                ))
            }),
        ))
    }

    fn on_primary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_primary",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.primary())
                } else {
                    delegate_ref.primary_dim()
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn primary_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "primary_container",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    30.0
                } else {
                    match s.variant {
                        Variant::Neutral => {
                            if s.is_dark {
                                30.0
                            } else {
                                90.0
                            }
                        }
                        Variant::TonalSpot => {
                            if s.is_dark {
                                t_min_c(&s.primary_palette, 35.0, 93.0)
                            } else {
                                t_max_c(&s.primary_palette, 0.0, 90.0, 1.0)
                            }
                        }
                        Variant::Expressive => {
                            if s.is_dark {
                                t_max_c(&s.primary_palette, 30.0, 93.0, 1.0)
                            } else {
                                let upper = if Hct::is_cyan(s.primary_palette.hue()) {
                                    88.0
                                } else {
                                    90.0
                                };
                                t_max_c(&s.primary_palette, 78.0, upper, 1.0)
                            }
                        }
                        Variant::Vibrant => {
                            if s.is_dark {
                                t_min_c(&s.primary_palette, 66.0, 93.0)
                            } else {
                                let upper = if Hct::is_cyan(s.primary_palette.hue()) {
                                    88.0
                                } else {
                                    93.0
                                };
                                t_max_c(&s.primary_palette, 66.0, upper, 1.0)
                            }
                        }
                        _ => {
                            if s.is_dark {
                                30.0
                            } else {
                                90.0
                            }
                        }
                    }
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform != Platform::Phone {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.primary_container(),
                        ColorSpecDelegateImpl2025.primary_dim().unwrap(),
                        10.0,
                        TonePolarity::Darker,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn on_primary_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_primary_container",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.primary_container())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn inverse_primary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "inverse_primary",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| t_max_c(&s.primary_palette, 0.0, 100.0, 1.0)),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.inverse_surface())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Secondaries [Q]
    // ================================================================

    fn secondary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "secondary",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    if s.variant == Variant::Neutral {
                        90.0
                    } else {
                        t_max_c(&s.secondary_palette, 0.0, 90.0, 1.0)
                    }
                } else {
                    match s.variant {
                        Variant::Neutral => {
                            if s.is_dark {
                                t_min_c(&s.secondary_palette, 0.0, 98.0)
                            } else {
                                t_max_c(&s.secondary_palette, 0.0, 100.0, 1.0)
                            }
                        }
                        Variant::Vibrant => {
                            let upper = if s.is_dark { 90.0 } else { 98.0 };
                            t_max_c(&s.secondary_palette, 0.0, upper, 1.0)
                        }
                        _ => {
                            // EXPRESSIVE and TONAL_SPOT
                            if s.is_dark {
                                80.0
                            } else {
                                t_max_c(&s.secondary_palette, 0.0, 100.0, 1.0)
                            }
                        }
                    }
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(4.5))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.secondary_container(),
                        ColorSpecDelegateImpl2025.secondary(),
                        5.0,
                        TonePolarity::Lighter,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn secondary_dim(&self) -> Option<DynamicColor> {
        let delegate_ref = ColorSpecDelegateImpl2025;
        Some(DynamicColor::from_palette(
            "secondary_dim",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.variant == Variant::Neutral {
                    85.0
                } else {
                    t_max_c(&s.secondary_palette, 0.0, 90.0, 1.0)
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.secondary_dim().unwrap(),
                    ColorSpecDelegateImpl2025.secondary(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Farther,
                ))
            }),
        ))
    }

    fn on_secondary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_secondary",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.secondary())
                } else {
                    delegate_ref.secondary_dim()
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn secondary_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "secondary_container",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    30.0
                } else {
                    match s.variant {
                        Variant::Vibrant => {
                            if s.is_dark {
                                t_min_c(&s.secondary_palette, 30.0, 40.0)
                            } else {
                                t_max_c(&s.secondary_palette, 84.0, 90.0, 1.0)
                            }
                        }
                        Variant::Expressive => {
                            if s.is_dark {
                                15.0
                            } else {
                                t_max_c(&s.secondary_palette, 90.0, 95.0, 1.0)
                            }
                        }
                        _ => {
                            if s.is_dark {
                                25.0
                            } else {
                                90.0
                            }
                        }
                    }
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.secondary_container(),
                        ColorSpecDelegateImpl2025.secondary_dim().unwrap(),
                        10.0,
                        TonePolarity::Darker,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn on_secondary_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_secondary_container",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.secondary_container())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Tertiaries [T]
    // ================================================================

    fn tertiary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "tertiary",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    if s.variant == Variant::TonalSpot {
                        t_max_c(&s.tertiary_palette, 0.0, 90.0, 1.0)
                    } else {
                        t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
                    }
                } else {
                    match s.variant {
                        Variant::Expressive | Variant::Vibrant => {
                            let upper = if Hct::is_cyan(s.tertiary_palette.hue()) {
                                88.0
                            } else if s.is_dark {
                                98.0
                            } else {
                                100.0
                            };
                            t_max_c(&s.tertiary_palette, 0.0, upper, 1.0)
                        }
                        _ => {
                            // NEUTRAL and TONAL_SPOT
                            if s.is_dark {
                                t_max_c(&s.tertiary_palette, 0.0, 98.0, 1.0)
                            } else {
                                t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
                            }
                        }
                    }
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(4.5))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.tertiary_container(),
                        ColorSpecDelegateImpl2025.tertiary(),
                        5.0,
                        TonePolarity::Lighter,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn tertiary_dim(&self) -> Option<DynamicColor> {
        let delegate_ref = ColorSpecDelegateImpl2025;
        Some(DynamicColor::from_palette(
            "tertiary_dim",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.variant == Variant::TonalSpot {
                    t_max_c(&s.tertiary_palette, 0.0, 90.0, 1.0)
                } else {
                    t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.tertiary_dim().unwrap(),
                    ColorSpecDelegateImpl2025.tertiary(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Farther,
                ))
            }),
        ))
    }

    fn on_tertiary(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_tertiary",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.tertiary())
                } else {
                    delegate_ref.tertiary_dim()
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn tertiary_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "tertiary_container",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    if s.variant == Variant::TonalSpot {
                        t_max_c(&s.tertiary_palette, 0.0, 90.0, 1.0)
                    } else {
                        t_max_c(&s.tertiary_palette, 0.0, 100.0, 1.0)
                    }
                } else {
                    match s.variant {
                        Variant::Neutral => {
                            if s.is_dark {
                                t_max_c(&s.tertiary_palette, 0.0, 93.0, 1.0)
                            } else {
                                t_max_c(&s.tertiary_palette, 0.0, 96.0, 1.0)
                            }
                        }
                        Variant::TonalSpot => {
                            let upper = if s.is_dark { 93.0 } else { 100.0 };
                            t_max_c(&s.tertiary_palette, 0.0, upper, 1.0)
                        }
                        Variant::Expressive => {
                            let upper = if Hct::is_cyan(s.tertiary_palette.hue()) {
                                88.0
                            } else if s.is_dark {
                                93.0
                            } else {
                                100.0
                            };
                            t_max_c(&s.tertiary_palette, 75.0, upper, 1.0)
                        }
                        Variant::Vibrant => {
                            if s.is_dark {
                                t_max_c(&s.tertiary_palette, 0.0, 93.0, 1.0)
                            } else {
                                t_max_c(&s.tertiary_palette, 72.0, 100.0, 1.0)
                            }
                        }
                        _ => {
                            if s.is_dark {
                                30.0
                            } else {
                                90.0
                            }
                        }
                    }
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.tertiary_container(),
                        ColorSpecDelegateImpl2025.tertiary_dim().unwrap(),
                        10.0,
                        TonePolarity::Darker,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn on_tertiary_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_tertiary_container",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.tertiary_container())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Errors [E]
    // ================================================================

    fn error(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "error",
            |s: &DynamicScheme| s.error_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    if s.is_dark {
                        t_min_c(&s.error_palette, 0.0, 98.0)
                    } else {
                        t_max_c(&s.error_palette, 0.0, 100.0, 1.0)
                    }
                } else {
                    t_min_c(&s.error_palette, 0.0, 100.0)
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    Some(delegate_ref.surface_container_high())
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(4.5))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.error_container(),
                        ColorSpecDelegateImpl2025.error(),
                        5.0,
                        TonePolarity::Lighter,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn error_dim(&self) -> Option<DynamicColor> {
        let delegate_ref = ColorSpecDelegateImpl2025;
        Some(DynamicColor::from_palette(
            "error_dim",
            |s: &DynamicScheme| s.error_palette.clone(),
            Some(|s: &DynamicScheme| t_min_c(&s.error_palette, 0.0, 100.0)),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.surface_container_high())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.error_dim().unwrap(),
                    ColorSpecDelegateImpl2025.error(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Farther,
                ))
            }),
        ))
    }

    fn on_error(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_error",
            |s: &DynamicScheme| s.error_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 100.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.error())
                } else {
                    delegate_ref.error_dim()
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(6.0))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn error_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "error_container",
            |s: &DynamicScheme| s.error_palette.clone(),
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    30.0
                } else if s.is_dark {
                    t_min_c(&s.error_palette, 30.0, 93.0)
                } else {
                    t_max_c(&s.error_palette, 0.0, 90.0, 1.0)
                }
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Watch {
                    Some(ToneDeltaPair::new(
                        ColorSpecDelegateImpl2025.error_container(),
                        ColorSpecDelegateImpl2025.error_dim().unwrap(),
                        10.0,
                        TonePolarity::Darker,
                        true,
                        DeltaConstraint::Farther,
                    ))
                } else {
                    None
                }
            }),
        )
    }

    fn on_error_container(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_error_container",
            |s: &DynamicScheme| s.error_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 90.0 } else { 10.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.error_container())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(get_curve(4.5))
                } else {
                    Some(get_curve(7.0))
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Primary Fixed Colors [PF]
    // ================================================================

    fn primary_fixed(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "primary_fixed",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(move |s: &DynamicScheme| {
                // Get the container tone in light mode with zero contrast
                let light_scheme = DynamicScheme {
                    is_dark: false,
                    contrast_level: 0.0,
                    ..s.clone()
                };
                delegate_ref.primary_container().tone.as_ref()(&light_scheme)
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn primary_fixed_dim(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "primary_fixed_dim",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(move |s: &DynamicScheme| delegate_ref.primary_fixed().tone.as_ref()(s)),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.primary_fixed_dim(),
                    ColorSpecDelegateImpl2025.primary_fixed(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Exact,
                ))
            }),
        )
    }

    fn on_primary_fixed(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_primary_fixed",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 10.0 } else { 10.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.primary_fixed_dim())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn on_primary_fixed_variant(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_primary_fixed_variant",
            |s: &DynamicScheme| s.primary_palette.clone(),
            Some(|s: &DynamicScheme| if s.is_dark { 30.0 } else { 30.0 }),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.primary_fixed_dim())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Secondary Fixed Colors [QF]
    // ================================================================

    fn secondary_fixed(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "secondary_fixed",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(move |s: &DynamicScheme| {
                let light_scheme = DynamicScheme {
                    is_dark: false,
                    contrast_level: 0.0,
                    ..s.clone()
                };
                delegate_ref.secondary_container().tone.as_ref()(&light_scheme)
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn secondary_fixed_dim(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "secondary_fixed_dim",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(move |s: &DynamicScheme| delegate_ref.secondary_fixed().tone.as_ref()(s)),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.secondary_fixed_dim(),
                    ColorSpecDelegateImpl2025.secondary_fixed(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Exact,
                ))
            }),
        )
    }

    fn on_secondary_fixed(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_secondary_fixed",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|_s: &DynamicScheme| 10.0),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.secondary_fixed_dim())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn on_secondary_fixed_variant(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_secondary_fixed_variant",
            |s: &DynamicScheme| s.secondary_palette.clone(),
            Some(|_s: &DynamicScheme| 30.0),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.secondary_fixed_dim())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    // ================================================================
    // Tertiary Fixed Colors [TF]
    // ================================================================

    fn tertiary_fixed(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "tertiary_fixed",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(move |s: &DynamicScheme| {
                let light_scheme = DynamicScheme {
                    is_dark: false,
                    contrast_level: 0.0,
                    ..s.clone()
                };
                delegate_ref.tertiary_container().tone.as_ref()(&light_scheme)
            }),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |s: &DynamicScheme| {
                if s.platform == Platform::Phone {
                    Some(delegate_ref.highest_surface(s))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|s: &DynamicScheme| {
                if s.platform == Platform::Phone && s.contrast_level > 0.0 {
                    Some(get_curve(1.5))
                } else {
                    None
                }
            }),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn tertiary_fixed_dim(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "tertiary_fixed_dim",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(move |s: &DynamicScheme| delegate_ref.tertiary_fixed().tone.as_ref()(s)),
            true,
            None::<fn(&DynamicScheme) -> f64>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            None::<fn(&DynamicScheme) -> Option<ContrastCurve>>,
            Some(move |_s: &DynamicScheme| {
                Some(ToneDeltaPair::new(
                    ColorSpecDelegateImpl2025.tertiary_fixed_dim(),
                    ColorSpecDelegateImpl2025.tertiary_fixed(),
                    5.0,
                    TonePolarity::Darker,
                    true,
                    DeltaConstraint::Exact,
                ))
            }),
        )
    }

    fn on_tertiary_fixed(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_tertiary_fixed",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|_s: &DynamicScheme| 10.0),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.tertiary_fixed_dim())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(7.0))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }

    fn on_tertiary_fixed_variant(&self) -> DynamicColor {
        let delegate_ref = ColorSpecDelegateImpl2025;
        DynamicColor::from_palette(
            "on_tertiary_fixed_variant",
            |s: &DynamicScheme| s.tertiary_palette.clone(),
            Some(|_s: &DynamicScheme| 30.0),
            false,
            None::<fn(&DynamicScheme) -> f64>,
            Some(move |_s: &DynamicScheme| Some(delegate_ref.tertiary_fixed_dim())),
            None::<fn(&DynamicScheme) -> Option<DynamicColor>>,
            Some(|_s: &DynamicScheme| Some(get_curve(4.5))),
            None::<fn(&DynamicScheme) -> Option<ToneDeltaPair>>,
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mcu_hct::Hct;
    use mcu_palettes::TonalPalette;

    fn create_test_scheme(is_dark: bool, variant: Variant) -> DynamicScheme {
        let source = Hct::from(200.0, 40.0, 50.0);
        DynamicScheme {
            source_color_hct: source,
            source_color_argb: source.to_int(),
            variant,
            contrast_level: 0.0,
            is_dark,
            platform: Platform::Phone,
            spec_version: crate::SpecVersion::Spec2025,
            primary_palette: TonalPalette::from_hue_and_chroma(200.0, 40.0),
            secondary_palette: TonalPalette::from_hue_and_chroma(220.0, 30.0),
            tertiary_palette: TonalPalette::from_hue_and_chroma(180.0, 35.0),
            neutral_palette: TonalPalette::from_hue_and_chroma(200.0, 8.0),
            neutral_variant_palette: TonalPalette::from_hue_and_chroma(200.0, 12.0),
            error_palette: TonalPalette::from_hue_and_chroma(25.0, 84.0),
        }
    }

    #[test]
    fn test_all_methods_implemented() {
        let delegate = ColorSpecDelegateImpl2025;
        let scheme = create_test_scheme(false, Variant::TonalSpot);

        // Test that all required methods return values
        assert!(delegate.primary().get_argb(&scheme) != 0);
        assert!(delegate.secondary().get_argb(&scheme) != 0);
        assert!(delegate.tertiary().get_argb(&scheme) != 0);
        assert!(delegate.error().get_argb(&scheme) != 0);
        assert!(delegate.surface().get_argb(&scheme) != 0);
    }

    #[test]
    fn test_dim_variants_return_some() {
        let delegate = ColorSpecDelegateImpl2025;

        // Verify dim variants return Some
        assert!(delegate.primary_dim().is_some());
        assert!(delegate.secondary_dim().is_some());
        assert!(delegate.tertiary_dim().is_some());
        assert!(delegate.error_dim().is_some());
    }

    #[test]
    fn test_key_colors_resolve_correctly() {
        let delegate = ColorSpecDelegateImpl2025;
        let light_scheme = create_test_scheme(false, Variant::TonalSpot);
        let dark_scheme = create_test_scheme(true, Variant::TonalSpot);

        // Test light mode
        let primary_light = delegate.primary().get_argb(&light_scheme);
        let surface_light = delegate.surface().get_argb(&light_scheme);
        assert_ne!(primary_light, 0);
        assert_ne!(surface_light, 0);
        assert_ne!(primary_light, surface_light);

        // Test dark mode
        let primary_dark = delegate.primary().get_argb(&dark_scheme);
        let surface_dark = delegate.surface().get_argb(&dark_scheme);
        assert_ne!(primary_dark, 0);
        assert_ne!(surface_dark, 0);
        assert_ne!(primary_dark, surface_dark);

        // Primary should differ between light and dark
        assert_ne!(primary_light, primary_dark);
    }

    #[test]
    fn test_surface_colors_differ_by_elevation() {
        let delegate = ColorSpecDelegateImpl2025;
        let scheme = create_test_scheme(false, Variant::TonalSpot);

        let container_lowest = delegate.surface_container_lowest().get_argb(&scheme);
        let container_low = delegate.surface_container_low().get_argb(&scheme);
        let container = delegate.surface_container().get_argb(&scheme);
        let container_high = delegate.surface_container_high().get_argb(&scheme);
        let container_highest = delegate.surface_container_highest().get_argb(&scheme);

        // All should be different
        assert_ne!(container_lowest, container_low);
        assert_ne!(container_low, container);
        assert_ne!(container, container_high);
        assert_ne!(container_high, container_highest);
    }

    #[test]
    fn test_variants_produce_different_results() {
        let delegate = ColorSpecDelegateImpl2025;

        let neutral = create_test_scheme(false, Variant::Neutral);
        let tonal_spot = create_test_scheme(false, Variant::TonalSpot);
        let vibrant = create_test_scheme(false, Variant::Vibrant);
        let expressive = create_test_scheme(false, Variant::Expressive);

        let primary_neutral = delegate.primary().get_argb(&neutral);
        let primary_tonal_spot = delegate.primary().get_argb(&tonal_spot);
        let primary_vibrant = delegate.primary().get_argb(&vibrant);
        let primary_expressive = delegate.primary().get_argb(&expressive);

        // Different variants may produce different colors depending on source color
        // At minimum, neutral should differ from other more saturated variants
        assert_ne!(primary_neutral, primary_tonal_spot);
        // Note: vibrant and expressive may produce same primary for some source colors
        // since they use similar tone mappings, so we only check that they were computed
        assert!(primary_vibrant != 0);
        assert!(primary_expressive != 0);
    }
}

// <FILE>crates/mcu-dynamiccolor/src/color_spec_2025.rs</FILE> - <DESC>2025 Material Design 3 color spec delegate implementation</DESC>
// <VERS>END OF VERSION: 1.2.0</VERS>
