Fry the onions in hot oil (350°F / 175°C) in batches for 15-20 minutes until deep golden brown and crispy. Drain on paper towels and reserve ⅓ for the marinade, keeping the rest for layering.
Combine yogurt, biryani masala, ginger-garlic paste, red chili powder, turmeric, garam masala, salt, lemon juice, 2 tbsp oil, and ⅓ of the fried onions in a large bowl. Mix thoroughly.
Add chicken pieces to the marinade and coat well, ensuring every piece is covered. Cover and refrigerate for a minimum of 2 hours, preferably overnight.
Rinse basmati rice 3-4 times until water runs clear, then soak in cold water for 30 minutes. Drain completely through a fine mesh strainer.
Steep saffron threads in 4 tablespoons of warm whole milk for at least 15 minutes until the milk turns deep golden-orange.
Bring 8-10 cups of water to a rolling boil with 1 tablespoon salt. Add whole spices, then gently add drained rice. Cook for exactly 5-6 minutes until 70% done (white center visible in each grain). Drain immediately and spread on a tray.
Heat 1 tablespoon ghee in a heavy-bottomed pot. Add marinated chicken in a single layer and cook undisturbed for 4-5 minutes. Stir and cook for 3-4 minutes more.
Reduce heat to medium-low, cover, and cook chicken for 10-12 minutes until 80% done and marinade has thickened into a glossy sauce. Spread chicken evenly across the bottom of the pot.
Drizzle 1 tablespoon ghee over the chicken. Spread half the par-cooked rice evenly over the chicken. Scatter half the remaining fried onions, mint, and cilantro over the rice. Drizzle half the saffron milk in streaks.
Add the remaining rice as the final layer. Top with remaining fried onions, mint, cilantro, and saffron milk. Add rose water (if using), remaining 1 tablespoon ghee, and 2 tablespoons warm water around the edges of the pot.
Cover the pot with foil, pressing tightly against the rim, then place the lid firmly on top. Cook over high heat for 3-4 minutes until steam builds, then reduce to the lowest heat and cook for 20-25 minutes.
Turn off the heat and let the biryani rest, still sealed, for an additional 10 minutes. Carefully remove the foil and lid, then gently mix the layers from the sides down. Serve hot.