float3 applyWhiteBalanceOnly(float3 color)
{
    float warmAmount = clamp(warmCool, -1.0, 1.0);
    float tintAmount = clamp(greenMagenta, -1.0, 1.0);

    // Warm/cool pivots red against blue while keeping green more stable.
    float3 warmCoolGain = float3(
        exp2(warmAmount * 0.35),
        exp2(-abs(warmAmount) * 0.08),
        exp2(-warmAmount * 0.35));

    // Green/magenta pivots green against the average of red and blue.
    float3 tintGain = float3(
        exp2(-tintAmount * 0.22),
        exp2(tintAmount * 0.35),
        exp2(-tintAmount * 0.22));

    return color * warmCoolGain * tintGain;
}

float3 applyExposureLikeBlender(float3 color)
{
    // Match the compositor-style exposure model: every +1.0 stop doubles the
    // image and every -1.0 stop halves it.
    return color * exp2(exposure);
}

float4 shadeVideo(ShaderContext context)
{
    float4 source = context.sourceColor;
    float3 balanced = applyWhiteBalanceOnly(source.rgb);
    float3 corrected = applyExposureLikeBlender(balanced);
    source.rgb = lerp(source.rgb, corrected, saturate(strength));
    return source;
}
