diff --git a/graph.json b/graph.json index cbbe116..9579258 100644 --- a/graph.json +++ b/graph.json @@ -1,9 +1,9 @@ { "meta": { "version": "1.0.0", - "generated_at": "2026-06-12T14:13:56.375101+00:00", + "generated_at": "2026-06-15T03:40:23.585989+00:00", "entity_count": 668, - "relationship_count": 748, + "relationship_count": 848, "canvas_width": 1800, "canvas_height": 1200, "year_range": [ @@ -392,7 +392,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -531,7 +531,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Consolidated_Fund_India", @@ -665,7 +665,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Ministry_Budget", @@ -821,7 +821,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -974,7 +974,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -1130,7 +1130,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -1283,7 +1283,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -1430,7 +1430,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -1571,7 +1571,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -1720,7 +1720,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -1874,7 +1874,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -2026,7 +2026,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -2174,7 +2174,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -2332,7 +2332,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -2652,7 +2652,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -2776,7 +2776,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "State_Consolidated_Fund", @@ -2942,7 +2942,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -3079,7 +3079,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -3173,7 +3173,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -3541,7 +3541,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Ministry_Budget", @@ -3768,7 +3768,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Consolidated_Fund_India", @@ -3898,7 +3898,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -4044,7 +4044,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -4200,7 +4200,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -4357,7 +4357,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -4520,7 +4520,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -4684,7 +4684,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -4841,7 +4841,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -5011,7 +5011,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -5181,7 +5181,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -5347,7 +5347,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -5498,7 +5498,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -5648,7 +5648,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -5787,7 +5787,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -5922,7 +5922,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -6052,7 +6052,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -6187,7 +6187,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -6390,7 +6390,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -6664,7 +6664,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -6753,7 +6753,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.45 }, "funding_source": null, @@ -6967,7 +6967,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -7067,7 +7067,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.68 }, "funding_source": "Consolidated_Fund_India", @@ -7204,7 +7204,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -7464,7 +7464,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Mixed", @@ -7598,7 +7598,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -7686,7 +7686,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -7805,7 +7805,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -7951,7 +7951,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -8084,7 +8084,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -8221,7 +8221,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -8358,7 +8358,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -8503,7 +8503,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -8623,7 +8623,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.68 }, "funding_source": "Ministry_Budget", @@ -8749,7 +8749,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -8910,7 +8910,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9062,7 +9062,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9208,7 +9208,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9357,7 +9357,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9521,7 +9521,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9694,7 +9694,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9842,7 +9842,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -9987,7 +9987,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -10138,7 +10138,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -10297,7 +10297,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -10450,7 +10450,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -10606,7 +10606,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -10773,7 +10773,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -10944,7 +10944,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11098,7 +11098,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11248,7 +11248,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11388,7 +11388,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11528,7 +11528,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11680,7 +11680,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11828,7 +11828,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -11968,7 +11968,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -12113,7 +12113,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -12258,7 +12258,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -12398,7 +12398,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -12546,7 +12546,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -12682,7 +12682,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -12783,7 +12783,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -12892,7 +12892,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -13148,7 +13148,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.44 }, "funding_source": "State_Consolidated_Fund", @@ -13282,7 +13282,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Consolidated_Fund_India", @@ -13412,7 +13412,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -13862,7 +13862,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -13949,7 +13949,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -14046,7 +14046,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -14184,7 +14184,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Statutory_Fund", @@ -14312,7 +14312,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -14398,7 +14398,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Mixed", @@ -14666,7 +14666,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Statutory_Fund", @@ -14804,7 +14804,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -14945,7 +14945,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15073,7 +15073,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15201,7 +15201,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15329,7 +15329,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15457,7 +15457,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15585,7 +15585,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15716,7 +15716,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15847,7 +15847,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -15975,7 +15975,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16103,7 +16103,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16234,7 +16234,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16365,7 +16365,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16493,7 +16493,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16621,7 +16621,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16749,7 +16749,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -16877,7 +16877,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17005,7 +17005,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17133,7 +17133,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17261,7 +17261,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17389,7 +17389,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17517,7 +17517,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17645,7 +17645,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17773,7 +17773,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -17903,7 +17903,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -18033,7 +18033,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -18285,7 +18285,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -18561,7 +18561,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Mixed", @@ -18683,7 +18683,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Party_Fees", @@ -18818,7 +18818,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -20272,7 +20272,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -20395,7 +20395,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -20578,7 +20578,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -20719,7 +20719,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Ministry_Budget", @@ -20871,7 +20871,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -21013,7 +21013,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -21146,7 +21146,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Consolidated_Fund_India", @@ -21274,7 +21274,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -21375,7 +21375,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -21559,7 +21559,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -21700,7 +21700,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Ministry_Budget", @@ -21913,7 +21913,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Consolidated_Fund_India", @@ -22136,7 +22136,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Ministry_Budget", @@ -22269,7 +22269,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -22357,7 +22357,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -22447,7 +22447,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Consolidated_Fund_India", @@ -22588,7 +22588,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Statutory_Fund", @@ -22724,7 +22724,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -22824,7 +22824,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -22912,7 +22912,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": null, @@ -22995,7 +22995,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -23165,7 +23165,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -23287,7 +23287,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "State_Consolidated_Fund", @@ -23427,7 +23427,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "State_Consolidated_Fund", @@ -23542,7 +23542,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "State_Consolidated_Fund", @@ -23696,7 +23696,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "State_Consolidated_Fund", @@ -23787,7 +23787,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "State_Consolidated_Fund", @@ -23897,7 +23897,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Consolidated_Fund_India", @@ -23988,7 +23988,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -24071,7 +24071,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "State_Consolidated_Fund", @@ -24188,7 +24188,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "State_Consolidated_Fund", @@ -24352,7 +24352,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": null, @@ -24441,7 +24441,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.61 }, "funding_source": "Ministry_Budget", @@ -24581,7 +24581,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.52 }, "funding_source": "Consolidated_Fund_India", @@ -24709,7 +24709,7 @@ "independence_risk_penalty": 0.55, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.53 }, "funding_source": "Ministry_Budget", @@ -27519,7 +27519,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -27698,7 +27698,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -27881,7 +27881,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -28064,7 +28064,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -28245,7 +28245,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -28422,7 +28422,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -28600,7 +28600,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -28781,7 +28781,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -28962,7 +28962,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -29140,7 +29140,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -29313,7 +29313,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -29498,7 +29498,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -29674,7 +29674,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -29847,7 +29847,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -30041,7 +30041,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -30237,7 +30237,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -30435,7 +30435,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -30629,7 +30629,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -30821,7 +30821,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -31016,7 +31016,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -31217,7 +31217,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -31409,7 +31409,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -31603,7 +31603,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -31797,7 +31797,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -31989,7 +31989,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -32183,7 +32183,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -32377,7 +32377,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -32571,7 +32571,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -32777,7 +32777,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -32975,7 +32975,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -33173,7 +33173,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -33365,7 +33365,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -33560,7 +33560,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -33755,7 +33755,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -33947,7 +33947,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -34145,7 +34145,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -34337,7 +34337,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -34535,7 +34535,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.6 }, "funding_source": "State_Consolidated_Fund", @@ -34715,7 +34715,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -34816,7 +34816,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -34917,7 +34917,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35018,7 +35018,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35119,7 +35119,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35220,7 +35220,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35321,7 +35321,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35422,7 +35422,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35523,7 +35523,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35624,7 +35624,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35725,7 +35725,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35826,7 +35826,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -35927,7 +35927,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36028,7 +36028,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36129,7 +36129,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36230,7 +36230,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36331,7 +36331,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36432,7 +36432,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36533,7 +36533,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36634,7 +36634,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36735,7 +36735,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36836,7 +36836,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -36937,7 +36937,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37038,7 +37038,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37139,7 +37139,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37240,7 +37240,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37341,7 +37341,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37442,7 +37442,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37543,7 +37543,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37644,7 +37644,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37745,7 +37745,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37846,7 +37846,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -37947,7 +37947,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38048,7 +38048,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38149,7 +38149,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38250,7 +38250,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38351,7 +38351,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38452,7 +38452,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38553,7 +38553,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38654,7 +38654,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38755,7 +38755,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38856,7 +38856,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -38957,7 +38957,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39058,7 +39058,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39159,7 +39159,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39260,7 +39260,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39361,7 +39361,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39462,7 +39462,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39563,7 +39563,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39664,7 +39664,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39765,7 +39765,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39866,7 +39866,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -39967,7 +39967,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40068,7 +40068,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40169,7 +40169,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40270,7 +40270,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40371,7 +40371,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40472,7 +40472,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40573,7 +40573,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40674,7 +40674,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -40775,7 +40775,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -41093,7 +41093,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -41190,7 +41190,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -41617,7 +41617,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -41714,7 +41714,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -42031,7 +42031,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -42128,7 +42128,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -42555,7 +42555,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -42652,7 +42652,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -43079,7 +43079,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -43176,7 +43176,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -43493,7 +43493,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -43590,7 +43590,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -43907,7 +43907,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44004,7 +44004,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44106,7 +44106,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44334,7 +44334,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44428,7 +44428,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44522,7 +44522,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44616,7 +44616,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44710,7 +44710,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -44804,7 +44804,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -45968,7 +45968,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -46074,7 +46074,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -46177,7 +46177,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -46404,7 +46404,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -46734,7 +46734,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -46831,7 +46831,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -47258,7 +47258,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -47355,7 +47355,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -47672,7 +47672,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -47769,7 +47769,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -48086,7 +48086,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -48183,7 +48183,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -48500,7 +48500,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -48597,7 +48597,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -48914,7 +48914,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -49011,7 +49011,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -49328,7 +49328,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -49434,7 +49434,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -49528,7 +49528,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -49622,7 +49622,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -53332,7 +53332,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -53446,7 +53446,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -53549,7 +53549,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -53776,7 +53776,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -53891,7 +53891,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -54239,7 +54239,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -54336,7 +54336,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -54653,7 +54653,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -54750,7 +54750,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -55067,7 +55067,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -55164,7 +55164,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -55393,7 +55393,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -55618,7 +55618,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Party_Fees", @@ -55718,7 +55718,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -55812,7 +55812,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -55918,7 +55918,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -56024,7 +56024,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -60568,7 +60568,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -60682,7 +60682,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -60785,7 +60785,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -61012,7 +61012,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -61342,7 +61342,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -61439,7 +61439,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -61756,7 +61756,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -61853,7 +61853,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -62280,7 +62280,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -62377,7 +62377,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -62694,7 +62694,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -62791,7 +62791,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -63108,7 +63108,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -63205,7 +63205,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -63522,7 +63522,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -63619,7 +63619,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -63936,7 +63936,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -64033,7 +64033,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -64224,7 +64224,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -64640,7 +64640,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -64833,7 +64833,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -65860,7 +65860,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -65954,7 +65954,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -66048,7 +66048,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -66142,7 +66142,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -66239,7 +66239,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -66556,7 +66556,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -66653,7 +66653,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -66847,7 +66847,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Party_Fees", @@ -66947,7 +66947,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -71469,7 +71469,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -71575,7 +71575,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -71683,7 +71683,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Mixed", @@ -71932,7 +71932,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -72041,7 +72041,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -72158,7 +72158,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -72475,7 +72475,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -72572,7 +72572,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -72999,7 +72999,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -73096,7 +73096,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -73413,7 +73413,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -73510,7 +73510,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -73695,7 +73695,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -74928,7 +74928,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -75022,7 +75022,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -75116,7 +75116,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -75210,7 +75210,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -75307,7 +75307,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -75492,7 +75492,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -76519,7 +76519,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -76613,7 +76613,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -76707,7 +76707,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -76801,7 +76801,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "State_Consolidated_Fund", @@ -77056,7 +77056,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.54 }, "funding_source": "Consolidated_Fund_India", @@ -77247,7 +77247,7 @@ "independence_risk_penalty": 0.3, "operational_status_modifier": 1.0 }, - "structural_health_level": "moderate", + "structural_health_level": "watch", "structural_health_score": 0.76 }, "funding_source": "Ministry_Budget", @@ -77417,7 +77417,7 @@ "independence_risk_penalty": 0.7, "operational_status_modifier": 1.0 }, - "structural_health_level": "concerning", + "structural_health_level": "at_risk", "structural_health_score": 0.44 }, "funding_source": "Ministry_Budget", @@ -94257,6 +94257,1506 @@ "accessed_date": "2026-06-12" } ] + }, + { + "id": "inferred_appellate_an_district_courts_generic", + "source": "an_district_courts_generic", + "target": "hc_calcutta", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ap_district_courts_generic", + "source": "ap_district_courts_generic", + "target": "hc_andhra_pradesh", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ar_district_courts_generic", + "source": "ar_district_courts_generic", + "target": "hc_gauhati", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_as_district_courts_generic", + "source": "as_district_courts_generic", + "target": "hc_gauhati", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_br_district_courts_generic", + "source": "br_district_courts_generic", + "target": "hc_patna", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_cg_district_courts_generic", + "source": "cg_district_courts_generic", + "target": "hc_chhattisgarh", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ch_district_courts_generic", + "source": "ch_district_courts_generic", + "target": "hc_punjab_haryana", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_district_court_dwarka", + "source": "dl_district_court_dwarka", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_district_court_karkardooma", + "source": "dl_district_court_karkardooma", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_district_court_patiala_house", + "source": "dl_district_court_patiala_house", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_district_court_rohini", + "source": "dl_district_court_rohini", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_district_court_tis_hazari", + "source": "dl_district_court_tis_hazari", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_district_courts_generic", + "source": "dl_district_courts_generic", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_dl_special_courts", + "source": "dl_special_courts", + "target": "hc_delhi", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ga_district_courts_generic", + "source": "ga_district_courts_generic", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_gj_district_courts_generic", + "source": "gj_district_courts_generic", + "target": "hc_gujarat", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_hp_district_courts_generic", + "source": "hp_district_courts_generic", + "target": "hc_himachal_pradesh", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_hr_district_courts_generic", + "source": "hr_district_courts_generic", + "target": "hc_punjab_haryana", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_jh_district_courts_generic", + "source": "jh_district_courts_generic", + "target": "hc_jharkhand", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_jk_district_courts_generic", + "source": "jk_district_courts_generic", + "target": "hc_jammu_kashmir_ladakh", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_bagalkot", + "source": "ka_district_court_bagalkot", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_ballari", + "source": "ka_district_court_ballari", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_bengaluru_rural", + "source": "ka_district_court_bengaluru_rural", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_bidar", + "source": "ka_district_court_bidar", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_chamarajanagar", + "source": "ka_district_court_chamarajanagar", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_chikkaballapur", + "source": "ka_district_court_chikkaballapur", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_chikkamagaluru", + "source": "ka_district_court_chikkamagaluru", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_chitradurga", + "source": "ka_district_court_chitradurga", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_davangere", + "source": "ka_district_court_davangere", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_gadag", + "source": "ka_district_court_gadag", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_hassan", + "source": "ka_district_court_hassan", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_haveri", + "source": "ka_district_court_haveri", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_kalaburagi", + "source": "ka_district_court_kalaburagi", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_kodagu", + "source": "ka_district_court_kodagu", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_kolar", + "source": "ka_district_court_kolar", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_mandya", + "source": "ka_district_court_mandya", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_mangaluru", + "source": "ka_district_court_mangaluru", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_mysuru", + "source": "ka_district_court_mysuru", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_raichur", + "source": "ka_district_court_raichur", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_ramanagara", + "source": "ka_district_court_ramanagara", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_shivamogga", + "source": "ka_district_court_shivamogga", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_tumkuru", + "source": "ka_district_court_tumkuru", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_udupi", + "source": "ka_district_court_udupi", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_uttara_kannada", + "source": "ka_district_court_uttara_kannada", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_vijayapura", + "source": "ka_district_court_vijayapura", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_court_yadgir", + "source": "ka_district_court_yadgir", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_district_courts_generic", + "source": "ka_district_courts_generic", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ka_special_courts", + "source": "ka_special_courts", + "target": "hc_karnataka", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_kl_district_courts_generic", + "source": "kl_district_courts_generic", + "target": "hc_kerala", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_la_district_courts_generic", + "source": "la_district_courts_generic", + "target": "hc_jammu_kashmir_ladakh", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ld_district_courts_generic", + "source": "ld_district_courts_generic", + "target": "hc_kerala", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_ahmednagar", + "source": "mh_district_court_ahmednagar", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_akola", + "source": "mh_district_court_akola", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_beed", + "source": "mh_district_court_beed", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_bhandara", + "source": "mh_district_court_bhandara", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_buldhana", + "source": "mh_district_court_buldhana", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_chandrapur", + "source": "mh_district_court_chandrapur", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_dhule", + "source": "mh_district_court_dhule", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_gadchiroli", + "source": "mh_district_court_gadchiroli", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_gondia", + "source": "mh_district_court_gondia", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_hingoli", + "source": "mh_district_court_hingoli", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_jalgaon", + "source": "mh_district_court_jalgaon", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_jalna", + "source": "mh_district_court_jalna", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_kolhapur", + "source": "mh_district_court_kolhapur", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_latur", + "source": "mh_district_court_latur", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_mumbai_suburban", + "source": "mh_district_court_mumbai_suburban", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_nandurbar", + "source": "mh_district_court_nandurbar", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_osmanabad", + "source": "mh_district_court_osmanabad", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_palghar", + "source": "mh_district_court_palghar", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_parbhani", + "source": "mh_district_court_parbhani", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_pune", + "source": "mh_district_court_pune", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_raigad", + "source": "mh_district_court_raigad", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_ratnagiri", + "source": "mh_district_court_ratnagiri", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_sangli", + "source": "mh_district_court_sangli", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_satara", + "source": "mh_district_court_satara", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_sindhudurg", + "source": "mh_district_court_sindhudurg", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_thane", + "source": "mh_district_court_thane", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_wardha", + "source": "mh_district_court_wardha", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_washim", + "source": "mh_district_court_washim", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_court_yavatmal", + "source": "mh_district_court_yavatmal", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_district_courts_generic", + "source": "mh_district_courts_generic", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mh_special_courts", + "source": "mh_special_courts", + "target": "hc_bombay", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ml_district_courts_generic", + "source": "ml_district_courts_generic", + "target": "hc_meghalaya", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mn_district_courts_generic", + "source": "mn_district_courts_generic", + "target": "hc_manipur", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mp_district_courts_generic", + "source": "mp_district_courts_generic", + "target": "hc_madhya_pradesh", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_mz_district_courts_generic", + "source": "mz_district_courts_generic", + "target": "hc_gauhati", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_nl_district_courts_generic", + "source": "nl_district_courts_generic", + "target": "hc_gauhati", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_od_district_courts_generic", + "source": "od_district_courts_generic", + "target": "hc_orissa", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_pb_district_courts_generic", + "source": "pb_district_courts_generic", + "target": "hc_punjab_haryana", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_rj_district_courts_generic", + "source": "rj_district_courts_generic", + "target": "hc_rajasthan", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_rj_special_courts", + "source": "rj_special_courts", + "target": "hc_rajasthan", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_sk_district_courts_generic", + "source": "sk_district_courts_generic", + "target": "hc_sikkim", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_tn_special_courts", + "source": "tn_special_courts", + "target": "hc_madras", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_tr_district_courts_generic", + "source": "tr_district_courts_generic", + "target": "hc_tripura", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_ts_district_courts_generic", + "source": "ts_district_courts_generic", + "target": "hc_telangana", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_uk_district_courts_generic", + "source": "uk_district_courts_generic", + "target": "hc_uttarakhand", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_up_district_courts_generic", + "source": "up_district_courts_generic", + "target": "hc_allahabad", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_up_special_courts", + "source": "up_special_courts", + "target": "hc_allahabad", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_wb_district_courts_generic", + "source": "wb_district_courts_generic", + "target": "hc_calcutta", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] + }, + { + "id": "inferred_appellate_wb_special_courts", + "source": "wb_special_courts", + "target": "hc_calcutta", + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": true, + "is_constitutional": false, + "year_established": null, + "year_abolished": null, + "data_quality": "inferred", + "contested_note": null, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [] } ], "browse_index": { diff --git a/jem/scripts/build.py b/jem/scripts/build.py index 3d2e173..064a4af 100644 --- a/jem/scripts/build.py +++ b/jem/scripts/build.py @@ -558,6 +558,74 @@ def build_graph_json(data_dir: Path, output_path: Path, no_derive: bool = False) } frontend_relationships.append(fr) + # ── Step 10b: Backfill missing District Court β†’ High Court edges ───────── + # 184 subordinate-court entities exist; ~half lack an explicit AppealableTo + # relationship in the YAML. Infer the parent HC from the state-code prefix + # in the entity id so the graph is structurally complete. These are tagged + # data_quality: "inferred" so they're visually distinct from curated edges. + STATE_CODE_TO_HC = { + "an": "hc_calcutta", "ap": "hc_andhra_pradesh", "ar": "hc_gauhati", + "as": "hc_gauhati", "br": "hc_patna", "ch": "hc_punjab_haryana", + "cg": "hc_chhattisgarh", "dd": "hc_bombay", "dl": "hc_delhi", + "ga": "hc_bombay", "gj": "hc_gujarat", "hr": "hc_punjab_haryana", + "hp": "hc_himachal_pradesh", "jk": "hc_jammu_kashmir_ladakh", + "jh": "hc_jharkhand", "ka": "hc_karnataka", "kl": "hc_kerala", + "la": "hc_jammu_kashmir_ladakh", "ld": "hc_kerala", + "mp": "hc_madhya_pradesh", "mh": "hc_bombay", "mn": "hc_manipur", + "ml": "hc_meghalaya", "mz": "hc_gauhati", "nl": "hc_gauhati", + "or": "hc_orissa", "od": "hc_orissa", "py": "hc_madras", + "pb": "hc_punjab_haryana", "rj": "hc_rajasthan", "sk": "hc_sikkim", + "tn": "hc_madras", "tg": "hc_telangana", "ts": "hc_telangana", + "tr": "hc_tripura", + "up": "hc_allahabad", "uk": "hc_uttarakhand", "wb": "hc_calcutta", + } + SUBORDINATE_TYPES = { + "SubordinateCivilCourt", "SubordinateCriminalCourt", "CityCivilCourt", + "SpecialCourt", "FastTrackCourt", "FamilyCourt", "CommercialCourt", + "RevenueCourt", + } + entity_ids = {e["id"] for e in frontend_entities} + has_appellate_parent = set() + for r in frontend_relationships: + if r.get("relationship_category") == "appellate_chain": + has_appellate_parent.add(r["source"]) + + inferred = 0 + for e in frontend_entities: + if e.get("type") not in SUBORDINATE_TYPES: + continue + eid = e["id"] + if eid in has_appellate_parent: + continue + # Use jurisdiction_scope.states_covered first; fall back to id prefix. + scope = (e.get("_detail") or {}).get("jurisdiction_scope") or {} + states = scope.get("states_covered") or [] + target_hc = None + if states: + target_hc = STATE_CODE_TO_HC.get(states[0].lower()) + if not target_hc: + prefix = eid.split("_", 1)[0].lower() + target_hc = STATE_CODE_TO_HC.get(prefix) + if not target_hc or target_hc not in entity_ids: + continue + frontend_relationships.append({ + "id": f"inferred_appellate_{eid}", + "source": eid, + "target": target_hc, + "relationship_type": "AppealableTo", + "relationship_category": "appellate_chain", + "is_binding": True, + "is_constitutional": False, + "year_established": None, + "year_abolished": None, + "data_quality": "inferred", + "contested_note": None, + "notes": "Edge inferred at build time from entity id / state code; not present in relationships YAML", + "sources": [], + }) + inferred += 1 + print(f" Backfilled {inferred} District Court β†’ HC edges (data_quality=inferred)") + print("\nStep 11: Building browse index...") browse_index = compute_browse_index(frontend_entities) diff --git a/jem/scripts/derive.py b/jem/scripts/derive.py index c07e11b..0196123 100644 --- a/jem/scripts/derive.py +++ b/jem/scripts/derive.py @@ -296,14 +296,14 @@ def op_modifier(status: str) -> float: base_health = 1.0 - (0.6 * p_ir + 0.4 * p_dp) final_health = max(0.0, min(1.0, base_health * op_modifier(op_status))) - # Rank into the exact buckets expected by the frontend. - # (renderer.js uses STRUCTURAL_HEALTH_RANK with these keys) + # Rank into the exact buckets the frontend filters on + # (state.js: structuralHealthBand + HEALTH_COLORS keys). if final_health <= 0.3: lvl = 'critical' elif final_health <= 0.6: - lvl = 'concerning' + lvl = 'at_risk' elif final_health <= 0.8: - lvl = 'moderate' + lvl = 'watch' else: lvl = 'healthy' diff --git a/jem/web/src/detailView.js b/jem/web/src/detailView.js index da995e0..da552ba 100644 --- a/jem/web/src/detailView.js +++ b/jem/web/src/detailView.js @@ -3,6 +3,7 @@ import { State } from './state.js'; import { buildPanelHTML } from './panel.js'; +import { getAppellateUpstream, getAppellateDownstream } from './entityConnections.js'; const RISK_COLORS = { low: '#16a34a', @@ -103,6 +104,16 @@ function statusPill(e) { return ''; } +// Convert snake_case / Title_Case identifiers into human-readable labels for UI. +function humanize(str) { + if (str == null) return ''; + return String(str) + .replace(/_/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .replace(/\b\w/g, c => c.toUpperCase()); +} + function clogColor(severity) { const map = { critical: '#a32d2d', high: '#c2722b', moderate: '#8a7a2b', low: '#1d7a5a', unknown: '#888' }; return map[severity] || '#888'; @@ -125,7 +136,7 @@ function renderBreakdown(breakdown, label, maxScore) { .map(([factor, pts]) => { const isPos = pts > 0; return ` - ${factor} + ${humanize(factor)} ${isPos ? '+' : ''}${pts} `; }).join(''); @@ -136,8 +147,81 @@ function renderBreakdown(breakdown, label, maxScore) { `; } +// Fused IR + DP breakdown β€” single table with a source tag column so each factor +// is still traceable to its scoring stream. +function renderFusedBreakdown(irBd, dpBd, irColor) { + const entries = []; + if (irBd) for (const [k, v] of Object.entries(irBd)) if (v !== 0) entries.push({ source: 'IR', factor: k, pts: v, color: irColor }); + if (dpBd) for (const [k, v] of Object.entries(dpBd)) if (v !== 0) entries.push({ source: 'DP', factor: k, pts: v, color: '#6366f1' }); + if (!entries.length) return '
No contributing factors recorded.
'; + + entries.sort((a, b) => b.pts - a.pts); + const rows = entries.map(e => { + const isPos = e.pts > 0; + return ` + ${e.source} + ${humanize(e.factor)} + ${isPos ? '+' : ''}${e.pts} + `; + }).join(''); + + return `${rows}
`; +} + // ── Case volume ─────────────────────────────────────────────────────────────── +// ── "Where this fits" β€” appellate chain strip ──────────────────────────────── +// Renders 2-hop upstream + 2-hop downstream from the focal entity as a horizontal +// breadcrumb. Each node is clickable β†’ opens its detail. Hidden when the focal +// entity has no appellate edges. +function renderAppellateChainStrip(entity) { + const upstream = getAppellateUpstream(entity.id, 3); // higher courts + const downstream = getAppellateDownstream(entity.id, 3); // lower courts + if (!upstream.length && !downstream.length) return ''; + + const nodeChip = (id, focal = false) => { + const e = State.getEntityById(id); + if (!e) return ''; + const label = e.abbreviation || e.name; + return ``; + }; + + const tierGroup = (tier) => `${tier.map(id => nodeChip(id)).join('Β·')}`; + + // Render highest tier first (so upstream chain reads left β†’ right going DOWN to focal). + const upChain = [...upstream].reverse().map(tierGroup); + const downChain = downstream.map(tierGroup); + + const arrow = ''; + const parts = [ + ...upChain, + nodeChip(entity.id, true), + ...downChain, + ]; + + return ` +
+
+ Appellate chain β€” where this fits + higher courts ← β†’ lower courts +
+
+ ${parts.join(arrow)} +
+
+ `; +} + +// Inline summary stats shown in the collapsed `` line of the Case Volume section. +function renderCaseVolumeSummary(cv) { + if (!cv) return ''; + const parts = []; + if (cv.clog_severity) parts.push(`${cv.clog_severity.toUpperCase()}`); + if (cv.pending_cases != null) parts.push(`${fmtNum(cv.pending_cases)} pending`); + if (cv.disposal_rate != null) parts.push(`${cv.disposal_rate.toFixed(2)} disposal`); + return parts.join(' Β· '); +} + function renderCaseVolume(cv) { if (!cv) return ''; const stats = []; @@ -898,6 +982,13 @@ export function renderDetailView(entityId, fromEntityId = null) { ${CLUSTER_LABELS[entity.cluster] || entity.cluster || ''}
+
@@ -920,11 +1011,12 @@ export function renderDetailView(entityId, fromEntityId = null) { ${isScoreExcluded ? `
Structural scores not computed for this entity type (governance anchor).
` : ` -
+ +
${healthScore != null ? healthScore.toFixed(2) : 'β€”'}
-
STRUCTURAL HEALTH
+
STRUCTURAL HEALTH INDICATOR
${healthLabel ? `
${healthLabel.toUpperCase()}
` : ''}
@@ -933,42 +1025,63 @@ export function renderDetailView(entityId, fromEntityId = null) { ${isNotValidated ? '
⚐ Scores pending community review
' : ''}
-
-
Constituents
- - ${score != null ? ` -
-
- ↳ Independence Risk - ${score} Β· ${(level || '').toUpperCase()} -
-
${RISK_EXPLAIN[level] || ''}
- ${renderBreakdown(derived.independence_risk_breakdown, 'Independence risk', 15)} -
- ` : ''} - - ${dpScore != null ? ` -
-
- ↳ Discretionary Power - ${dpScore} -
-
- ${renderBreakdown(derived.discretionary_power_breakdown, 'Discretionary power', 15)} + ${(score != null || dpScore != null) ? ` + +
+ Constituent score breakdown +
+ ${score != null ? ` +
+
+ Independence Risk + ${score} Β· ${(level || '').toUpperCase()} +
+
+
${RISK_EXPLAIN[level] || ''}
+
+ Factor breakdown + ${renderBreakdown(derived.independence_risk_breakdown, 'Independence risk', 15)} +
+
+ ` : ''} + ${dpScore != null ? ` +
+
+ Discretionary Power + ${dpScore} +
+
+
+ Factor breakdown + ${renderBreakdown(derived.discretionary_power_breakdown, 'Discretionary power', 15)} +
+
+ ` : ''}
- ` : ''} -
+ + ` : ''} `} - ${renderCaseVolume(detail.case_volume)} - -
-
- Full structural profile - appointment Β· funding Β· audit Β· complaint Β· sources + ${renderAppellateChainStrip(entity)} + + ${renderCaseVolume(detail.case_volume) ? ` + +
+ + Case volume & clog + ${renderCaseVolumeSummary(detail.case_volume)} + +
${renderCaseVolume(detail.case_volume)}
+
+ ` : ''} + + +
+ Profile +
+
-
-
+
@@ -990,7 +1103,7 @@ export function renderDetailView(entityId, fromEntityId = null) { // Auto-render the full structural profile (open by default) const _profileBody = container.querySelector('#dv-profile-body'); if (_profileBody) { - _profileBody.innerHTML = buildPanelHTML(entity); + _profileBody.innerHTML = buildPanelHTML(entity, { omitRiskIndicators: true }); _profileBody.dataset.rendered = '1'; _profileBody.addEventListener('click', ev => { const lnk = ev.target.closest('.detail-connection-row'); @@ -1016,6 +1129,51 @@ export function renderDetailView(entityId, fromEntityId = null) { downloadEntityXLSX(entity); }); + // ── Wire "Where this fits" chips β†’ navigate to that entity ───────────────── + container.querySelector('.wtf-card')?.addEventListener('click', (ev) => { + const btn = ev.target.closest('.wtf-node'); + if (!btn) return; + const id = btn.dataset.entityId; + if (id && id !== entity.id) State.emit('navigateToDetail', id); + }); + + // ── Wire Copy-link ───────────────────────────────────────────────────────── + // Builds a fresh URL from window.location so it works regardless of how the + // user arrived (deep-link, search, navigation history). + container.querySelector('#dv-copy-link')?.addEventListener('click', async (ev) => { + const btn = ev.currentTarget; + const labelEl = btn.querySelector('.dv-action-label'); + const shareUrl = `${location.origin}${location.pathname}${location.search}#/entity/${encodeURIComponent(entity.id)}`; + const flash = (label, cls) => { + const prev = labelEl ? labelEl.textContent : btn.textContent; + if (labelEl) labelEl.textContent = label; else btn.textContent = label; + btn.classList.add(cls); + setTimeout(() => { + if (labelEl) labelEl.textContent = prev; else btn.textContent = prev; + btn.classList.remove(cls); + }, 1500); + }; + try { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(shareUrl); + } else { + // Fallback for non-secure contexts (file://, http://) where Clipboard API is gated. + const ta = document.createElement('textarea'); + ta.value = shareUrl; + ta.style.position = 'fixed'; + ta.style.opacity = '0'; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + ta.remove(); + } + flash('Copied βœ“', 'dv-copy-ok'); + } catch (err) { + console.error('Copy link failed:', err); + flash('Copy failed', 'dv-copy-err'); + } + }); + // ── Wire lens toggles ─────────────────────────────────────────────────────── container.querySelector('#nb-lens-group')?.addEventListener('click', e => { const btn = e.target.closest('.nb-lens-btn'); diff --git a/jem/web/src/entityConnections.js b/jem/web/src/entityConnections.js index a2bc830..95f326e 100644 --- a/jem/web/src/entityConnections.js +++ b/jem/web/src/entityConnections.js @@ -108,3 +108,119 @@ export function buildEntityConnectionSummary(entityId) { export function formatCategoryLabel(cat) { return (cat || 'other').replace(/_/g, ' '); } + +// ── Appellate chain walkers ────────────────────────────────────────────────── +// Edge direction: source appeals β†’ target (lower court β†’ higher court). +// "Upstream" = follow edges in the direction of target (lower β†’ higher) +// "Downstream" = follow edges in reverse (higher ← lower) + +function _appellateAdjacency() { + const out = new Map(); // node β†’ [target] (where node appeals to) + const incoming = new Map(); // node β†’ [source] (who appeals to node) + for (const rel of State.graph?.relationships || []) { + if (rel.relationship_category !== 'appellate_chain') continue; + if (!out.has(rel.source)) out.set(rel.source, []); + out.get(rel.source).push(rel.target); + if (!incoming.has(rel.target)) incoming.set(rel.target, []); + incoming.get(rel.target).push(rel.source); + } + return { out, incoming }; +} + +/** + * Walk up the appellate chain from `entityId` (toward higher courts) up to `maxHops`. + * Returns an array of tiers, each tier being the unique node ids at that hop distance. + * Tier 0 is the immediate parent(s). Stops when a tier has no nodes. + */ +export function getAppellateUpstream(entityId, maxHops = 3) { + const { out } = _appellateAdjacency(); + const tiers = []; + let frontier = new Set([entityId]); + const seen = new Set([entityId]); + for (let h = 0; h < maxHops; h++) { + const next = new Set(); + for (const id of frontier) { + for (const t of out.get(id) || []) { + if (!seen.has(t)) { next.add(t); seen.add(t); } + } + } + if (!next.size) break; + tiers.push([...next]); + frontier = next; + } + return tiers; +} + +/** + * Walk down the appellate chain from `entityId` (toward lower courts) up to `maxHops`. + * Returns tiers in the same shape as getAppellateUpstream. + */ +export function getAppellateDownstream(entityId, maxHops = 3) { + const { incoming } = _appellateAdjacency(); + const tiers = []; + let frontier = new Set([entityId]); + const seen = new Set([entityId]); + for (let h = 0; h < maxHops; h++) { + const next = new Set(); + for (const id of frontier) { + for (const s of incoming.get(id) || []) { + if (!seen.has(s)) { next.add(s); seen.add(s); } + } + } + if (!next.size) break; + tiers.push([...next]); + frontier = next; + } + return tiers; +} + +/** + * Build the full appellate hierarchy as ordered tiers, where tier 0 are roots + * (no outgoing appellate edges = top of chain, e.g. Supreme Court). + * Uses longest-path layering so leaves sit at the bottom regardless of DAG shape. + * Returns { tiers, edges, orphans }. + */ +export function buildAppellateHierarchy() { + const { out, incoming } = _appellateAdjacency(); + const allNodes = new Set(); + const edges = []; + for (const rel of State.graph?.relationships || []) { + if (rel.relationship_category !== 'appellate_chain') continue; + allNodes.add(rel.source); + allNodes.add(rel.target); + edges.push({ src: rel.source, tgt: rel.target }); + } + // Roots = nodes with no outgoing appellate edges (nothing higher to appeal to). + const roots = [...allNodes].filter(id => !(out.get(id)?.length)); + + // Longest-path layer assignment: layer(n) = max(layer(parent)) + 1, roots = 0. + // We follow incoming-edge ancestors via memoization (DAG assumed). + const layer = new Map(); + const computeLayer = (id, stack = new Set()) => { + if (layer.has(id)) return layer.get(id); + if (stack.has(id)) { layer.set(id, 0); return 0; } // cycle guard + stack.add(id); + const parents = out.get(id) || []; // "higher than me" β†’ my layer = parent+1 + if (!parents.length) { layer.set(id, 0); stack.delete(id); return 0; } + let lv = 0; + for (const p of parents) lv = Math.max(lv, computeLayer(p, stack) + 1); + layer.set(id, lv); + stack.delete(id); + return lv; + }; + for (const id of allNodes) computeLayer(id); + + const tierMap = new Map(); + for (const [id, lv] of layer) { + if (!tierMap.has(lv)) tierMap.set(lv, []); + tierMap.get(lv).push(id); + } + const maxLayer = Math.max(0, ...tierMap.keys()); + const tiers = []; + for (let i = 0; i <= maxLayer; i++) tiers.push((tierMap.get(i) || []).slice().sort()); + + // Orphan entities are court-like entities with no appellate edges at all. + // We don't enumerate them here (would need entity-type filtering); caller + // can compute separately if it wants to surface them. + return { tiers, edges, roots }; +} diff --git a/jem/web/src/main.js b/jem/web/src/main.js index 6d8da53..94a8b90 100644 --- a/jem/web/src/main.js +++ b/jem/web/src/main.js @@ -34,6 +34,72 @@ function _updateBackBtn() { } } +// ── URL hash routing ────────────────────────────────────────────────────────── +// Shareable deep-links via fragment: '#/entity/' opens the detail view, +// '#/map' opens the map. Empty/absent hash = summary. +// Hash-only (not pathname) so static hosts need no rewrites. + +function _entityIdFromHash() { + const m = (location.hash || '').match(/^#\/entity\/(.+)$/); + return m ? decodeURIComponent(m[1]) : null; +} + +function _hashViewFromHash() { + if (/^#\/entity\/.+/.test(location.hash)) return 'detail'; + if (location.hash === '#/map') return 'map'; + return 'summary'; +} + +let _suppressHashSync = false; + +function _syncHashForView(view, entityId) { + if (_suppressHashSync) return; + let target = ''; + if (view === 'detail' && entityId) target = `#/entity/${encodeURIComponent(entityId)}`; + else if (view === 'map') target = '#/map'; + // Empty hash for summary. + if (location.hash === target) return; + const url = target ? `${location.pathname}${location.search}${target}` : `${location.pathname}${location.search}`; + history.pushState(null, '', url); +} + +function _showMissingEntityToast(id) { + const el = document.createElement('div'); + el.className = 'jem-toast jem-toast-warn'; + el.textContent = `Entity β€œ${id}” not found β€” showing overview.`; + document.body.appendChild(el); + requestAnimationFrame(() => el.classList.add('jem-toast-in')); + setTimeout(() => { + el.classList.remove('jem-toast-in'); + setTimeout(() => el.remove(), 300); + }, 3200); +} + +function _applyHashRoute() { + const view = _hashViewFromHash(); + _suppressHashSync = true; + try { + if (view === 'detail') { + const id = _entityIdFromHash(); + const e = id ? State.getEntityById(id) : null; + if (e) { + switchView('detail', id); + } else { + if (id) _showMissingEntityToast(id); + // Clear the bad hash so refresh doesn't re-toast. + history.replaceState(null, '', `${location.pathname}${location.search}`); + switchView('summary'); + } + } else if (view === 'map') { + switchView('map'); + } else { + switchView('summary'); + } + } finally { + _suppressHashSync = false; + } +} + function switchView(view, entityId = null) { const prevView = document.body.dataset.appView; @@ -110,6 +176,8 @@ function switchView(view, entityId = null) { requestAnimationFrame(() => fitFocusToView({ animate: false })); }); } + + _syncHashForView(view, entityId); } function wireNavigationEvents() { @@ -362,7 +430,18 @@ async function boot() { render(); initSummaryView(); - switchView('summary'); + + // Hash deep-link: open the entity / view encoded in location.hash if any, + // otherwise default to summary. Browser back/forward and pasted URLs + // re-route via the popstate/hashchange listeners below. + if (location.hash) { + _applyHashRoute(); + } else { + switchView('summary'); + } + + window.addEventListener('popstate', _applyHashRoute); + window.addEventListener('hashchange', _applyHashRoute); } catch (err) { console.error('JEM boot error:', err); diff --git a/jem/web/src/panel.js b/jem/web/src/panel.js index d79e67a..da97ac4 100644 --- a/jem/web/src/panel.js +++ b/jem/web/src/panel.js @@ -37,7 +37,7 @@ export function openDetailPanel(entity) { nameEl.innerHTML = `${entity.name} ${entity.name_hindi}`; } - typeEl.textContent = `${formatType(entity.type)} Β· ${entity.level_of_government} Β· ${entity.operational_status}`; + typeEl.textContent = `${formatType(entity.type)} Β· ${humanize(entity.level_of_government)} Β· ${humanize(entity.operational_status)}`; typeEl.className = `detail-type status-${entity.operational_status.toLowerCase().replace('_','-')}`; body.innerHTML = buildPanelHTML(entity); @@ -64,7 +64,7 @@ export function closeDetailPanel() { State.clearEntity(); } -function buildPanelHTML(e) { +function buildPanelHTML(e, opts = {}) { const d = e._detail || {}; const derived = e.derived || {}; const irColors = State.graph ? State.getIndependenceRiskColors() : {}; @@ -99,7 +99,7 @@ function buildPanelHTML(e) { html += section('Lifecycle', `
Established${e.created_year}
${e.abolished_year ? `
Abolished${e.abolished_year}
` : ''} -
Status${e.operational_status}
+
Status${humanize(e.operational_status)}
${e.constitutional_basis ? `
Constitutional basis${e.constitutional_basis}
` : ''} ${e.statutory_basis ? `
Statutory basis${e.statutory_basis}
` : ''} `); @@ -238,7 +238,11 @@ function buildPanelHTML(e) { html += buildStructuralGapsSectionHTML(e); // ── Derived Scores ───────────────────────────────────── - if (State.viewMode === 'risk' && irScore === undefined && dpScore === undefined && healthScore == null) { + // Suppressed in detail view (omitRiskIndicators) since the Indicator card + // and Constituent breakdown widgets already render this information. + if (opts.omitRiskIndicators) { + // skip block + } else if (State.viewMode === 'risk' && irScore === undefined && dpScore === undefined && healthScore == null) { html += section('Structural Risk Indicators', `

Structural-health, independence-risk and discretionary-power scores are not computed for this entity yet.

`); @@ -441,7 +445,7 @@ function buildBreakdown(breakdown) { const cls = pts > 0 ? 'breakdown-positive' : 'breakdown-negative'; html += `
${sign}${pts} - ${reason} + ${humanize(reason)}
`; }); html += '
'; @@ -507,6 +511,15 @@ function formatType(type) { return (type || '').replace(/([A-Z])/g, ' $1').trim(); } +function humanize(str) { + if (str == null) return ''; + return String(str) + .replace(/_/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .replace(/\b\w/g, c => c.toUpperCase()); +} + // ── Event Binding (called after DOM ready) ──────────────────────────────────── // Exported so detailView.js can embed the full structural profile inline. diff --git a/jem/web/src/summaryView.js b/jem/web/src/summaryView.js index b0695ed..9e66b74 100644 --- a/jem/web/src/summaryView.js +++ b/jem/web/src/summaryView.js @@ -2,6 +2,7 @@ // Landing page: stat band, risk-distribution small multiples, high-risk registry, all-entities table. import { State } from './state.js'; +import { buildAppellateHierarchy } from './entityConnections.js'; const RISK_COLORS = { low: '#16a34a', @@ -641,6 +642,713 @@ function renderEntitiesTab(members) { return `
${rows}
`; } +// ── Temporal structure (decade mirror chart + slider) ──────────────────────── +// Births up, deaths down. Constitutional events overlaid as vertical markers. +// A draggable year scrubber focuses a year β€” chart highlights the decade, and +// an inline list shows entities created or abolished within a window. + +const TEMPORAL_DECADE_START = 1950; +const TEMPORAL_DECADE_END = Math.floor(new Date().getFullYear() / 10) * 10; // current decade start +const FOCUS_WINDOW_YEARS = 3; // Β±3 yr window when scrubbing + +// Pick bucket size based on visible span: decade > 40yr, 5yr 15–40, yearly < 15. +function _pickBucketSize(span) { + if (span > 40) return 10; + if (span > 15) return 5; + return 1; +} + +function _bucketLabel(start, size) { + if (size === 10) return `${String(start).slice(2)}s`; + if (size === 5) return `${start}`; + return String(start); +} + +function _buildBuckets(entities, rangeStart, rangeEnd) { + const size = _pickBucketSize(rangeEnd - rangeStart); + const aligned = Math.floor(rangeStart / size) * size; + const buckets = []; + for (let y = aligned; y < rangeEnd; y += size) { + buckets.push({ start: y, size, label: _bucketLabel(y, size), births: [], deaths: [] }); + } + const bucketOf = (yr) => { + if (yr == null) return null; + const idx = Math.floor((yr - aligned) / size); + if (idx < 0 || idx >= buckets.length) return null; + return buckets[idx]; + }; + for (const e of entities) { + const b1 = bucketOf(e.created_year); + if (b1) b1.births.push(e); + const b2 = bucketOf(e.abolished_year); + if (b2) b2.deaths.push(e); + } + return { buckets, size }; +} + +function _eventTypeColor(type) { + return { + constitutional: '#7c3aed', + institutional: '#0891b2', + reform: '#16a34a', + judgment: '#dc2626', + }[type] || '#6b7280'; +} + +function renderTemporalStructure(opts = {}) { + const entities = State.graph?.entities || []; + const events = (State.graph?.timeline_events || []).slice().sort((a, b) => a.year - b.year); + const fullStart = TEMPORAL_DECADE_START; + const fullEnd = TEMPORAL_DECADE_END + 10; + const rangeStart = opts.rangeStart ?? fullStart; + const rangeEnd = opts.rangeEnd ?? fullEnd; + const brushMode = !!opts.brushMode; + const isZoomed = rangeStart !== fullStart || rangeEnd !== fullEnd; + const { buckets, size: bucketSize } = _buildBuckets(entities, rangeStart, rangeEnd); + + const maxBirths = Math.max(1, ...buckets.map(b => b.births.length)); + const maxDeaths = Math.max(1, ...buckets.map(b => b.deaths.length)); + const yMax = Math.max(maxBirths, maxDeaths); + + // Layout + const W = 760, H = 300, PAD_L = 64, PAD_R = 20, PAD_T = 36, PAD_B = 64; + const plotW = W - PAD_L - PAD_R; + const plotH = H - PAD_T - PAD_B; + const midY = PAD_T + plotH / 2; + const halfH = plotH / 2; + const barW = plotW / buckets.length; + const barGap = Math.max(4, barW * 0.18); + const usableBarW = barW - barGap; + + const yearToX = (yr) => PAD_L + ((yr - rangeStart) / (rangeEnd - rangeStart)) * plotW; + + // Bars β€” labels thin out when bucketSize=1 to avoid label collisions. + const labelEvery = bucketSize === 1 ? Math.max(1, Math.ceil(buckets.length / 12)) : 1; + const barsSVG = buckets.map((b, i) => { + const x = PAD_L + i * barW + barGap / 2; + const bH = (b.births.length / yMax) * halfH; + const dH = (b.deaths.length / yMax) * halfH; + const showLabel = i % labelEvery === 0; + return ` + + ${bH > 0 ? `${b.births.length} created in ${b.label}${b.size > 1 ? ` (${b.start}–${b.start + b.size - 1})` : ''}` : ''} + ${dH > 0 ? `${b.deaths.length} abolished in ${b.label}${b.size > 1 ? ` (${b.start}–${b.start + b.size - 1})` : ''}` : ''} + ${showLabel ? `${b.label}` : ''} + ${b.births.length ? `${b.births.length}` : ''} + ${b.deaths.length ? `${b.deaths.length}` : ''} + + `; + }).join(''); + + // Constitutional event markers (only those in visible range) + const eventsSVG = events.map((ev, i) => { + if (ev.year < rangeStart || ev.year > rangeEnd) return ''; + const x = yearToX(ev.year); + const color = _eventTypeColor(ev.type); + return ` + + + + ${ev.year} β€” ${ev.label} + + `; + }).join(''); + + // Mid axis (zero baseline) + const midAxis = ``; + + // Y labels β€” direction header + max-count ticks. + // Direction labels sit at the SVG's very left edge (anchor start at x=2), + // so they stay confined to the left gutter and never collide with bars. + const yLabels = ` + ↑ Created + ↓ Abolished + ${yMax} + 0 + ${yMax} + + `; + + // Range rail at the bottom β€” two handles for zoom + one thumb for focus. + const scrubY = H - PAD_B + 30; + const scrubX1 = PAD_L; + const scrubX2 = W - PAD_R; + const proposedFocus = opts.focusYear ?? Math.round(rangeStart + (rangeEnd - rangeStart) * 0.5); + const initialFocusYear = Math.max(rangeStart, Math.min(rangeEnd, proposedFocus)); + + // ALL three rail elements (handles + focus thumb) share the SAME coordinate + // system: the full-rail axis (1950 β†’ fullEnd). This is what makes them + // visually consistent β€” the focus thumb always sits between the handles. + const fullRailYearToX = (yr) => + PAD_L + ((yr - fullStart) / (fullEnd - fullStart)) * plotW; + const leftHandleX = fullRailYearToX(rangeStart); + const rightHandleX = fullRailYearToX(rangeEnd); + const focusX = fullRailYearToX(initialFocusYear); + + const railSVG = ` + + + + ${fullStart} + ${fullEnd} + + + + ${initialFocusYear} + + + `; + + const eventLegend = ['constitutional', 'institutional', 'reform', 'judgment'].map(t => + `${t}` + ).join(''); + + const bucketLabel = bucketSize === 10 ? 'decade' : bucketSize === 5 ? '5-yr' : 'year'; + const span = rangeEnd - rangeStart; + + // Preset eras β€” policy-researcher language. Each renders as a clickable pill. + const currentYear = new Date().getFullYear(); + const presets = [ + { label: 'All', s: fullStart, e: fullEnd }, + { label: 'Pre-tribunalisation 1950–85', s: 1950, e: 1985 }, + { label: 'Liberalisation 1991–2010', s: 1991, e: 2010 }, + { label: 'Tribunals Reforms era 2017+', s: 2017, e: fullEnd }, + { label: 'Last 25 yr', s: currentYear - 25, e: fullEnd }, + ]; + const presetsHTML = presets.map(p => { + const active = p.s === rangeStart && p.e === rangeEnd; + return ``; + }).join(''); + + return ` +
+
+ ${entities.filter(e => e.created_year).length} entities with creation year Β· + ${entities.filter(e => e.abolished_year).length} with abolition year Β· + ${events.length} constitutional moments + + ${buckets.length} ${bucketLabel} bucket${buckets.length === 1 ? '' : 's'} Β· ${span} yr span + +
+
${presetsHTML}
+
+ + ${midAxis} + ${yLabels} + ${barsSVG} + ${eventsSVG} + ${railSVG} + +
+
${eventLegend}click event = focus Β· double-click = zoom Β±15 yr
+
+
+ `; +} +// "Spine + anomalies" β€” researcher-grade overview rather than a 600-node graph. +// Left: the canonical 5-tier appellate structure with curated reference +// entities at each tier (the rest is available via the catalog drawer +// and per-entity profiles). +// Right: structural anomalies in the appellate chain β€” entities flagged as +// Not Constituted, Partial Operational, or carrying documented +// structural gaps. These are the editorial story. +// +// Why this shape: a policy researcher already knows there is a Supreme Court +// and 25 High Courts; the value of an "appellate hierarchy" overview is to +// surface *what is structurally broken*, not enumerate names already familiar. + +const TIER_DEFS = [ + { key: 0, name: 'Constitutional apex', note: 'Final appellate jurisdiction under Article 136 (SLP).' }, + { key: 1, name: 'Constitutional courts & central tribunals', note: 'High Courts under Article 214; central tribunals under their parent statutes.' }, + { key: 2, name: 'Permanent benches & state tribunals', note: 'Permanent HC benches; state-level appellate tribunals.' }, + { key: 3, name: 'Subordinate appellate & regional', note: 'District & sessions courts; regional tribunal benches.' }, + { key: 4, name: 'First-instance & quasi-judicial', note: 'Trial-level courts and original-jurisdiction quasi-judicial bodies.' }, +]; + +function _entitiesInAppellateGraph() { + const set = new Set(); + for (const r of State.graph?.relationships || []) { + if (r.relationship_category === 'appellate_chain') { + set.add(r.source); set.add(r.target); + } + } + return set; +} + +// Structural anomaly register β€” every category a legal-policy researcher would +// consider materially anomalous. Project-wide, not restricted to the appellate +// graph (Not Constituted entities by definition have no edges). +// +// Severity order (informs default-open state + colour): +// critical β†’ Not Constituted, De Facto Blocked, Abolished +// high β†’ Suspended, Partial Operational, Merged +// moderate β†’ Structural exception, Contested data, Structural gaps +function _appellateAnomalyGroups() { + const entities = State.graph?.entities || []; + const now = new Date().getFullYear(); + + // Per-row fact: the single most relevant datum for a researcher (year + context). + const fact = (e, cat) => { + if (cat === 'Not Constituted' && e.created_year) { + return `${e.created_year} Β· ${now - e.created_year} yr pending`; + } + if (cat === 'Abolished' && e.abolished_year) { + return `Abolished ${e.abolished_year}`; + } + if (cat === 'Suspended' && e.suspended_year) return `Suspended ${e.suspended_year}`; + if (cat === 'Merged') { + const into = e.merged_into || e._detail?.merged_into; + return into ? `β†’ ${into}` : 'Merged'; + } + if (cat === 'De Facto Blocked') return 'Non-operational in practice'; + if (cat === 'Partial Operational') return 'Partial coverage'; + if (cat === 'Structural exception') return 'Deviates from statutory template'; + if (cat === 'Contested data') return e.data_quality_notes ? 'Sources disagree' : 'Data contested'; + if (cat === 'Structural gaps') { + const n = Number(e.gap_count) || (e.gaps?.length || 0); + return `${n} gap${n === 1 ? '' : 's'}`; + } + return ''; + }; + + const groups = { + 'Not Constituted': { severity: 'critical', entities: [] }, + 'Abolished': { severity: 'abolished', entities: [] }, + 'De Facto Blocked': { severity: 'critical', entities: [] }, + 'Suspended': { severity: 'high', entities: [] }, + 'Partial Operational': { severity: 'high', entities: [] }, + 'Merged': { severity: 'high', entities: [] }, + 'Structural exception': { severity: 'moderate', entities: [] }, + 'Contested data': { severity: 'moderate', entities: [] }, + 'Structural gaps': { severity: 'moderate', entities: [] }, + }; + + // First pass: assign by primary operational_status. An entity sits in exactly + // one status bucket so the count tallies cleanly with the stat band. + for (const e of entities) { + const status = e.operational_status; + const row = { id: e.id, name: e.name, abbr: e.abbreviation }; + let placed = null; + if (status === 'Not_Constituted') placed = 'Not Constituted'; + else if (status === 'Abolished') placed = 'Abolished'; + else if (status === 'De_Facto_Blocked') placed = 'De Facto Blocked'; + else if (status === 'Suspended') placed = 'Suspended'; + else if (status === 'Partial_Operational') placed = 'Partial Operational'; + else if (status === 'Merged') placed = 'Merged'; + if (placed) { + row.fact = fact(e, placed); + groups[placed].entities.push(row); + } + } + + // Second pass: structural-exception, contested data quality, and documented + // gaps for *active* entities only (we don't want to double-count an Abolished + // entity into a gaps bucket β€” the abolition IS the anomaly). + for (const e of entities) { + if (['Not_Constituted', 'Abolished', 'De_Facto_Blocked', 'Suspended', 'Partial_Operational', 'Merged'] + .includes(e.operational_status)) continue; + const row = { id: e.id, name: e.name, abbr: e.abbreviation }; + if (e.structural_exception) { + row.fact = fact(e, 'Structural exception'); + groups['Structural exception'].entities.push(row); + continue; + } + if (e.data_quality === 'contested') { + row.fact = fact(e, 'Contested data'); + groups['Contested data'].entities.push(row); + continue; + } + const gapCount = Number(e.gap_count) || (e.gaps?.length || 0); + if (gapCount > 0) { + row.fact = fact({ ...e, gap_count: gapCount }, 'Structural gaps'); + groups['Structural gaps'].entities.push(row); + } + } + + // Sort within group: by name (researcher scan-pattern). + for (const k of Object.keys(groups)) { + groups[k].entities.sort((a, b) => (a.name || '').localeCompare(b.name || '')); + } + + return Object.entries(groups) + .filter(([, g]) => g.entities.length) + .map(([tag, g]) => ({ tag, severity: g.severity, entities: g.entities })); +} + +function _wireTemporalStructure(tsWrap, entities) { + const events = (State.graph?.timeline_events || []).slice().sort((a, b) => a.year - b.year); + const W = 760, PAD_L = 64, PAD_R = 20; + const plotW = W - PAD_L - PAD_R; + const fullStart = TEMPORAL_DECADE_START; + const fullEnd = TEMPORAL_DECADE_END + 10; + const MIN_SPAN = 5; // smallest zoom span in years + + const getState = () => { + const wrap = tsWrap.querySelector('.ts-wrap'); + return { + wrap, + svg: tsWrap.querySelector('.ts-svg'), + focusPanel: tsWrap.querySelector('#ts-focus-panel'), + tip: tsWrap.querySelector('.ts-tip'), + tipBg: tsWrap.querySelector('.ts-tip-bg'), + tipText: tsWrap.querySelector('.ts-tip-text'), + handleL: tsWrap.querySelector('.ts-handle-left'), + handleR: tsWrap.querySelector('.ts-handle-right'), + railRange: tsWrap.querySelector('.ts-rail-range'), + thumb: tsWrap.querySelector('.ts-scrub-thumb'), + focusLine: tsWrap.querySelector('.ts-scrub-focus-line'), + yearText: tsWrap.querySelector('.ts-scrub-year'), + rangeStart: parseInt(wrap.dataset.rangeStart, 10), + rangeEnd: parseInt(wrap.dataset.rangeEnd, 10), + focusYear: parseInt(wrap.dataset.focusYear, 10), + }; + }; + + const rerender = (overrides = {}) => { + const s = getState(); + tsWrap.innerHTML = renderTemporalStructure({ + rangeStart: overrides.rangeStart ?? s.rangeStart, + rangeEnd: overrides.rangeEnd ?? s.rangeEnd, + focusYear: overrides.focusYear ?? s.focusYear, + }); + refreshFocusPanel(); + }; + + // Coordinate helpers + // Visible-range x ↔ year (for the focus thumb / chart plot). + const visibleXToYear = (clientX) => { + const { svg, rangeStart, rangeEnd } = getState(); + const rect = svg.getBoundingClientRect(); + const scale = rect.width / W; + const localX = (clientX - rect.left) / scale; + const px = Math.max(PAD_L, Math.min(W - PAD_R, localX)); + const yr = rangeStart + ((px - PAD_L) / plotW) * (rangeEnd - rangeStart); + return Math.round(yr); + }; + const yearToVisibleLocalX = (yr, rangeStart, rangeEnd) => + PAD_L + ((yr - rangeStart) / (rangeEnd - rangeStart)) * plotW; + // Full-rail x ↔ year (for the range handles, which span the entire timeline regardless of zoom). + const fullRailXToYear = (clientX) => { + const { svg } = getState(); + const rect = svg.getBoundingClientRect(); + const scale = rect.width / W; + const localX = (clientX - rect.left) / scale; + const px = Math.max(PAD_L, Math.min(W - PAD_R, localX)); + const yr = fullStart + ((px - PAD_L) / plotW) * (fullEnd - fullStart); + return Math.round(yr); + }; + const yearToFullRailX = (yr) => + PAD_L + ((yr - fullStart) / (fullEnd - fullStart)) * plotW; + + const refreshFocusPanel = () => { + const { wrap, focusPanel } = getState(); + const year = parseInt(wrap.dataset.focusYear, 10); + const lo = year - FOCUS_WINDOW_YEARS; + const hi = year + FOCUS_WINDOW_YEARS; + const created = entities.filter(e => e.created_year != null && e.created_year >= lo && e.created_year <= hi); + const abolished = entities.filter(e => e.abolished_year != null && e.abolished_year >= lo && e.abolished_year <= hi); + const nearEvents = events.filter(ev => ev.year >= lo && ev.year <= hi); + const chip = (e, kind) => ``; + const eventRow = nearEvents.length ? ` +
+ ${nearEvents.map(ev => `${ev.year} Β· ${ev.label}`).join('')} +
` : ''; + if (!created.length && !abolished.length && !nearEvents.length) { + focusPanel.innerHTML = `

Nothing within Β±${FOCUS_WINDOW_YEARS} years of ${year}.

`; + return; + } + focusPanel.innerHTML = ` +
+ ${year} + within Β±${FOCUS_WINDOW_YEARS} yr + + ${created.length} created + ${abolished.length} abolished + +
+ ${eventRow} + ${created.length ? `
Born
${created.map(e => chip(e, 'birth')).join('')}
` : ''} + ${abolished.length ? `
Abolished
${abolished.map(e => chip(e, 'death')).join('')}
` : ''} + `; + }; + + refreshFocusPanel(); + + // ── Move focus thumb (always at full-rail x for consistency with handles) ─ + const moveScrubberTo = (year) => { + const { wrap, rangeStart, rangeEnd, thumb, yearText } = getState(); + year = Math.max(rangeStart, Math.min(rangeEnd, year)); + wrap.dataset.focusYear = year; + const x = yearToFullRailX(year); + thumb?.setAttribute('cx', x); + if (yearText) { yearText.setAttribute('x', x); yearText.textContent = year; } + refreshFocusPanel(); + }; + // Visual-only thumb update (no panel refresh β€” used during handle drag). + const syncThumbVisual = () => { + const { wrap, thumb, yearText } = getState(); + const yr = parseInt(wrap.dataset.focusYear, 10); + const x = yearToFullRailX(yr); + thumb?.setAttribute('cx', x); + if (yearText) { yearText.setAttribute('x', x); yearText.textContent = yr; } + }; + + // ── Tooltip ─────────────────────────────────────────────────── + const showTip = (anchorX, anchorY, text) => { + const { tip, tipBg, tipText } = getState(); + if (!tip) return; + // Measure: monospace text β€” estimate width by char count. + const w = Math.max(80, text.length * 7 + 16); + tipBg.setAttribute('width', w); + tip.setAttribute('transform', `translate(${anchorX - w / 2}, ${anchorY - 42})`); + tipText.setAttribute('x', w / 2); + tipText.textContent = text; + tip.style.display = ''; + }; + const hideTip = () => { + const { tip } = getState(); + if (tip) tip.style.display = 'none'; + }; + + // ── Drag dispatcher ─────────────────────────────────────────── + // dragRole: 'handle-left' | 'handle-right' | 'thumb' | 'plot' | null + let dragRole = null; + let pendingRangeStart = null; + let pendingRangeEnd = null; + + const startDrag = (target, clientX) => { + const role = target.closest('[data-role]')?.dataset.role; + if (role === 'handle-left' || role === 'handle-right' || role === 'thumb') { + dragRole = role; + const s = getState(); + pendingRangeStart = s.rangeStart; + pendingRangeEnd = s.rangeEnd; + updateDrag(clientX); + return true; + } + if (target.closest('.ts-svg') && !target.closest('.ts-bar, .ts-event circle, .ts-rail')) { + // Clicking on the chart plot area scrubs focus along the visible axis. + dragRole = 'plot'; + moveScrubberTo(visibleXToYear(clientX)); + return true; + } + return false; + }; + + const updateDrag = (clientX) => { + const s = getState(); + if (dragRole === 'handle-left') { + // Use the THUMB radius as the floor so the handle can never overlap the focus thumb + // visually β€” focus year auto-clamps in lockstep. + const yr = fullRailXToYear(clientX); + const clamped = Math.max(fullStart, Math.min(pendingRangeEnd - MIN_SPAN, yr)); + pendingRangeStart = clamped; + const x = yearToFullRailX(clamped); + s.handleL?.setAttribute('cx', x); + s.railRange?.setAttribute('x1', x); + showTip(x, parseFloat(s.handleL?.getAttribute('cy') || 0), + `From ${clamped} Β· span ${pendingRangeEnd - clamped}`); + // Live-clamp focus year if it now falls outside pending range. + const curFocus = parseInt(s.wrap.dataset.focusYear, 10); + if (curFocus < pendingRangeStart) { + s.wrap.dataset.focusYear = pendingRangeStart; + syncThumbVisual(); + } + } else if (dragRole === 'handle-right') { + const yr = fullRailXToYear(clientX); + const clamped = Math.max(pendingRangeStart + MIN_SPAN, Math.min(fullEnd, yr)); + pendingRangeEnd = clamped; + const x = yearToFullRailX(clamped); + s.handleR?.setAttribute('cx', x); + s.railRange?.setAttribute('x2', x); + showTip(x, parseFloat(s.handleR?.getAttribute('cy') || 0), + `To ${clamped} Β· span ${clamped - pendingRangeStart}`); + const curFocus = parseInt(s.wrap.dataset.focusYear, 10); + if (curFocus > pendingRangeEnd) { + s.wrap.dataset.focusYear = pendingRangeEnd; + syncThumbVisual(); + } + } else if (dragRole === 'thumb') { + // Thumb is on the full-rail axis β€” use the same conversion. + moveScrubberTo(fullRailXToYear(clientX)); + } else if (dragRole === 'plot') { + // Plot-area drag uses the visible (zoomed) axis. + moveScrubberTo(visibleXToYear(clientX)); + } + }; + + const endDrag = () => { + if (!dragRole) return; + if (dragRole === 'handle-left' || dragRole === 'handle-right') { + hideTip(); + const s = getState(); + if (pendingRangeStart !== s.rangeStart || pendingRangeEnd !== s.rangeEnd) { + const curFocus = parseInt(s.wrap.dataset.focusYear, 10); + const newFocus = Math.max(pendingRangeStart, Math.min(pendingRangeEnd, curFocus)); + rerender({ rangeStart: pendingRangeStart, rangeEnd: pendingRangeEnd, focusYear: newFocus }); + } + } + dragRole = null; + pendingRangeStart = null; + pendingRangeEnd = null; + }; + + tsWrap.addEventListener('mousedown', (ev) => { + if (ev.target.closest('.ts-preset, .ts-chip, .ts-bar, .ts-event circle')) return; + if (startDrag(ev.target, ev.clientX)) ev.preventDefault(); + }); + document.addEventListener('mousemove', (ev) => { if (dragRole) updateDrag(ev.clientX); }); + document.addEventListener('mouseup', endDrag); + + tsWrap.addEventListener('touchstart', (ev) => { + if (ev.target.closest('.ts-preset, .ts-chip, .ts-bar, .ts-event circle')) return; + if (ev.touches[0]) startDrag(ev.target, ev.touches[0].clientX); + }, { passive: true }); + document.addEventListener('touchmove', (ev) => { if (dragRole && ev.touches[0]) updateDrag(ev.touches[0].clientX); }, { passive: true }); + document.addEventListener('touchend', endDrag); + + // ── Click delegation (presets, bars, event single-click, chips) ────── + tsWrap.addEventListener('click', (ev) => { + const preset = ev.target.closest('.ts-preset'); + if (preset) { + const s = parseInt(preset.dataset.start, 10); + const e = parseInt(preset.dataset.end, 10); + rerender({ rangeStart: s, rangeEnd: e, focusYear: Math.round((s + e) / 2) }); + return; + } + const bar = ev.target.closest('.ts-bar'); + if (bar) { + const start = parseInt(bar.dataset.bucketStart, 10); + const size = parseInt(bar.dataset.bucketSize, 10); + moveScrubberTo(start + Math.floor(size / 2)); + return; + } + const evt = ev.target.closest('.ts-event'); + if (evt) { + const idx = parseInt(evt.dataset.eventIndex, 10); + const e = events[idx]; + if (e) moveScrubberTo(e.year); + return; + } + const chipBtn = ev.target.closest('.ts-chip'); + if (chipBtn) State.emit('navigateToDetail', chipBtn.dataset.entityId); + }); + + // ── Double-click an event marker β†’ zoom Β±15 yr around it ───── + tsWrap.addEventListener('dblclick', (ev) => { + const evt = ev.target.closest('.ts-event'); + if (!evt) return; + const idx = parseInt(evt.dataset.eventIndex, 10); + const e = events[idx]; + if (!e) return; + const s = Math.max(fullStart, e.year - 15); + const ee = Math.min(fullEnd, e.year + 15); + rerender({ rangeStart: s, rangeEnd: ee, focusYear: e.year }); + }); +} + +function renderAppellateHierarchy() { + const { tiers } = buildAppellateHierarchy(); + if (!tiers.length || tiers.every(t => !t.length)) { + return '

No appellate-chain relationships in the dataset yet.

'; + } + + // Curate each tier: surface 4 reference entities. Sort by structural salience β€” + // anomaly status first (so a Not Constituted entity at tier 1 is visible), + // then by entity name length (shorter β‰ˆ more canonical) as a stable secondary. + const inGraph = _entitiesInAppellateGraph(); + const salience = (id) => { + const e = State.getEntityById(id); + if (!e) return 99; + if (e.operational_status === 'Not_Constituted') return 0; + if (e.operational_status === 'De_Facto_Blocked') return 1; + if (e.operational_status === 'Partial_Operational') return 2; + return 10; + }; + + const referenceChip = (id) => { + const e = State.getEntityById(id); + if (!e) return ''; + const label = e.abbreviation || e.name; + const full = (e.name || id).replace(/"/g, '"'); + const flag = e.operational_status === 'Not_Constituted' ? 'NOT CONSTITUTED' + : e.operational_status === 'De_Facto_Blocked' ? 'DE FACTO BLOCKED' + : e.operational_status === 'Abolished' ? `ABOLISHED${e.abolished_year ? ' ' + e.abolished_year : ''}` + : ''; + const chipClass = e.operational_status === 'Abolished' ? 'ah-chip ah-chip-abolished' : 'ah-chip'; + return ``; + }; + + const tierRow = (tier, ti) => { + const def = TIER_DEFS[ti] || { name: `Tier ${ti + 1}`, note: '' }; + const sorted = [...tier].sort((a, b) => salience(a) - salience(b) || (State.getEntityById(a)?.name || a).length - (State.getEntityById(b)?.name || b).length); + const refs = sorted.slice(0, 4).map(referenceChip).join(''); + const moreCount = tier.length - 4; + const rest = sorted.slice(4).map(referenceChip).join(''); + return ` +
+
${ti + 1}
+
+
+ ${def.name} + ${tier.length} ${tier.length === 1 ? 'entity' : 'entities'} +
+ ${def.note ? `
${def.note}
` : ''} +
+ ${refs} + ${moreCount > 0 ? `
+ ${moreCount} more
${rest}
` : ''} +
+
+
+ `; + }; + + const anomalyGroups = _appellateAnomalyGroups(); + const anomalyTotal = anomalyGroups.reduce((acc, g) => acc + g.entities.length, 0); + const anomalyHTML = anomalyGroups.length ? anomalyGroups.map(g => ` +
+ + ${g.tag} + ${g.entities.length} + +
+ ${g.entities.map(a => ` + + `).join('')} +
+
+ `).join('') : '

No structural anomalies flagged in appellate graph.

'; + + const totalEntities = tiers.reduce((acc, t) => acc + t.length, 0); + + return ` +
+
+ ${totalEntities} entities across + ${tiers.length} appellate tiers Β· + ${anomalyTotal} structural anomalies +
+
+
+
The spine
+ ${tiers.map(tierRow).join('')} +
+ +
+
+ `; +} + function buildClusterDrillHTML(clusterId, entities, filterIds = null) { const label = CLUSTER_LABELS[clusterId] || clusterId; const idSet = filterIds ? new Set(filterIds) : null; @@ -741,6 +1449,209 @@ function showClusterDrill(clusterId, entities, container, filterIds = null) { // ── Main render ─────────────────────────────────────────────────────────────── +// ── Stat-band detail panels (researcher hat: dense, sourced, one-line-per-row) ─ +const CURRENT_YEAR = new Date().getFullYear(); + +function _statPanelEntities(stat, entities) { + if (stat === 'risk') { + return entities + .filter(e => ['high', 'severe'].includes(e.derived?.independence_risk_level)) + .sort((a, b) => (b.derived?.independence_risk_score ?? 0) - (a.derived?.independence_risk_score ?? 0)); + } + if (stat === 'nc') { + return entities + .filter(e => e.operational_status === 'Not_Constituted') + .sort((a, b) => (a.created_year ?? 9999) - (b.created_year ?? 9999)); + } + if (stat === 'abolished') { + return entities + .filter(e => e.operational_status === 'Abolished') + .sort((a, b) => (b.abolished_year ?? 0) - (a.abolished_year ?? 0)); + } + if (stat === 'disposal') { + return entities + .filter(e => e._detail?.case_volume?.disposal_rate != null) + .sort((a, b) => (a._detail.case_volume.disposal_rate) - (b._detail.case_volume.disposal_rate)); + } + return entities; +} + +function _statPanelMeta(stat, list, totalEntities) { + if (stat === 'coverage') return { + title: 'Coverage', + blurb: `${totalEntities} entities mapped against the ~1,500-entity universe (Constitution + central + state + UT). Coverage is sparse for state-level subordinate courts; central and constitutional layers are substantially complete.`, + column: 'By govt level Β· count Β· share', + }; + if (stat === 'risk') return { + title: 'High or severe independence risk', + blurb: 'Algorithmic indicator of structural design β€” not findings of conduct. Drivers include appointer-litigant conflict, executive removal authority without judicial concurrence, opaque appointment criteria, and reappointment dependency.', + column: 'IR score Β· level', + }; + if (stat === 'nc') return { + title: 'Legislated but not constituted', + blurb: 'Entities created by statute but not yet operationalised β€” appellate vacuums where the legislated remedy does not exist in practice. Litigants typically route through writ jurisdiction (HC under Article 226) instead.', + column: 'Legislated Β· years pending', + }; + if (stat === 'abolished') return { + title: 'Abolished', + blurb: 'Entities formally dissolved by statute, gazette notification, or judicial order. Their dissolution typically routed caseload to successor courts (e.g. IPAB β†’ HCs after 2021 Tribunals Reforms Act). Transitional provisions are often the structural research question.', + column: 'Abolished Β· years since', + }; + if (stat === 'disposal') return { + title: 'Case-volume tracked entities Β· worst disposal first', + blurb: 'Ratio of cases disposed to cases filed in the most recent NJDG snapshot. Below 1.0 means backlog is growing. Coverage limited to entities with NJDG data published in this build.', + column: 'Disposal Β· pending', + }; + return { title: '', blurb: '', column: '' }; +} + +function _statRow(stat, e) { + const meta = []; + if (stat === 'risk') { + const lvl = e.derived?.independence_risk_level || 'β€”'; + const sc = e.derived?.independence_risk_score ?? 'β€”'; + meta.push(`${sc}${lvl.toUpperCase()}`); + } else if (stat === 'nc') { + const yr = e.created_year; + const yrsPending = yr ? (CURRENT_YEAR - yr) : null; + meta.push(`${yr || 'β€”'}${yrsPending != null ? `${yrsPending} yr unconstituted` : ''}`); + } else if (stat === 'abolished') { + const yr = e.abolished_year; + const yrsSince = yr ? (CURRENT_YEAR - yr) : null; + meta.push(`${yr || 'β€”'}${yrsSince != null ? `${yrsSince} yr ago` : ''}`); + } else if (stat === 'disposal') { + const cv = e._detail?.case_volume || {}; + const rate = cv.disposal_rate != null ? cv.disposal_rate.toFixed(2) : 'β€”'; + const pending = cv.pending_cases != null ? cv.pending_cases.toLocaleString() : 'β€”'; + meta.push(`${rate}${pending} pending`); + } else if (stat === 'coverage') { + meta.push(`${(e.cluster || '').replace(/_/g, ' ')}${(e.level_of_government || '').replace(/_/g, ' ')}`); + } + return ``; +} + +// Govt-level segmentation for the coverage panel. Order matters: it drives +// stack order in the segmented bar and the legend. +const GOV_LEVELS = [ + { key: 'Central', short: 'C', label: 'Central' }, + { key: 'Shared_CentralState', short: 'C/S', label: 'Shared' }, + { key: 'Shared_MultiState', short: 'M/S', label: 'Multi-state' }, + { key: 'State', short: 'S', label: 'State' }, + { key: 'UT', short: 'UT', label: 'UT' }, +]; + +// Coverage filter state β€” set of selected govt-level keys, or null for "all". +// Lives on the DOM (data-filter-levels) so it survives focus across re-renders +// without needing global state. +function _coverageActiveSet(scope) { + const raw = scope?.dataset?.filterLevels; + if (raw == null || raw === '') return null; + return new Set(raw.split(',').filter(Boolean)); +} + +function _renderCoveragePanel(entities, selected) { + // If `selected` is null we treat all levels as active. + const isActive = (key) => !selected || selected.has(key); + const filtered = entities.filter(e => isActive(e.level_of_government || 'unknown')); + const filteredTotal = filtered.length || 1; // avoid /0 in pct + + // Group filtered entities by cluster. + const byCluster = new Map(); + for (const e of filtered) { + const c = e.cluster || 'unknown'; + if (!byCluster.has(c)) byCluster.set(c, []); + byCluster.get(c).push(e); + } + + const rows = [...byCluster.entries()] + .sort((a, b) => b[1].length - a[1].length) + .map(([clusterId, list]) => { + const total = list.length; + const pct = ((total / filteredTotal) * 100).toFixed(1); + const label = CLUSTER_LABELS[clusterId] || clusterId.replace(/_/g, ' '); + + const counts = {}; + for (const e of list) { + const lv = e.level_of_government || 'unknown'; + counts[lv] = (counts[lv] || 0) + 1; + } + const orderedLevels = GOV_LEVELS.filter(l => counts[l.key]); + const otherCount = total - orderedLevels.reduce((s, l) => s + counts[l.key], 0); + const segments = orderedLevels.map(l => { + const w = (counts[l.key] / total) * 100; + return ``; + }).join('') + (otherCount > 0 + ? `` + : ''); + + const breakdown = orderedLevels.map(l => + `${l.short} ${counts[l.key]}` + ).join(''); + + return ``; + }).join(''); + + // Legend doubles as filter UI. Inactive levels are visibly muted. + const legend = GOV_LEVELS.map(l => { + const active = isActive(l.key); + return ``; + }).join(''); + + // Filter summary: count of entities in current selection vs total. + const filterNote = selected + ? `${filtered.length} of ${entities.length} shown Β· ` + : `Click a level to filter`; + + return `
+
${legend}
+ ${filterNote} +
${rows || '

No entities match the current filter.

'}`; +} + +function renderStatPanel(stat, entities, opts = {}) { + const meta = _statPanelMeta(stat, null, entities.length); + let body; + if (stat === 'coverage') { + body = _renderCoveragePanel(entities, opts.coverageFilter || null); + } else { + const list = _statPanelEntities(stat, entities); + if (!list.length) { + body = '

No matching entities in the current dataset.

'; + } else { + body = list.map(e => _statRow(stat, e)).join(''); + } + } + return ` +
+
+
+

${meta.title}

+

${meta.blurb}

+
+ +
+
+ Entity + ${meta.column} +
+
${body}
+
+ `; +} + export function initSummaryView() { const graph = State.graph; if (!graph) return; @@ -756,6 +1667,8 @@ export function initSummaryView() { ?? entities.filter(e => e.operational_status === 'Not_Constituted').length; const highRiskCount = metrics.high_independence_risk_count ?? entities.filter(e => ['high', 'severe'].includes(e.derived?.independence_risk_level)).length; + const abolishedCount = metrics.abolished_count + ?? entities.filter(e => e.operational_status === 'Abolished').length; container.innerHTML = `
@@ -765,24 +1678,29 @@ export function initSummaryView() {

Structural map of appointment chains, funding flows, independence risk, and systemic gaps across India's judicial ecosystem.

-
-
+
+
-
+ +
-
+ +
- ${avgDisposal ? `
+ + + ${avgDisposal ? `
` : ''} + Avg disposal rate (NJDG-tracked entities) + ` : ''}
+
@@ -792,6 +1710,14 @@ export function initSummaryView() { ${renderSpotlightCarousel(spotlightSet(entities), entities.length)}
+
+
+ Appellate hierarchy +
+

The five-tier appellate spine on the left; a project-wide structural anomaly register on the right β€” every entity flagged by operational status, structural exception, contested data, or documented gap. Click any reference entity or anomaly to open its structural profile.

+
+
+
Structural health distribution @@ -803,13 +1729,39 @@ export function initSummaryView() {
- `; + // ── Render temporal structure ───────────────────────────────────────────── + requestAnimationFrame(() => { + const tsWrap = container.querySelector('#temporal-structure-wrap'); + if (!tsWrap) return; + tsWrap.innerHTML = renderTemporalStructure(); + _wireTemporalStructure(tsWrap, entities); + }); + + // ── Render appellate hierarchy ───────────────────────────────────────────── + requestAnimationFrame(() => { + const ahWrap = container.querySelector('#appellate-hierarchy-wrap'); + if (ahWrap) { + ahWrap.innerHTML = renderAppellateHierarchy(); + ahWrap.addEventListener('click', ev => { + const target = ev.target.closest('.ah-chip, .ah-anom'); + if (!target) return; + const id = target.dataset.entityId; + if (id) State.emit('navigateToDetail', id); + }); + } + }); + // ── Render strip plot (sized to container after DOM paint) ─────────────── requestAnimationFrame(() => { const wrap = container.querySelector('#strip-plot-wrap'); @@ -881,6 +1833,111 @@ export function initSummaryView() { const featured = spotlightSet(entities); wireSpotlightCarousel(container, featured); + // ── Wire stat-band tiles β†’ toggle detail panel ───────────────────────────── + const statBand = container.querySelector('#stat-band'); + const statPanel = container.querySelector('#stat-panel'); + let openStat = null; + const closeStatPanel = () => { + statPanel.hidden = true; + statPanel.innerHTML = ''; + statBand?.querySelectorAll('.stat-item').forEach(b => { + b.setAttribute('aria-expanded', 'false'); + b.classList.remove('stat-item-active'); + }); + openStat = null; + }; + statBand?.addEventListener('click', e => { + const btn = e.target.closest('.stat-item'); + if (!btn) return; + const stat = btn.dataset.stat; + if (stat === openStat) { closeStatPanel(); return; } + statPanel.innerHTML = renderStatPanel(stat, entities); + statPanel.hidden = false; + statBand.querySelectorAll('.stat-item').forEach(b => { + const isActive = b === btn; + b.setAttribute('aria-expanded', isActive ? 'true' : 'false'); + b.classList.toggle('stat-item-active', isActive); + }); + openStat = stat; + // Scroll the panel into view if it ends up offscreen. + requestAnimationFrame(() => { + const r = statPanel.getBoundingClientRect(); + if (r.bottom > window.innerHeight) { + statPanel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + }); + }); + // Coverage-panel-local filter state, kept on the .sp-card dataset for resilience. + const _rerenderCoverage = (card) => { + const selectedRaw = card.dataset.coverageFilter || ''; + const selected = selectedRaw ? new Set(selectedRaw.split(',')) : null; + const fresh = renderStatPanel('coverage', entities, { coverageFilter: selected }); + statPanel.innerHTML = fresh; + // Re-apply dataset to the new card (innerHTML wipes prior dataset). + const newCard = statPanel.querySelector('.sp-card'); + if (newCard) newCard.dataset.coverageFilter = selectedRaw; + }; + + statPanel.addEventListener('click', e => { + if (e.target.closest('.sp-close')) { closeStatPanel(); return; } + + const card = e.target.closest('.sp-card'); + const isCoverage = card?.dataset.stat === 'coverage'; + + // Legend filter toggle + const legendBtn = e.target.closest('.sp-legend-item'); + if (legendBtn && isCoverage) { + e.preventDefault(); + e.stopPropagation(); + const key = legendBtn.dataset.levelKey; + const currentRaw = card.dataset.coverageFilter || ''; + const all = GOV_LEVELS.map(l => l.key); + let selected; + if (!currentRaw) { + // First click: deselect the one clicked = keep all others. + selected = new Set(all.filter(k => k !== key)); + } else { + selected = new Set(currentRaw.split(',')); + if (selected.has(key)) selected.delete(key); else selected.add(key); + } + // If everything is back in: clear filter (null state). If nothing: keep empty set but show empty. + if (selected.size === all.length) { + card.dataset.coverageFilter = ''; + } else { + card.dataset.coverageFilter = [...selected].join(','); + } + _rerenderCoverage(card); + return; + } + + // Reset filter + if (e.target.closest('.sp-filter-reset') && isCoverage) { + e.preventDefault(); + e.stopPropagation(); + card.dataset.coverageFilter = ''; + _rerenderCoverage(card); + return; + } + + // Cluster row β†’ drill overlay, respecting active filter when set + const clusterRow = e.target.closest('.sp-row-cluster-bar'); + if (clusterRow) { + e.preventDefault(); + e.stopPropagation(); + const cid = clusterRow.dataset.clusterId; + const selectedRaw = isCoverage ? (card.dataset.coverageFilter || '') : ''; + if (selectedRaw) { + const sel = new Set(selectedRaw.split(',')); + const ids = entities + .filter(en => en.cluster === cid && sel.has(en.level_of_government || 'unknown')) + .map(en => en.id); + showClusterDrill(cid, entities, container, ids); + } else { + showClusterDrill(cid, entities, container); + } + } + }); + // ── Wire card actions, browse-all, generic entity links ─────────────────── container.addEventListener('click', e => { const reportBtn = e.target.closest('.sp-report'); @@ -913,10 +1970,6 @@ export function initSummaryView() { } }); - // ── Wire "Explore full map" ──────────────────────────────────────────────── - container.querySelector('#btn-explore-map')?.addEventListener('click', () => { - State.emit('navigateToMap', null); - }); } // ── Spotlight carousel state + interaction ─────────────────────────────────── diff --git a/jem/web/styles/main.css b/jem/web/styles/main.css index 3ce1469..7f797a2 100644 --- a/jem/web/styles/main.css +++ b/jem/web/styles/main.css @@ -2514,11 +2514,271 @@ body.jem-resizing-inspector * { .stat-item { flex: 1; - padding: 0 24px; + padding: 4px 24px; border-right: 1px solid var(--jem-faint); + /* Reset button defaults so it behaves like the previous div */ + background: transparent; + text-align: left; + font-family: inherit; + cursor: pointer; + border-top: none; + border-bottom: none; + border-left: none; + position: relative; + transition: background 0.12s; } .stat-item:last-child { border-right: none; padding-right: 0; } .stat-item:first-child { padding-left: 0; } +.stat-item:hover { background: rgba(0,0,0,0.02); } +.stat-item::after { + content: 'β†—'; + position: absolute; + top: 6px; + right: 12px; + font-size: 11px; + color: var(--jem-mute); + opacity: 0; + transition: opacity 0.12s, transform 0.12s; +} +.stat-item:hover::after { opacity: 0.6; } +.stat-item-active { + background: rgba(17, 24, 39, 0.04); +} +.stat-item-active::after { content: 'βˆ’'; opacity: 1; color: var(--jem-ink); } + +/* ── Stat detail panel (researcher-grade list) ───────────────────────────── */ +/* Scoped under .stat-panel so .sp-card here does NOT collide with the + spotlight carousel's .sp-card (which lives at line ~1221). */ +.stat-panel { + margin: -16px 0 24px; +} +.stat-panel .sp-card { + border: 1px solid var(--jem-faint); + border-top: 2px solid var(--jem-ink); + border-radius: 0 0 8px 8px; + background: #fff; + padding: 16px 18px 12px; +} +.sp-head { + display: flex; + align-items: flex-start; + gap: 16px; + margin-bottom: 10px; +} +.sp-head > div:first-child { flex: 1; } +.sp-title { + font-family: Georgia, serif; + font-size: 16px; + font-weight: 600; + color: #111827; + margin: 0 0 4px; +} +.sp-blurb { + font-size: 12px; + color: #4b5563; + line-height: 1.5; + margin: 0; + max-width: 78ch; +} +.sp-close { + background: transparent; + border: 1px solid var(--jem-faint); + border-radius: 50%; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 11px; + color: var(--jem-mute); + flex-shrink: 0; +} +.sp-close:hover { color: var(--jem-ink); border-color: var(--jem-ink); } +.sp-col-head { + display: flex; + gap: 12px; + font-family: var(--font-mono, monospace); + font-size: 9px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--jem-mute); + padding: 6px 10px; + border-top: 1px solid var(--jem-faint); + border-bottom: 1px solid var(--jem-faint); + margin-bottom: 2px; +} +.sp-col-name { flex: 1; } +.sp-col-meta { flex-shrink: 0; min-width: 200px; text-align: right; } +.sp-list { + max-height: 420px; + overflow-y: auto; + display: flex; + flex-direction: column; +} +.sp-row { + display: flex; + align-items: center; + gap: 12px; + padding: 7px 10px; + background: transparent; + border: none; + border-bottom: 1px solid #f1f5f9; + cursor: pointer; + font-family: var(--font-main); + font-size: 12px; + text-align: left; + color: #1f2937; + transition: background 0.1s; +} +.sp-row:last-child { border-bottom: none; } +.sp-row:hover { background: #f9fafb; } +.sp-row-name { flex: 1; font-weight: 500; } +.sp-row-abbr { color: var(--jem-mute); font-weight: 400; } +.sp-row-meta { + flex-shrink: 0; + min-width: 200px; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 8px; + font-family: var(--font-mono, monospace); + font-size: 11px; +} +.sp-row-score { + font-weight: 700; + color: #111827; +} +.sp-row-lvl { + font-size: 9px; + font-weight: 700; + letter-spacing: 0.04em; + padding: 1px 5px; + border-radius: 3px; + background: #f3f4f6; +} +.sp-lvl-severe { background: #ede9fe; color: #6d28d9; } +.sp-lvl-high { background: #fee2e2; color: #b91c1c; } +.sp-lvl-moderate { background: #fef3c7; color: #92400e; } +.sp-lvl-low { background: #d1fae5; color: #047857; } +.sp-row-yr { color: var(--jem-ink); font-weight: 600; } +.sp-row-pending, .sp-row-cluster, .sp-row-gov { color: var(--jem-mute); } +.sp-row-cluster { text-transform: capitalize; } +.sp-row-gov { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; } +.sp-row-cluster-bar { gap: 16px; } +.sp-bar { + display: inline-block; + width: 90px; + height: 5px; + background: #f1f5f9; + border-radius: 3px; + overflow: hidden; +} +.sp-bar-fill { display: block; height: 100%; background: var(--jem-ink); } + +/* Stacked segmented bar (coverage panel β€” govt-level split) */ +.sp-bar-stacked { + display: inline-flex; + width: 110px; + height: 6px; + border-radius: 3px; + overflow: hidden; + background: #f1f5f9; +} +.sp-seg { display: inline-block; height: 100%; } +.sp-seg-central { background: #111827; } +.sp-seg-shared-centralstate { background: #6366f1; } +.sp-seg-shared-multistate { background: #0891b2; } +.sp-seg-state { background: #d97706; } +.sp-seg-ut { background: #16a34a; } +.sp-seg-unknown { background: #cbd5e1; } + +/* Govt-level inline count tags */ +.sp-gov-breakdown { + display: inline-flex; + gap: 4px; + flex-shrink: 0; +} +.sp-gov-tag { + font-family: var(--font-mono, monospace); + font-size: 9px; + font-weight: 700; + padding: 1px 5px; + border-radius: 2px; + letter-spacing: 0.02em; + color: #fff; +} +.sp-gov-central { background: #111827; } +.sp-gov-shared-centralstate { background: #6366f1; } +.sp-gov-shared-multistate { background: #0891b2; } +.sp-gov-state { background: #d97706; } +.sp-gov-ut { background: #16a34a; } + +/* Legend row above the cluster list β€” doubles as filter UI */ +.sp-legend-row { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 6px 10px 10px; + border-bottom: 1px dashed var(--jem-faint); + margin-bottom: 4px; + flex-wrap: wrap; +} +.sp-legend { + display: flex; + gap: 6px; + flex-wrap: wrap; + font-size: 10px; +} +.sp-legend-item { + display: inline-flex; + align-items: center; + gap: 5px; + background: transparent; + border: 1px solid var(--jem-faint); + border-radius: 4px; + padding: 3px 8px; + cursor: pointer; + font-family: var(--font-main); + color: var(--jem-ink); + transition: background 0.1s, border-color 0.1s, opacity 0.1s; +} +.sp-legend-item:hover { border-color: var(--jem-ink); } +.sp-legend-active { background: #f3f4f6; } +.sp-legend-inactive { opacity: 0.4; } +.sp-legend-inactive .sp-legend-swatch { filter: grayscale(0.6); } +.sp-legend-swatch { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 2px; +} +.sp-filter-note { + font-size: 10px; + color: var(--jem-mute); + display: inline-flex; + align-items: center; + gap: 6px; + font-family: var(--font-mono, monospace); +} +.sp-filter-note-mute { color: #9ca3af; font-style: italic; font-family: var(--font-main); } +.sp-filter-reset { + background: transparent; + border: none; + color: var(--jem-signal); + cursor: pointer; + font: inherit; + text-decoration: underline; + padding: 0; +} +.sp-filter-reset:hover { color: var(--jem-ink); } + +/* Coverage rows get a wider meta column to fit the breakdown tags */ +.sp-row-cluster-bar .sp-row-meta { min-width: 320px; } +.sp-col-head:has(+ .sp-list .sp-row-cluster-bar) .sp-col-meta { min-width: 320px; } +.sp-empty { font-size: 12px; color: var(--jem-mute); font-style: italic; padding: 12px 10px; } .stat-num { display: block; @@ -2531,6 +2791,7 @@ body.jem-resizing-inspector * { .stat-denom { font-size: 16px; color: var(--jem-mute); font-weight: 400; } .stat-item-risk .stat-num { color: #dc2626; } .stat-item-nc .stat-num { color: #c2722b; } +.stat-item-abolished .stat-num { color: #6d28d9; } .stat-lbl { font-size: 11px; color: var(--jem-mute); margin-top: 2px; display: block; } /* ── Small multiples ──────────────────────────────────────────────────────── */ @@ -2921,20 +3182,6 @@ body.jem-resizing-inspector * { /* ── Summary footer ───────────────────────────────────────────────────────── */ -.sum-footer { margin-top: 32px; padding-top: 16px; border-top: 1px solid var(--jem-faint); } -.btn-explore-map { - background: none; - border: 1px solid var(--jem-ink); - padding: 6px 16px; - border-radius: 4px; - font-family: var(--font-main); - font-size: 12px; - color: var(--jem-ink); - cursor: pointer; - transition: all 0.15s; -} -.btn-explore-map:hover { background: var(--jem-ink); color: #fff; } - /* ── Command palette search augmentation ──────────────────────────────────── */ .palette-hint { @@ -3005,14 +3252,168 @@ body.jem-resizing-inspector * { } .dv-action-btn.dv-export-pdf:hover { background: #000; } +/* Share button layout β€” icon + label */ +.dv-action-btn.dv-copy-link { + display: inline-flex; + align-items: center; + gap: 6px; +} +.dv-link-icon { display: block; flex-shrink: 0; } + +/* Copy-link button feedback states */ +.dv-action-btn.dv-copy-ok { + background: #ecfdf5; + border-color: #10b981; + color: #047857; +} +.dv-action-btn.dv-copy-err { + background: #fef2f2; + border-color: #ef4444; + color: #b91c1c; +} + +/* Lightweight toast (used for "entity not found" on stale shared links) */ +.jem-toast { + position: fixed; + bottom: 24px; + left: 50%; + transform: translate(-50%, 16px); + background: #111827; + color: #fff; + padding: 10px 16px; + border-radius: 8px; + font-size: 13px; + font-family: var(--font-main); + box-shadow: 0 8px 24px rgba(0,0,0,0.18); + opacity: 0; + transition: opacity 0.2s, transform 0.2s; + z-index: 9999; + pointer-events: none; + max-width: 80vw; +} +.jem-toast-in { opacity: 1; transform: translate(-50%, 0); } +.jem-toast-warn { background: #92400e; } + /* ── Structural health hero ─────────────────────────────────────────────── */ .health-hero { - background: linear-gradient(135deg, var(--h-color) 0%, #ffffff 65%); + background: #ffffff; border: 1px solid #e5e7eb; border-left: 4px solid var(--h-color); border-radius: 10px; padding: 18px 20px; margin-bottom: 14px; + position: sticky; + top: 16px; + z-index: 5; + box-shadow: 0 1px 2px rgba(0,0,0,0.04); +} + +/* ── 1. Summary score card (sticky, not collapsible) ───────────────────── */ +.summary-score-card { + background: #ffffff; + border: 2px solid #d1d5db; + border-left: 4px solid var(--h-color); + border-radius: 12px; + padding: 16px 18px 14px; + margin-bottom: 12px; + position: sticky; + top: 16px; + z-index: 5; + box-shadow: 0 0 0 4px color-mix(in srgb, var(--h-color) 12%, transparent), + 0 2px 6px rgba(0,0,0,0.05); +} + +/* ── 2. Constituent breakdown card ─────────────────────────────────────── */ +.dv-section-constituents .constituent-block { + padding: 10px 0; + border-top: 1px dashed #e5e7eb; +} +.dv-section-constituents .constituent-block:first-child { border-top: none; padding-top: 4px; } +.constituent-head { + display: flex; + align-items: baseline; + justify-content: space-between; + margin-bottom: 6px; + gap: 8px; +} +.constituent-name { + font-size: 12px; + font-weight: 700; + color: #374151; +} +.constituent-score { + font-family: var(--font-mono, monospace); + font-size: 12px; + font-weight: 700; +} +.constituent-bar { + height: 6px; + background: rgba(0,0,0,0.06); + border-radius: 3px; + overflow: hidden; + margin-bottom: 6px; +} +.constituent-fill { height: 100%; border-radius: 3px; } +.constituent-explain { + font-size: 11px; + color: #6b7280; + line-height: 1.4; + margin-bottom: 8px; +} +.constituent-bd { margin-top: 4px; } +.constituent-bd > summary { + list-style: none; + cursor: pointer; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: #6b7280; + display: flex; + align-items: center; + gap: 6px; + padding: 4px 0; +} +.constituent-bd > summary::-webkit-details-marker { display: none; } +.constituent-bd > summary::before { + content: 'β–Έ'; + font-size: 9px; + color: #9ca3af; + transition: transform 0.15s; +} +.constituent-bd[open] > summary::before { transform: rotate(90deg); } +.constituent-bd > summary:hover { color: #111827; } + +/* ── 3. Inline stats in collapsed summary lines (Case volume / clog) ───── */ +.dv-section > summary { justify-content: flex-start; } +.dv-section-summary-stats { + margin-left: auto; + display: inline-flex; + align-items: center; + gap: 8px; + font-weight: 500; + font-size: 11px; + text-transform: none; + letter-spacing: 0; +} +.dv-stat-chip { + display: inline-block; + font-family: var(--font-mono, monospace); + font-size: 10px; + font-weight: 700; + padding: 1px 6px; + border: 1px solid; + border-radius: 3px; + letter-spacing: 0.04em; +} +.dv-stat-mute { color: #6b7280; } + +@media print { + .summary-score-card { + position: static; + box-shadow: none; + border: 1px solid #d1d5db; + } } .health-hero-top { display: flex; align-items: center; gap: 18px; } .health-hero-num { @@ -3100,7 +3501,7 @@ body.jem-resizing-inspector * { .dv-layout { display: grid; - grid-template-columns: 380px 1fr; + grid-template-columns: minmax(420px, 1.05fr) 1.25fr; gap: 32px; align-items: start; } @@ -3108,6 +3509,70 @@ body.jem-resizing-inspector * { .dv-layout { grid-template-columns: 1fr; } } +/* ── Collapsible sections (Profile / Clog / Calculation) ───────────────── */ +.dv-section { + border: 1px solid #e5e7eb; + border-radius: 8px; + margin-bottom: 12px; + background: #fff; +} +.dv-section > summary { + list-style: none; + cursor: pointer; + padding: 10px 14px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + color: #374151; + display: flex; + align-items: center; + gap: 8px; + user-select: none; +} +.dv-section > summary::-webkit-details-marker { display: none; } +.dv-section > summary::before { + content: 'β–Έ'; + font-size: 10px; + color: #9ca3af; + transition: transform 0.15s; +} +.dv-section[open] > summary::before { transform: rotate(90deg); } +.dv-section > summary:hover { background: #f9fafb; } +.dv-section-body { padding: 4px 14px 14px; } + +/* Nested constituent collapsibles (IR / DP) */ +.dv-const-block[open] > summary { color: #111827; } +details.dv-const-block { border-top: 1px solid #f1f5f9; padding: 6px 0; } +details.dv-const-block:first-of-type { border-top: none; } +details.dv-const-block > summary { + list-style: none; + cursor: pointer; + display: flex; + align-items: baseline; + justify-content: space-between; + padding: 6px 4px; + font-size: 12px; + font-weight: 600; + color: #374151; +} +details.dv-const-block > summary::-webkit-details-marker { display: none; } +details.dv-const-block > summary::after { + content: 'β–Έ'; + font-size: 9px; + color: #9ca3af; + margin-left: 6px; + transition: transform 0.15s; +} +details.dv-const-block[open] > summary::after { transform: rotate(90deg); } +@media print { + .dv-section, details.dv-const-block { border: none; } + .dv-section > summary::before, + details.dv-const-block > summary::after { display: none; } + details:not([open]) > *:not(summary) { display: revert !important; } + details { display: block; } +} + /* ── Detail left column ───────────────────────────────────────────────────── */ .dv-header { @@ -3673,6 +4138,689 @@ body[data-app-view="detail"] #zoom-controls { display: none !important; } .strip-bubble:hover { fill-opacity: 1 !important; } .strip-cluster-label:hover { fill: var(--jem-signal) !important; } +/* Timeline section β€” narrative epilogue: cross-sectional analysis above, + historical context below. Soft hairline separator and a serif title style + reinforce the chapter break. */ +.sm-section-timeline { + margin-top: 40px; + padding-top: 28px; + border-top: 1px solid #e5e7eb; + position: relative; +} +.sm-section-timeline::before { + content: ''; + position: absolute; + top: -1px; + left: 50%; + transform: translateX(-50%); + width: 60px; + height: 1px; + background: #111827; +} +.sm-section-timeline .sm-section-title { + font-family: Georgia, serif; + font-weight: 600; + font-size: 14px; +} + +/* ── Temporal structure ──────────────────────────────────────────────────── */ +.ts-wrap { + background: #fff; + border: 1px solid #e5e7eb; + border-radius: 10px; + padding: 14px 16px 12px; + width: max-content; + max-width: 100%; +} +.ts-meta { + display: flex; + justify-content: space-between; + gap: 12px; + font-size: 12px; + color: #4b5563; + padding-bottom: 8px; + flex-wrap: wrap; +} +.ts-meta-num { + font-family: var(--font-mono, monospace); + font-weight: 700; + color: #111827; +} +.ts-meta-hint { color: #9ca3af; font-size: 11px; } +.ts-status { + display: inline-flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} +.ts-bucket-info { + font-family: var(--font-mono, monospace); + font-size: 10px; + color: var(--jem-mute); + letter-spacing: 0.02em; +} +/* Preset era pills */ +.ts-presets { + display: flex; + gap: 6px; + flex-wrap: wrap; + padding: 8px 0 12px; + border-bottom: 1px dashed #e5e7eb; + margin-bottom: 6px; +} +.ts-preset { + background: #fff; + border: 1px solid #d1d5db; + border-radius: 999px; + padding: 3px 11px; + font-size: 11px; + font-weight: 600; + font-family: var(--font-main); + color: #374151; + cursor: pointer; + transition: all 0.1s; +} +.ts-preset:hover { border-color: #111827; color: #111827; } +.ts-preset-active { + background: #111827; + color: #fff; + border-color: #111827; +} +/* Range rail */ +.ts-rail-line { stroke-width: 3; stroke-linecap: round; } +.ts-rail-full { stroke: #e5e7eb; } +.ts-rail-range { stroke: #111827; } +.ts-rail-end { + font-family: var(--font-mono, monospace); + font-size: 9px; + fill: #9ca3af; +} +.ts-handle { + fill: #fff; + stroke: #111827; + stroke-width: 2; + cursor: ew-resize; + transition: r 0.1s; +} +.ts-handle:hover { r: 7; fill: #f3f4f6; } +.ts-scrub-focus-line { stroke: #111827; stroke-width: 1; stroke-dasharray: 2 3; pointer-events: none; } +/* Tooltip */ +.ts-tip-bg { + fill: #111827; + stroke: none; + height: 26px; +} +.ts-tip-text { + fill: #ffffff; + font-family: var(--font-mono, monospace); + font-size: 11px; + font-weight: 600; +} +.ts-legend-hint { + margin-left: auto; + font-style: italic; + color: #9ca3af; + font-size: 10px; +} +.ts-svg-wrap { + overflow: hidden; + width: 760px; + max-width: 100%; + padding: 4px 0; +} +.ts-svg { + display: block; + width: 760px; + max-width: 100%; + height: auto; + cursor: ew-resize; + overflow: visible; +} +.ts-axis-dir { + font-family: var(--font-main); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + fill: #6b7280; +} +.ts-axis-dir-up { fill: #475569; } +.ts-axis-dir-down { fill: #111827; } +.ts-axis-tick { + font-family: var(--font-mono, monospace); + font-size: 10px; + font-weight: 700; + fill: #6b7280; +} +.ts-axis-line { + stroke: #cbd5e1; + stroke-width: 1; +} +.ts-bar { cursor: pointer; transition: opacity 0.12s; } +.ts-bar-birth { fill: #94a3b8; } +.ts-bar-death { fill: #111827; } +.ts-bar:hover { opacity: 0.7; } +.ts-bar-label { + font-family: var(--font-mono, monospace); + font-size: 10px; + fill: #6b7280; + letter-spacing: 0.04em; + pointer-events: none; +} +.ts-bar-count { + font-family: var(--font-mono, monospace); + font-size: 9px; + font-weight: 700; + pointer-events: none; +} +.ts-bar-count-birth { fill: #475569; } +.ts-bar-count-death { fill: #111827; } +.ts-axis-label { + font-family: var(--font-mono, monospace); + font-size: 9px; + fill: #9ca3af; + letter-spacing: 0.04em; +} +.ts-event { cursor: pointer; } +.ts-event circle { transition: r 0.12s; } +.ts-event:hover circle { r: 6; } +.ts-scrub-thumb { cursor: ew-resize; } +.ts-scrub-focus-line { pointer-events: none; } +.ts-scrub-year { + font-family: var(--font-mono, monospace); + font-size: 11px; + font-weight: 700; + fill: #111827; + pointer-events: none; +} +.ts-legend { + display: flex; + gap: 12px; + flex-wrap: wrap; + align-items: center; + padding: 6px 0 10px; + border-top: 1px dashed #e5e7eb; + margin-top: 4px; + font-size: 10px; + color: var(--jem-mute); + letter-spacing: 0.02em; +} +.ts-evl { display: inline-flex; align-items: center; gap: 5px; text-transform: capitalize; } +.ts-evl-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; +} + +/* Focus panel β€” surfaces entities born/abolished near the scrubbed year */ +.ts-focus-panel { + border-top: 1px solid var(--jem-faint); + padding-top: 10px; + margin-top: 4px; +} +.ts-focus-empty { + font-size: 12px; + color: var(--jem-mute); + font-style: italic; + padding: 6px 0; + margin: 0; +} +.ts-focus-head { + display: flex; + align-items: baseline; + gap: 10px; + margin-bottom: 8px; + flex-wrap: wrap; +} +.ts-focus-year { + font-family: Georgia, serif; + font-size: 22px; + font-weight: 700; + color: #111827; + line-height: 1; +} +.ts-focus-window { + font-size: 10px; + color: var(--jem-mute); + font-family: var(--font-mono, monospace); + letter-spacing: 0.04em; +} +.ts-focus-counts { + margin-left: auto; + display: inline-flex; + gap: 8px; +} +.ts-focus-count { + font-family: var(--font-mono, monospace); + font-size: 10px; + font-weight: 700; + padding: 2px 8px; + border-radius: 3px; +} +.ts-focus-count-birth { background: #e2e8f0; color: #1f2937; } +.ts-focus-count-death { background: #111827; color: #fff; } +.ts-focus-events { + display: flex; + flex-direction: column; + gap: 4px; + margin-bottom: 10px; +} +.ts-focus-event { + font-size: 11px; + padding: 4px 8px; + background: #f9fafb; + border-left: 3px solid #9ca3af; + color: #374151; + border-radius: 0 4px 4px 0; +} +.ts-focus-row { + display: flex; + gap: 10px; + align-items: flex-start; + margin-bottom: 6px; +} +.ts-focus-row-label { + font-family: var(--font-mono, monospace); + font-size: 9px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--jem-mute); + padding-top: 4px; + flex-shrink: 0; + min-width: 70px; +} +.ts-focus-chips { + display: flex; + flex-wrap: wrap; + gap: 4px; + flex: 1; +} +.ts-chip { + background: #fff; + border: 1px solid #d1d5db; + border-radius: 4px; + padding: 3px 8px; + font-size: 11px; + font-weight: 600; + font-family: var(--font-main); + color: #1f2937; + cursor: pointer; + display: inline-flex; + align-items: baseline; + gap: 5px; + white-space: nowrap; +} +.ts-chip:hover { border-color: var(--jem-signal); color: var(--jem-signal); } +.ts-chip-birth { border-left: 2px solid #94a3b8; } +.ts-chip-death { border-left: 2px solid #111827; } +.ts-chip-yr { + font-family: var(--font-mono, monospace); + font-size: 9px; + color: var(--jem-mute); + font-weight: 500; +} + +/* ── Appellate hierarchy (summary view) β€” spine + anomalies ──────────────── */ +.ah-wrap { + background: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 10px; + padding: 16px 18px; +} +.ah-meta { + font-size: 12px; + color: #4b5563; + padding-bottom: 12px; + border-bottom: 1px solid #f1f5f9; + margin-bottom: 14px; +} +.ah-meta-num { + font-family: var(--font-mono, monospace); + font-weight: 700; + color: #111827; +} +.ah-grid { + display: grid; + grid-template-columns: 1.6fr 1fr; + gap: 24px; + align-items: start; +} +@media (max-width: 860px) { + .ah-grid { grid-template-columns: 1fr; } +} +.ah-col-head { + font-family: Georgia, serif; + font-size: 14px; + font-weight: 600; + color: #111827; + margin-bottom: 12px; + padding-bottom: 6px; + border-bottom: 1px solid #e5e7eb; + letter-spacing: 0.01em; +} +.ah-col-sub { + font-size: 11px; + color: #6b7280; + line-height: 1.45; + margin: -6px 0 12px; +} + +/* ── Spine ─────────────────────────────────────────────────────────────── */ +.ah-spine { position: relative; } +.ah-tier-row { + display: grid; + grid-template-columns: 32px 1fr; + gap: 12px; + padding: 12px 0; + position: relative; +} +.ah-tier-row::before { + content: ''; + position: absolute; + left: 15px; + top: 28px; + bottom: -8px; + width: 1px; + background: #e5e7eb; +} +.ah-tier-row:last-child::before { display: none; } +.ah-tier-num { + font-family: var(--font-mono, monospace); + font-size: 11px; + font-weight: 700; + color: #6b7280; + background: #fff; + border: 1px solid #d1d5db; + border-radius: 50%; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + z-index: 1; +} +.ah-tier-body { min-width: 0; } +.ah-tier-head { + display: flex; + align-items: baseline; + gap: 8px; + flex-wrap: wrap; +} +.ah-tier-name { + font-size: 13px; + font-weight: 700; + color: #111827; + font-family: Georgia, serif; +} +.ah-tier-count { + font-family: var(--font-mono, monospace); + font-size: 11px; + color: #6b7280; +} +.ah-tier-note { + font-size: 11px; + color: #6b7280; + line-height: 1.45; + margin: 4px 0 8px; + font-style: italic; +} +.ah-tier-refs { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} +.ah-chip { + background: #fff; + border: 1px solid #d1d5db; + border-radius: 4px; + padding: 3px 9px; + font-size: 11px; + font-weight: 600; + color: #1f2937; + font-family: var(--font-main); + cursor: pointer; + white-space: nowrap; + display: inline-flex; + align-items: center; + gap: 4px; +} +.ah-chip:hover { + border-color: var(--jem-signal); + color: var(--jem-signal); +} +.ah-flag { + display: inline-block; + font-family: var(--font-mono, monospace); + font-size: 9px; + font-weight: 700; + background: #fef3c7; + color: #92400e; + padding: 2px 7px; + border-radius: 3px; + margin-left: 4px; + line-height: 1.2; + vertical-align: middle; + letter-spacing: 0.02em; +} +.ah-flag-blocked { background: #fee2e2; color: #991b1b; } +.ah-flag-abolished { background: #ede9fe; color: #5b21b6; } +.ah-chip-abolished { + background: #faf5ff; + border-color: #c4b5fd; + color: #5b21b6; +} +.ah-chip-abolished:hover { border-color: #6d28d9; color: #4c1d95; } +.ah-chip-abolished .ah-chip-label { + text-decoration: line-through; + text-decoration-color: #a78bfa; +} +.ah-tier-more-wrap { display: inline-block; } +.ah-tier-more { + font-size: 11px; + color: #6b7280; + cursor: pointer; + list-style: none; + padding: 3px 9px; + border: 1px dashed #d1d5db; + border-radius: 4px; + font-family: var(--font-main); + background: transparent; +} +.ah-tier-more::-webkit-details-marker { display: none; } +.ah-tier-more:hover { color: var(--jem-signal); border-color: var(--jem-signal); } +.ah-tier-rest { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 6px; +} + +/* ── Anomalies sidebar β€” grouped, collapsible, scrollable ─────────────── */ +.ah-anomalies { + padding-top: 0; + max-height: 560px; + overflow-y: auto; + padding-right: 4px; +} +.ah-anom-group { + border: 1px solid #e5e7eb; + border-radius: 6px; + margin-bottom: 6px; + background: #fff; + border-left-width: 3px; + border-left-style: solid; +} +.ah-anom-sev-critical { border-left-color: #dc2626; } +.ah-anom-sev-abolished { border-left-color: #6d28d9; } +.ah-anom-sev-high { border-left-color: #f59e0b; } +.ah-anom-sev-moderate { border-left-color: #ca8a04; } +.ah-anom-group-head { + list-style: none; + cursor: pointer; + display: flex; + align-items: center; + gap: 10px; + padding: 8px 12px; + user-select: none; +} +.ah-anom-group-head::-webkit-details-marker { display: none; } +.ah-anom-group-head::before { + content: 'β–Έ'; + font-size: 10px; + color: #9ca3af; + transition: transform 0.15s; + flex-shrink: 0; +} +.ah-anom-group[open] .ah-anom-group-head::before { transform: rotate(90deg); } +.ah-anom-group-head:hover { background: #f9fafb; } +.ah-anom-group-tag { + font-family: var(--font-mono, monospace); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + color: #111827; + flex: 1; +} +.ah-anom-sev-critical .ah-anom-group-tag { color: #991b1b; } +.ah-anom-sev-abolished .ah-anom-group-tag { color: #5b21b6; } +.ah-anom-sev-high .ah-anom-group-tag { color: #92400e; } +.ah-anom-sev-moderate .ah-anom-group-tag { color: #854d0e; } + +/* Abolished anomaly rows β€” struck-through name to read as "dissolved" */ +.ah-anom-sev-abolished .ah-anom-name { + text-decoration: line-through; + text-decoration-color: #c4b5fd; +} +.ah-anom-sev-abolished .ah-anom { color: #4b5563; } +.ah-anom-sev-abolished .ah-anom:hover { background: #faf5ff; color: #5b21b6; } +.ah-anom-sev-abolished .ah-anom:hover .ah-anom-name { text-decoration-color: #6d28d9; } +.ah-anom-group-count { + font-family: var(--font-mono, monospace); + font-size: 11px; + font-weight: 700; + background: #f3f4f6; + color: #374151; + padding: 1px 7px; + border-radius: 10px; +} +.ah-anom-list { + padding: 2px 8px 8px; + display: flex; + flex-direction: column; + gap: 2px; +} +.ah-anom { + background: transparent; + border: none; + border-radius: 4px; + padding: 5px 8px; + cursor: pointer; + font-family: var(--font-main); + font-size: 12px; + color: #1f2937; + text-align: left; + display: inline-flex; + width: max-content; + max-width: 100%; + align-self: flex-start; + align-items: center; + gap: 8px; + transition: background 0.1s; +} +.ah-anom-name { flex: 0 1 auto; } +.ah-anom:hover { + background: #f3f4f6; + color: var(--jem-signal); +} +.ah-anom-name { + font-weight: 500; + line-height: 1.3; + flex: 1; +} +.ah-anom-fact { + font-family: var(--font-mono, monospace); + font-size: 10px; + font-weight: 500; + color: #6b7280; + flex-shrink: 0; + white-space: nowrap; +} +.ah-anom:hover .ah-anom-fact { color: #374151; } +.ah-anom-empty { + font-size: 11px; + color: #9ca3af; + font-style: italic; +} + +/* ── "Where this fits" appellate strip (detail view) ─────────────────────── */ +.wtf-card { + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 8px; + padding: 10px 12px; + margin-bottom: 12px; +} +.wtf-head { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; + margin-bottom: 8px; +} +.wtf-label { + font-size: 11px; + font-weight: 700; + letter-spacing: 0.05em; + text-transform: uppercase; + color: #374151; +} +.wtf-hint { font-size: 10px; color: #9ca3af; } +.wtf-strip { + display: flex; + align-items: center; + gap: 4px; + overflow-x: auto; + padding-bottom: 4px; +} +.wtf-tier { + display: inline-flex; + align-items: center; + gap: 4px; +} +.wtf-sib-sep { color: #cbd5e1; margin: 0 2px; } +.wtf-node { + background: #fff; + border: 1px solid #d1d5db; + border-radius: 4px; + padding: 3px 8px; + font-size: 11px; + font-weight: 600; + color: #1f2937; + font-family: var(--font-main); + cursor: pointer; + white-space: nowrap; +} +.wtf-node:hover { border-color: var(--jem-signal); color: var(--jem-signal); } +.wtf-node-focal { + background: #111827; + color: #fff; + border-color: #111827; + cursor: default; +} +.wtf-node-focal:hover { background: #111827; color: #fff; } +.wtf-arrow { + color: #9ca3af; + font-size: 14px; + font-weight: 700; + padding: 0 2px; + flex-shrink: 0; +} + /* Hide the old sm-grid (no longer used) */ .sm-grid { display: none; }