// USA Map — true interactive 3D, both views, driven by real Medicaid data.
// Country view: Three.js extruded states (flat until hovered; play = sweep).
// Drill view: Three.js extruded counties for the pilot states (MI, MN, WI, PA),
// heights = real Medicaid paid dollars per county per year.

const USA_TOPO_URL = 'vendor/counties-10m.json';
const WORLD_TOPO_URL = 'vendor/land-110m.json';

const STATE_ABBR = {
  'Alabama':'AL','Alaska':'AK','Arizona':'AZ','Arkansas':'AR','California':'CA',
  'Colorado':'CO','Connecticut':'CT','Delaware':'DE','District of Columbia':'DC',
  'Florida':'FL','Georgia':'GA','Hawaii':'HI','Idaho':'ID','Illinois':'IL',
  'Indiana':'IN','Iowa':'IA','Kansas':'KS','Kentucky':'KY','Louisiana':'LA',
  'Maine':'ME','Maryland':'MD','Massachusetts':'MA','Michigan':'MI','Minnesota':'MN',
  'Mississippi':'MS','Missouri':'MO','Montana':'MT','Nebraska':'NE','Nevada':'NV',
  'New Hampshire':'NH','New Jersey':'NJ','New Mexico':'NM','New York':'NY',
  'North Carolina':'NC','North Dakota':'ND','Ohio':'OH','Oklahoma':'OK','Oregon':'OR',
  'Pennsylvania':'PA','Rhode Island':'RI','South Carolina':'SC','South Dakota':'SD',
  'Tennessee':'TN','Texas':'TX','Utah':'UT','Vermont':'VT','Virginia':'VA',
  'Washington':'WA','West Virginia':'WV','Wisconsin':'WI','Wyoming':'WY',
};

const PILOT_NEIGHBORS = {
  'Michigan':     ['Wisconsin','Indiana','Ohio','Illinois','Minnesota'],
  'Minnesota':    ['Wisconsin','Iowa','South Dakota','North Dakota','Michigan'],
  'Wisconsin':    ['Michigan','Minnesota','Iowa','Illinois'],
  'Pennsylvania': ['New York','New Jersey','Ohio','West Virginia','Maryland','Delaware'],
};

function fwIsPilot(stateName) {
  return !!(stateName && window.FW_PILOT && window.FW_PILOT.has(window.STATE_ABBR_BY_NAME[stateName]));
}

function USAMap({ year, playing, zoomedState, activeRegion, onStateClick, onYear, onPlayingChange, onBack }) {
  const [topo, setTopo] = React.useState(null);
  const [worldTopo, setWorldTopo] = React.useState(null);

  React.useEffect(() => {
    fetch(USA_TOPO_URL).then(r => r.json()).then(setTopo).catch(console.error);
    fetch(WORLD_TOPO_URL).then(r => r.json()).then(setWorldTopo).catch(() => {});
  }, []);

  const geo = React.useMemo(() => {
    if (!topo || !window.topojson) return null;
    const statesFC = window.topojson.feature(topo, topo.objects.states);
    const countiesFC = window.topojson.feature(topo, topo.objects.counties);
    let worldLand = null;
    if (worldTopo) worldLand = window.topojson.feature(worldTopo, worldTopo.objects.land);
    return { statesFC, countiesFC, worldLand };
  }, [topo, worldTopo]);

  const drill = React.useMemo(() => {
    if (!geo || !zoomedState || !fwIsPilot(zoomedState)) return null;
    const fips = window.STATE_FIPS_BY_NAME[zoomedState];
    const counties = {
      type: 'FeatureCollection',
      features: geo.countiesFC.features.filter(f => String(f.id).startsWith(fips)),
    };
    const NB = new Set([zoomedState, ...(PILOT_NEIGHBORS[zoomedState] || [])]);
    const neighbors = {
      type: 'FeatureCollection',
      features: geo.statesFC.features.filter(f => NB.has(f.properties.name)),
    };
    return { counties, neighbors };
  }, [geo, zoomedState]);

  if (!topo || !geo) {
    return <div style={{ width:'100%', height:'100%', display:'flex',
      alignItems:'center', justifyContent:'center',
      color:'#ff3030', fontFamily:'ui-monospace, monospace',
      fontSize:11, letterSpacing:2 }}>LOADING BASEMAP…</div>;
  }

  if (drill) {
    return <StateDrillThree key={zoomedState} stateName={zoomedState} counties={drill.counties}
      neighbors={drill.neighbors} year={year} playing={playing}
      onYear={onYear} onPlayingChange={onPlayingChange} onBack={onBack}/>;
  }

  return <CountryThree statesFC={geo.statesFC} worldLand={geo.worldLand} year={year} playing={playing}
    activeRegion={activeRegion} onStateClick={onStateClick} onPlayingChange={onPlayingChange}/>;
}

/* ---------------- Country — Three.js extruded states ----------------
   States start FLAT. A single state grows while hovered. The play button
   triggers a sweep animation that grows all states in sequence, then
   settles them back to flat. Heights = real Medicaid paid $ per year. */
function CountryThree({ statesFC, worldLand, year, playing, activeRegion, onStateClick, onPlayingChange }) {
  const mountRef = React.useRef(null);
  const labelsRef = React.useRef(null);
  const [hover, setHover] = React.useState(null);
  const activeRegionRef = React.useRef(activeRegion);
  React.useEffect(() => { activeRegionRef.current = activeRegion; }, [activeRegion]);
  const onStateClickRef = React.useRef(onStateClick);
  React.useEffect(() => { onStateClickRef.current = onStateClick; }, [onStateClick]);
  const onPlayingChangeRef = React.useRef(onPlayingChange);
  React.useEffect(() => { onPlayingChangeRef.current = onPlayingChange; }, [onPlayingChange]);
  const yearRef = React.useRef(year);
  React.useEffect(() => { yearRef.current = year; }, [year]);
  const sweepRef = React.useRef({ start: 0, running: false });
  React.useEffect(() => {
    if (playing) { sweepRef.current = { start: performance.now(), running: true }; }
    else { sweepRef.current.running = false; }
  }, [playing]);

  React.useEffect(() => {
    const mount = mountRef.current;
    const labelsEl = labelsRef.current;
    if (!mount || !window.THREE) return;
    const THREE = window.THREE;

    const width = mount.clientWidth;
    const height = mount.clientHeight;

    const proj = d3.geoAlbersUsa().fitExtent([[-1.15, -0.6], [1.15, 0.6]], statesFC);

    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x02060f);
    scene.fog = new THREE.Fog(0x02060f, 5.0, 12);

    const camera = new THREE.PerspectiveCamera(42, width / height, 0.05, 50);
    camera.up.set(0, 1, 0);
    camera.position.set(0, 1.6, 1.3);
    camera.lookAt(0, 0, 0);

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
    renderer.setSize(width, height);
    renderer.setClearColor(0x000000, 0);
    mount.appendChild(renderer.domElement);

    scene.add(new THREE.AmbientLight(0x886666, 0.75));
    const key = new THREE.DirectionalLight(0xffcccc, 1.25);
    key.position.set(-2, 4, 3); scene.add(key);
    const rim = new THREE.DirectionalLight(0xffaaaa, 0.55);
    rim.position.set(3, 2, -2); scene.add(rim);
    const fill = new THREE.DirectionalLight(0xffffff, 0.35);
    fill.position.set(0, 5, 0); scene.add(fill);

    const plate = new THREE.Mesh(
      new THREE.PlaneGeometry(20, 12),
      new THREE.MeshStandardMaterial({ color: 0x040a16, roughness: 0.95, metalness: 0.0 })
    );
    plate.rotation.x = -Math.PI / 2;
    plate.position.y = -0.008;
    scene.add(plate);

    const mapGroup = new THREE.Group();
    mapGroup.rotation.x = -Math.PI / 2;
    scene.add(mapGroup);

    const buildShape = (ring) => {
      const s = new THREE.Shape();
      ring.forEach(([x, y], i) => {
        const p = proj([x, y]);
        if (!p) return;
        const px = p[0], py = -p[1];
        if (i === 0) s.moveTo(px, py); else s.lineTo(px, py);
      });
      return s;
    };

    const MAXY = window.FW_MAX_YEAR;
    const maxVal = Math.max(...Object.values(STATE_HEIGHTS).map(o => o[MAXY] || 0));
    const maxHeightFor = (name) => {
      const s = STATE_HEIGHTS[name];
      if (!s) return 0.25;
      return 0.08 + ((s[yearRef.current] || 0) / maxVal) * 0.45;
    };
    const litFor = (name) =>
      !activeRegionRef.current || window.fwRegionOf(name) === activeRegionRef.current;
    const colorFor = (name) => {
      if (!litFor(name)) return new THREE.Color(0.10, 0.045, 0.05);  // dimmed / off-region
      const s = STATE_HEIGHTS[name];
      if (!s) return new THREE.Color(0.32, 0.12, 0.14);
      const intensity = Math.min(1, (s[yearRef.current] || 0) / maxVal);
      return new THREE.Color(
        0.38 + intensity * 0.55,
        0.12 + intensity * 0.08,
        0.14 + intensity * 0.08,
      );
    };

    const stateMeshes = [];
    const stateByName = new Map();
    const labelPositions = [];

    statesFC.features.forEach(f => {
      const name = f.properties.name;
      const maxH = maxHeightFor(name);
      const base = colorFor(name);
      const geom = f.geometry;
      const polys = geom.type === 'Polygon' ? [geom.coordinates] : geom.coordinates;

      const centroidAcc = { x: 0, y: 0, n: 0 };
      const stateGroup = { meshes: [], maxH, targetScale: 0.0001, currentScale: 0.0001 };

      polys.forEach(poly => {
        const outer = poly[0];
        if (!outer || outer.length < 3) return;
        const shape = buildShape(outer);
        for (let i = 1; i < poly.length; i++) {
          const hole = new THREE.Path();
          poly[i].forEach(([x, y], k) => {
            const p = proj([x, y]);
            if (!p) return;
            const px = p[0], py = -p[1];
            if (k === 0) hole.moveTo(px, py); else hole.lineTo(px, py);
          });
          shape.holes.push(hole);
        }
        const extrude = new THREE.ExtrudeGeometry(shape, {
          depth: maxH, bevelEnabled: true, bevelSize: 0.0015, bevelThickness: 0.0015, bevelSegments: 1,
        });
        extrude.computeVertexNormals();
        const mat = new THREE.MeshStandardMaterial({
          color: base, roughness: 0.55, metalness: 0.18,
          emissive: base.clone().multiplyScalar(0.12),
        });
        const mesh = new THREE.Mesh(extrude, mat);
        mesh.userData = { name, maxH, baseColor: base.clone(), mat };
        mesh.scale.z = 0.0001;
        mapGroup.add(mesh);
        stateMeshes.push(mesh);
        stateGroup.meshes.push(mesh);

        const edgeGeom = new THREE.EdgesGeometry(extrude, 22);
        const edges = new THREE.LineSegments(edgeGeom,
          new THREE.LineBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.55 }));
        mesh.add(edges);

        outer.forEach(([x, y]) => {
          const p = proj([x, y]);
          if (!p) return;
          centroidAcc.x += p[0]; centroidAcc.y += -p[1]; centroidAcc.n++;
        });
      });
      if (centroidAcc.n > 0) {
        const cx = centroidAcc.x / centroidAcc.n;
        const cy = centroidAcc.y / centroidAcc.n;
        const proxy = new THREE.Object3D();
        proxy.position.set(cx, cy, 0.01);
        mapGroup.add(proxy);
        labelPositions.push({ name, proxy, maxH });
        stateByName.set(name, { ...stateGroup, centroid: { cx, cy }, proxy });
      }
    });

    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.08;
    controls.minDistance = 1.0;
    controls.maxDistance = 6.5;
    controls.maxPolarAngle = Math.PI * 0.48;
    controls.minPolarAngle = 0.05;
    controls.enablePan = true;
    controls.screenSpacePanning = true;
    controls.target.set(0, 0, 0);
    controls.update();

    // Frame a region (or the whole country when null), keeping the view angle.
    const focusRegion = (key) => {
      mapGroup.updateMatrixWorld(true);
      let target = new THREE.Vector3(0, 0, 0);
      const names = key ? window.fwRegionStates(key).filter(n => stateByName.has(n)) : [];
      if (names.length) {
        let ax = 0, ay = 0;
        names.forEach(n => { const c = stateByName.get(n).centroid; ax += c.cx; ay += c.cy; });
        target = new THREE.Vector3(ax / names.length, ay / names.length, 0).applyMatrix4(mapGroup.matrixWorld);
      }
      const offset = key ? new THREE.Vector3(0, 0.82, 0.70) : new THREE.Vector3(0, 1.6, 1.3);
      controls.target.copy(target);
      camera.position.copy(target).add(offset);
      controls.update();
    };
    focusRegion(activeRegionRef.current);
    let currentRegion = activeRegionRef.current;

    const ray = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    let hoveredMesh = null;

    const onPointerMove = (e) => {
      const rect = renderer.domElement.getBoundingClientRect();
      mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
    };
    renderer.domElement.addEventListener('pointermove', onPointerMove);

    let downX = 0, downY = 0;
    const onPointerDown = (e) => { downX = e.clientX; downY = e.clientY; };
    const onPointerUp = (e) => {
      const dx = Math.abs(e.clientX - downX), dy = Math.abs(e.clientY - downY);
      if (dx + dy > 4) return;
      ray.setFromCamera(mouse, camera);
      const hits = ray.intersectObjects(stateMeshes, false);
      const hit = hits[0]?.object;
      if (hit && onStateClickRef.current) {
        const nm = hit.userData.name;
        if (litFor(nm) || fwIsPilot(nm)) onStateClickRef.current(nm);
      }
    };
    renderer.domElement.addEventListener('pointerdown', onPointerDown);
    renderer.domElement.addEventListener('pointerup', onPointerUp);

    const labelNodes = new Map();
    labelPositions.forEach(lp => {
      const el = document.createElement('div');
      el.className = 'state-label';
      el.textContent = STATE_ABBR[lp.name] || lp.name.slice(0, 2).toUpperCase();
      el.style.cssText = [
        'position:absolute','pointer-events:none','transform:translate(-50%,-50%)',
        'font-family:"Space Grotesk", sans-serif','font-weight:800',
        'font-size:11px','letter-spacing:2px',
        'color:rgba(255,120,120,0.32)','text-shadow:0 0 6px rgba(0,0,0,0.9)',
        'transition:color .15s, font-size .15s, opacity .15s','will-change:transform,left,top',
      ].join(';');
      labelsEl.appendChild(el);
      labelNodes.set(lp.name, el);
    });

    const v = new THREE.Vector3();
    const projectToScreen = (obj, targetVec) => {
      obj.getWorldPosition(v);
      v.project(camera);
      const rect = renderer.domElement.getBoundingClientRect();
      targetVec.x = (v.x * 0.5 + 0.5) * rect.width;
      targetVec.y = (-v.y * 0.5 + 0.5) * rect.height;
      targetVec.z = v.z;
    };

    let currentYear = yearRef.current;
    let currentHover = null;
    const tmp = new THREE.Vector3();
    const sweepOrder = [...stateByName.keys()].sort((a, b) => {
      const av = STATE_HEIGHTS[a]?.[MAXY] || 0;
      const bv = STATE_HEIGHTS[b]?.[MAXY] || 0;
      return bv - av;
    });
    const SWEEP_MS = 4500;
    const smoothstep = t => t < 0.5 ? 2*t*t : 1 - Math.pow(-2*t+2, 2)/2;

    let raf;
    const tick = () => {
      controls.update();

      if (currentYear !== yearRef.current) {
        currentYear = yearRef.current;
        stateByName.forEach((grp, name) => {
          const newMaxH = maxHeightFor(name);
          const base = colorFor(name);
          grp.yearFactor = newMaxH / grp.maxH;
          grp.meshes.forEach(m => {
            m.userData.baseColor.copy(base);
            m.material.color.copy(base);
            m.material.emissive.copy(base).multiplyScalar(0.12);
          });
        });
      }

      if (currentRegion !== activeRegionRef.current) {
        currentRegion = activeRegionRef.current;
        stateByName.forEach((grp, name) => {
          const base = colorFor(name);
          grp.meshes.forEach(m => {
            m.userData.baseColor.copy(base);
            m.material.color.copy(base);
            m.material.emissive.copy(base).multiplyScalar(0.12);
          });
        });
        focusRegion(currentRegion);
        currentHover = null; setHover(null);
      }

      const sweep = sweepRef.current;
      let sweepProgress = null;
      if (sweep.running) {
        const elapsed = performance.now() - sweep.start;
        sweepProgress = Math.min(1, elapsed / SWEEP_MS);
        if (sweepProgress >= 1) {
          sweep.running = false;
          if (onPlayingChangeRef.current) onPlayingChangeRef.current(false);
        }
      }

      stateByName.forEach((grp, name) => {
        const yearFactor = grp.yearFactor || 1;
        let target = 0.0001;

        if (sweepProgress !== null && litFor(name)) {
          const idx = sweepOrder.indexOf(name);
          const total = sweepOrder.length;
          const startT = (idx / total) * 0.55;
          const growDur = 0.20;
          const holdDur = 0.15;
          const localT = sweepProgress - startT;
          let sweepScale = 0;
          if (localT <= 0) sweepScale = 0;
          else if (localT < growDur) sweepScale = smoothstep(localT / growDur);
          else if (localT < growDur + holdDur) sweepScale = 1;
          else {
            const fallT = Math.min(1, (localT - growDur - holdDur) / (1 - growDur - holdDur - startT));
            sweepScale = 1 - smoothstep(Math.max(0, Math.min(1, fallT)));
          }
          target = Math.max(target, sweepScale * yearFactor);
        }

        if (name === currentHover) {
          target = Math.max(target, yearFactor);
        }

        grp.targetScale = target;
        const k = 0.18;
        grp.currentScale += (target - grp.currentScale) * k;
        const s = Math.max(0.0001, grp.currentScale);
        grp.meshes.forEach(m => { m.scale.z = s; });
        grp.proxy.position.z = grp.maxH * s + 0.015;
      });

      ray.setFromCamera(mouse, camera);
      const hits = ray.intersectObjects(stateMeshes, false);
      let newHover = hits[0]?.object || null;
      if (newHover && !litFor(newHover.userData.name)) newHover = null;
      if (newHover !== hoveredMesh) {
        if (hoveredMesh) {
          hoveredMesh.material.emissive.copy(hoveredMesh.userData.baseColor).multiplyScalar(0.12);
        }
        hoveredMesh = newHover;
        if (hoveredMesh) {
          hoveredMesh.material.emissive.set(0xff4040);
        }
        const n = newHover ? newHover.userData.name : null;
        if (n !== currentHover) { currentHover = n; setHover(n); }
        renderer.domElement.style.cursor = newHover ? 'pointer' : 'grab';
      }

      labelPositions.forEach(lp => {
        const el = labelNodes.get(lp.name);
        if (!el) return;
        projectToScreen(lp.proxy, tmp);
        if (tmp.z > 1 || tmp.z < -1) { el.style.opacity = '0'; return; }
        el.style.left = tmp.x + 'px';
        el.style.top = tmp.y + 'px';
        const isHover = lp.name === currentHover;
        if (isHover) {
          el.textContent = lp.name.toUpperCase();
          el.style.color = '#fff';
          el.style.fontSize = '14px';
          el.style.letterSpacing = '3px';
          el.style.opacity = '1';
          el.style.textShadow = '0 0 10px rgba(255,40,40,0.9), 0 0 2px #000';
        } else if (!litFor(lp.name)) {
          el.textContent = STATE_ABBR[lp.name] || lp.name.slice(0, 2).toUpperCase();
          el.style.color = 'rgba(255,170,170,0.22)';
          el.style.fontSize = '10px';
          el.style.letterSpacing = '2px';
          el.style.opacity = '0.5';
          el.style.textShadow = '0 0 6px rgba(0,0,0,0.9)';
        } else {
          el.textContent = STATE_ABBR[lp.name] || lp.name.slice(0, 2).toUpperCase();
          el.style.color = 'rgba(255,170,170,0.72)';
          el.style.fontSize = '11px';
          el.style.letterSpacing = '2px';
          el.style.opacity = '0.95';
          el.style.textShadow = '0 0 6px rgba(0,0,0,0.9)';
        }
      });

      renderer.render(scene, camera);
      raf = requestAnimationFrame(tick);
    };
    tick();

    const onResize = () => {
      const w = mount.clientWidth, h = mount.clientHeight;
      camera.aspect = w / h; camera.updateProjectionMatrix();
      renderer.setSize(w, h);
    };
    window.addEventListener('resize', onResize);

    renderer.domElement.style.cursor = 'grab';

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', onResize);
      renderer.domElement.removeEventListener('pointermove', onPointerMove);
      renderer.domElement.removeEventListener('pointerdown', onPointerDown);
      renderer.domElement.removeEventListener('pointerup', onPointerUp);
      controls.dispose();
      renderer.dispose();
      stateMeshes.forEach(m => { m.geometry.dispose(); m.material.dispose(); });
      labelNodes.forEach(n => n.remove());
      if (mount.contains(renderer.domElement)) mount.removeChild(renderer.domElement);
    };
  }, [statesFC]);

  const hoverPaidM = hover && STATE_HEIGHTS[hover] ? STATE_HEIGHTS[hover][year] : null;
  const hoverPilot = fwIsPilot(hover);

  return (
    <div style={{ width:'100%', height:'100%', position:'relative', overflow:'hidden' }}>
      <div ref={mountRef} style={{ position:'absolute', inset:0, cursor:'grab' }}/>
      <div ref={labelsRef} style={{ position:'absolute', inset:0, pointerEvents:'none', zIndex:10 }}/>

      {hover && (
        <div style={{ position:'absolute', top:96, left:'50%', transform:'translateX(-50%)',
          background:'rgba(8,0,0,0.95)', border:'1px solid #ff3030',
          padding:'10px 16px', pointerEvents:'none', zIndex:30,
          fontFamily:'"Space Grotesk", sans-serif',
          boxShadow:'0 0 30px rgba(255,40,40,0.35)' }}>
          <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
            color:'#ff3030', letterSpacing:2, fontWeight:700, marginBottom:4 }}>
            {(STATE_ABBR[hover] || '').toUpperCase()} · {hover.toUpperCase()} · {hoverPilot ? 'CLICK TO DRILL IN' : 'CLICK FOR TOP COUNTIES'}
          </div>
          <div style={{ fontSize:16, fontWeight:800, color:'#fff' }}>
            {hoverPaidM != null ? fmtPaid(hoverPaidM * 1e6) : '—'}{' '}
            <span style={{ color:'#ff5050', fontSize:11, fontWeight:700 }}>MEDICAID PAID · {year}</span>
          </div>
        </div>
      )}

      <div style={{ position:'absolute', bottom:100, left:'50%', transform:'translateX(-50%)', zIndex:25,
        fontFamily:'ui-monospace, monospace', fontSize:9, fontWeight:700,
        letterSpacing:2, color:'rgba(255,100,100,0.55)',
        background:'rgba(8,0,0,0.7)', padding:'6px 12px',
        border:'1px solid rgba(255,40,40,0.2)', whiteSpace:'nowrap' }}>
        HOVER · RAISE STATE    PLAY · GROW ALL    DRAG · ROTATE    SCROLL · ZOOM    CLICK · DRILL IN
      </div>
    </div>
  );
}

/* ---------------- Pilot state — Three.js true 3D county extrusion ----------------
   Heights = real Medicaid paid per county. The intro animation grows counties
   to their first-year level then sweeps to the latest year, each county scaled
   by its own per-year dollars. */
function StateDrillThree({ stateName, counties, neighbors, year, playing, onYear, onPlayingChange, onBack }) {
  const abbr = window.STATE_ABBR_BY_NAME[stateName];
  const countyData = (window.COUNTY_DATA[abbr]) || {};
  const stateInfo = window.STATE_INFO[stateName] || { paid: {}, providers: {} };

  const mountRef = React.useRef(null);
  const [hoverData, setHoverData] = React.useState(null);
  const [selected, setSelected] = React.useState(null);
  const selectedRef = React.useRef(null);
  React.useEffect(() => { selectedRef.current = selected?.fips || null; }, [selected]);

  const animRef = React.useRef({ start: 0, running: false });
  const playingRef = React.useRef(playing);
  React.useEffect(() => { playingRef.current = playing; }, [playing]);
  const onYearRef = React.useRef(onYear);
  React.useEffect(() => { onYearRef.current = onYear; }, [onYear]);
  const onPlayingChangeRef = React.useRef(onPlayingChange);
  React.useEffect(() => { onPlayingChangeRef.current = onPlayingChange; }, [onPlayingChange]);
  const onBackRef = React.useRef(onBack);
  React.useEffect(() => { onBackRef.current = onBack; }, [onBack]);

  React.useEffect(() => {
    if (playing) {
      animRef.current.start = performance.now();
      animRef.current.running = true;
    }
  }, [playing]);

  // Debug hook so verification tooling can select a county without raycasting.
  React.useEffect(() => {
    window.__fwDrillDebug = {
      select: (fips) => {
        const d = countyData[fips];
        if (d) setSelected({ fips, name: d.name, pop: d.pop, paid: d.paid, providers: d.providers });
      },
    };
    return () => { delete window.__fwDrillDebug; };
  }, []);

  React.useEffect(() => {
    const mount = mountRef.current;
    if (!mount || !window.THREE) return;
    const THREE = window.THREE;

    const width = mount.clientWidth;
    const height = mount.clientHeight;

    const proj = d3.geoMercator().fitExtent([[-0.9, -0.9], [0.9, 0.9]], counties);

    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x070203);
    scene.fog = new THREE.Fog(0x070203, 3.6, 7);

    const camera = new THREE.PerspectiveCamera(45, width / height, 0.05, 50);
    camera.up.set(0, 1, 0);
    camera.position.set(0, 2.2, 1.7);
    camera.lookAt(0, 0, 0);

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
    renderer.setSize(width, height);
    renderer.setClearColor(0x000000, 0);
    mount.appendChild(renderer.domElement);

    scene.add(new THREE.AmbientLight(0x662222, 0.5));
    const key = new THREE.DirectionalLight(0xff4040, 1.1);
    key.position.set(-2, 4, 3); scene.add(key);
    const rim = new THREE.DirectionalLight(0xff8080, 0.4);
    rim.position.set(3, 2, -2); scene.add(rim);
    const fill = new THREE.DirectionalLight(0xffffff, 0.25);
    fill.position.set(0, 5, 0); scene.add(fill);

    const makeGroundTexture = () => {
      const size = 1024;
      const cnv = document.createElement('canvas');
      cnv.width = cnv.height = size;
      const ctx = cnv.getContext('2d');
      const grad = ctx.createRadialGradient(size/2, size/2, size*0.1, size/2, size/2, size*0.6);
      grad.addColorStop(0, '#1a0608');
      grad.addColorStop(0.6, '#0a0305');
      grad.addColorStop(1, '#050102');
      ctx.fillStyle = grad; ctx.fillRect(0, 0, size, size);
      const img = ctx.getImageData(0, 0, size, size);
      const d = img.data;
      for (let i = 0; i < d.length; i += 4) {
        const n = (Math.random() - 0.5) * 22;
        d[i]   = Math.max(0, Math.min(255, d[i]   + n));
        d[i+1] = Math.max(0, Math.min(255, d[i+1] + n * 0.3));
        d[i+2] = Math.max(0, Math.min(255, d[i+2] + n * 0.3));
      }
      ctx.putImageData(img, 0, 0);
      ctx.strokeStyle = 'rgba(120,30,30,0.08)'; ctx.lineWidth = 1;
      for (let x = 0; x < size; x += 32) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, size); ctx.stroke(); }
      for (let y = 0; y < size; y += 32) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(size, y); ctx.stroke(); }
      ctx.strokeStyle = 'rgba(180,60,60,0.12)'; ctx.lineWidth = 1.5;
      for (let x = 0; x < size; x += 128) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, size); ctx.stroke(); }
      for (let y = 0; y < size; y += 128) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(size, y); ctx.stroke(); }
      const tex = new THREE.CanvasTexture(cnv);
      tex.wrapS = tex.wrapT = THREE.RepeatWrapping; tex.repeat.set(2, 2);
      return tex;
    };
    const plate = new THREE.Mesh(
      new THREE.PlaneGeometry(10, 10),
      new THREE.MeshStandardMaterial({ map: makeGroundTexture(), roughness: 0.9, metalness: 0.05, color: 0x805050 })
    );
    plate.rotation.x = -Math.PI / 2; plate.position.y = -0.008; scene.add(plate);

    const neighborGroup = new THREE.Group();
    neighborGroup.rotation.x = -Math.PI / 2;
    neighborGroup.position.y = -0.001;
    scene.add(neighborGroup);

    const buildShapeWith = (ring) => {
      const s = new THREE.Shape();
      ring.forEach(([x, y], i) => {
        const p = proj([x, y]); if (!p) return;
        const px = p[0], py = -p[1];
        if (i === 0) s.moveTo(px, py); else s.lineTo(px, py);
      });
      return s;
    };

    if (neighbors) {
      neighbors.features.forEach(f => {
        if (f.properties.name === stateName) return;
        const geom = f.geometry;
        const polys = geom.type === 'Polygon' ? [geom.coordinates] : geom.coordinates;
        polys.forEach(poly => {
          const outer = poly[0]; if (!outer || outer.length < 3) return;
          const shape = buildShapeWith(outer);
          for (let i = 1; i < poly.length; i++) {
            const hole = new THREE.Path();
            poly[i].forEach(([x, y], k) => {
              const p = proj([x, y]); if (!p) return;
              const px = p[0], py = -p[1];
              if (k === 0) hole.moveTo(px, py); else hole.lineTo(px, py);
            });
            shape.holes.push(hole);
          }
          const flatGeom = new THREE.ShapeGeometry(shape);
          const flatMesh = new THREE.Mesh(flatGeom, new THREE.MeshStandardMaterial({
            color: 0x1a0608, roughness: 0.85, metalness: 0.1, transparent: true, opacity: 0.85,
          }));
          neighborGroup.add(flatMesh);
          const edges = new THREE.LineSegments(new THREE.EdgesGeometry(flatGeom, 1),
            new THREE.LineBasicMaterial({ color: 0x663030, transparent: true, opacity: 0.55 }));
          edges.position.z = 0.0005;
          neighborGroup.add(edges);
        });
      });
    }

    const YEARS = window.FW_YEARS;
    const minY = YEARS[0], maxY = YEARS[YEARS.length - 1];

    const maxPaid = Math.max(0.01, ...Object.values(countyData).map(c => (c.paid && c.paid[maxY]) || 0));
    const paidAt = (fips, y) => {
      const c = countyData[fips];
      return (c && c.paid && c.paid[y]) || 0;
    };
    // sqrt scale: county dollars span ~500:1, linear bars read as one tower.
    const colorFor = (fips) => {
      const intensity = Math.min(1, Math.sqrt(paidAt(fips, maxY) / maxPaid));
      return new THREE.Color(0.9 + intensity * 0.1,
        Math.max(0.1, 0.35 - intensity * 0.25), Math.max(0.1, 0.35 - intensity * 0.25));
    };
    const heightFor = (fips) => 0.02 + Math.sqrt(paidAt(fips, maxY) / maxPaid) * 0.7;

    const countyMeshes = [];
    const mapGroup = new THREE.Group();
    mapGroup.rotation.x = -Math.PI / 2;
    scene.add(mapGroup);

    counties.features.forEach(f => {
      const fips = String(f.id);
      const name = f.properties.name;
      const h = heightFor(fips); const color = colorFor(fips);
      const geom = f.geometry;
      const polys = geom.type === 'Polygon' ? [geom.coordinates] : geom.coordinates;
      polys.forEach(poly => {
        const outer = poly[0]; if (!outer || outer.length < 3) return;
        const shape = buildShapeWith(outer);
        for (let i = 1; i < poly.length; i++) {
          const hole = new THREE.Path();
          poly[i].forEach(([x, y], k) => {
            const p = proj([x, y]); if (!p) return;
            const px = p[0], py = -p[1];
            if (k === 0) hole.moveTo(px, py); else hole.lineTo(px, py);
          });
          shape.holes.push(hole);
        }
        const extrude = new THREE.ExtrudeGeometry(shape, {
          depth: h, bevelEnabled: true, bevelSize: 0.002, bevelThickness: 0.002, bevelSegments: 1,
        });
        extrude.computeVertexNormals();
        const mat = new THREE.MeshStandardMaterial({
          color, roughness: 0.55, metalness: 0.15,
          emissive: color.clone().multiplyScalar(0.08),
        });
        const mesh = new THREE.Mesh(extrude, mat);
        mesh.userData = { fips, name, h, color: color.clone(), mat };
        mapGroup.add(mesh); countyMeshes.push(mesh);
        const edges = new THREE.LineSegments(new THREE.EdgesGeometry(extrude, 25),
          new THREE.LineBasicMaterial({ color: 0x000000, transparent: true, opacity: 0.55 }));
        mesh.add(edges);
        mesh.userData.edges = edges;
        mesh.scale.z = 0.0001;
      });
    });

    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true; controls.dampingFactor = 0.08;
    controls.minDistance = 0.6; controls.maxDistance = 6;
    controls.maxPolarAngle = Math.PI * 0.48;
    controls.minPolarAngle = 0.05;
    controls.enablePan = true; controls.screenSpacePanning = true;
    controls.target.set(0, 0, 0);
    camera.up.set(0, 1, 0); camera.position.set(0, 2.2, 1.7);
    controls.update();

    const ray = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    let hoveredMesh = null;
    const onPointerMove = (e) => {
      const rect = renderer.domElement.getBoundingClientRect();
      mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
    };
    renderer.domElement.addEventListener('pointermove', onPointerMove);

    let downX = 0, downY = 0;
    const onPointerDown = (e) => { downX = e.clientX; downY = e.clientY; };
    const onPointerUp = (e) => {
      const dx = Math.abs(e.clientX - downX), dy = Math.abs(e.clientY - downY);
      if (dx + dy > 4) return;
      ray.setFromCamera(mouse, camera);
      const hits = ray.intersectObjects(countyMeshes, false);
      const hit = hits[0]?.object;
      if (!hit) { setSelected(null); return; }
      const fips = hit.userData.fips;
      const d = countyData[fips];
      if (!d) { setSelected(null); return; }
      setSelected({ fips, name: d.name || hit.userData.name, pop: d.pop, paid: d.paid, providers: d.providers });
    };
    renderer.domElement.addEventListener('pointerdown', onPointerDown);
    renderer.domElement.addEventListener('pointerup', onPointerUp);

    animRef.current.start = performance.now();
    animRef.current.running = true;

    const ANIM_MS = 12000;
    const easeOutCubic = t => 1 - Math.pow(1 - t, 3);
    const smooth = t => t < 0.5 ? 2*t*t : 1 - Math.pow(-2*t+2, 2)/2;
    let lastReportedYear = null;

    // Per-county relative height at a continuous year position, relative to
    // that county's latest-year level (which the geometry was built at).
    // sqrt to match the height scale.
    const relAt = (fips, yearT) => {
      const pMax = paidAt(fips, maxY);
      if (pMax <= 0) return 0;
      const lo = Math.floor(yearT), hi = Math.min(maxY, lo + 1);
      const f = yearT - lo;
      const v = paidAt(fips, lo) * (1 - f) + paidAt(fips, hi) * f;
      return Math.sqrt(Math.max(0, v / pMax));
    };

    let raf;
    const tick = () => {
      controls.update();
      const a = animRef.current;
      let progress;
      if (a.running) {
        const elapsed = performance.now() - a.start;
        progress = Math.min(1, elapsed / ANIM_MS);
        if (progress >= 1) {
          a.running = false;
          if (playingRef.current && onPlayingChangeRef.current) onPlayingChangeRef.current(false);
        }
      } else { progress = 1; }

      let displayYear, yearT, growP;
      if (a.running && progress < 0.25) {
        growP = smooth(progress / 0.25);
        yearT = minY;
        displayYear = minY;
      } else if (a.running) {
        growP = 1;
        const p = easeOutCubic((progress - 0.25) / 0.75);
        yearT = minY + p * (maxY - minY);
        displayYear = Math.round(yearT);
      } else {
        growP = 1; yearT = maxY; displayYear = maxY;
      }

      for (let i = 0; i < countyMeshes.length; i++) {
        const m = countyMeshes[i];
        const rel = relAt(m.userData.fips, yearT);
        m.scale.z = Math.max(0.0001, rel * growP);
      }

      if (displayYear !== lastReportedYear && onYearRef.current) {
        lastReportedYear = displayYear;
        onYearRef.current(displayYear);
      }

      ray.setFromCamera(mouse, camera);
      const hits = ray.intersectObjects(countyMeshes, false);
      const newHover = hits[0]?.object || null;
      if (newHover !== hoveredMesh) {
        if (hoveredMesh && hoveredMesh.userData.fips !== selectedRef.current) {
          hoveredMesh.material.emissive.copy(hoveredMesh.userData.color).multiplyScalar(0.08);
        }
        hoveredMesh = newHover;
        if (hoveredMesh && hoveredMesh.userData.fips !== selectedRef.current) {
          hoveredMesh.material.emissive.set(0xff4040);
        }
        if (hoveredMesh) {
          const fips = hoveredMesh.userData.fips;
          const d = countyData[fips];
          setHoverData(d ? {
            fips, name: d.name || hoveredMesh.userData.name,
            pop: d.pop, paid: (d.paid && d.paid[displayYear]) || 0,
            providers: (d.providers && d.providers[displayYear]) || 0,
          } : { fips, name: hoveredMesh.userData.name, pop: null, paid: 0, providers: 0 });
        } else { setHoverData(null); }
      }

      countyMeshes.forEach(m => {
        const isSel = m.userData.fips === selectedRef.current;
        if (isSel) { m.material.emissive.set(0xffa020); m.material.emissiveIntensity = 1.4; }
        else if (m !== hoveredMesh) { m.material.emissiveIntensity = 1.0; }
      });

      renderer.render(scene, camera);
      raf = requestAnimationFrame(tick);
    };
    tick();

    const onResize = () => {
      const w = mount.clientWidth, h = mount.clientHeight;
      camera.aspect = w / h; camera.updateProjectionMatrix();
      renderer.setSize(w, h);
    };
    window.addEventListener('resize', onResize);

    const onKey = (e) => { if (e.key === 'Escape') onBackRef.current && onBackRef.current(); };
    window.addEventListener('keydown', onKey);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('resize', onResize);
      window.removeEventListener('keydown', onKey);
      renderer.domElement.removeEventListener('pointermove', onPointerMove);
      renderer.domElement.removeEventListener('pointerdown', onPointerDown);
      renderer.domElement.removeEventListener('pointerup', onPointerUp);
      controls.dispose(); renderer.dispose();
      countyMeshes.forEach(m => { m.geometry.dispose(); m.material.dispose(); });
      if (mount.contains(renderer.domElement)) mount.removeChild(renderer.domElement);
    };
  }, [counties]);

  const nCounties = counties.features.length;
  const statePaid = (stateInfo.paid && stateInfo.paid[year]) || 0;
  const selPaid = selected ? ((selected.paid && selected.paid[year]) || 0) : 0;
  const selProviders = selected ? ((selected.providers && selected.providers[year]) || 0) : 0;

  const bigLabel = selected
    ? `${selected.name.toUpperCase()} COUNTY · ${year}`
    : `${stateName.toUpperCase()} · ${nCounties} COUNTIES · ${year}`;
  const bigValue = selected ? fmtPaid(selPaid) : fmtPaid(statePaid);
  const bigSub = selected
    ? `MEDICAID PAID · ${selProviders} PROVIDERS${selected.pop ? ' · POP ' + fmtCount(selected.pop) : ''}`
    : `BAR HEIGHT ∝ √ MEDICAID PAID · NPPES PRACTICE ADDRESS`;

  return (
    <div style={{ width:'100%', height:'100%', position:'relative', overflow:'hidden',
      background:'radial-gradient(ellipse at 50% 60%, #1a0d10 0%, #0a0608 40%, #000 100%)' }}>
      <div ref={mountRef} style={{ position:'absolute', inset:0, cursor:'grab' }}/>

      {hoverData && !selected && (
        <div style={{ position:'absolute', top:24, left:'50%', transform:'translateX(-50%)',
          background:'rgba(8,0,0,0.95)', border:'1px solid #ff3030',
          padding:'10px 16px', pointerEvents:'none', zIndex:30,
          fontFamily:'"Space Grotesk", sans-serif',
          boxShadow:'0 0 30px rgba(255,40,40,0.4)' }}>
          <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
            color:'#ff3030', letterSpacing:2, fontWeight:700, marginBottom:6 }}>
            {hoverData.name.toUpperCase()} · CLICK TO SELECT
          </div>
          <div style={{ display:'flex', gap:18 }}>
            <div><div style={{ fontSize:8, color:'rgba(255,120,120,0.6)', letterSpacing:1.5, fontWeight:700 }}>PAID · {year}</div>
              <div style={{ fontSize:15, color:'#ff5050', fontWeight:800 }}>{fmtPaid(hoverData.paid)}</div></div>
            <div><div style={{ fontSize:8, color:'rgba(255,120,120,0.6)', letterSpacing:1.5, fontWeight:700 }}>PROVIDERS</div>
              <div style={{ fontSize:15, color:'#fff', fontWeight:800 }}>{fmtCount(hoverData.providers)}</div></div>
            <div><div style={{ fontSize:8, color:'rgba(255,120,120,0.6)', letterSpacing:1.5, fontWeight:700 }}>POP</div>
              <div style={{ fontSize:15, color:'#fff', fontWeight:800 }}>{hoverData.pop ? fmtCount(hoverData.pop) : '—'}</div></div>
          </div>
        </div>
      )}

      <div style={{ position:'absolute', top:200, left:24, zIndex:25 }}>
        <button onClick={onBack} style={{ background:'#ff2020', border:'1px solid #ff5050',
          padding:'10px 18px', color:'#000', cursor:'pointer',
          fontFamily:'"Space Grotesk", sans-serif', fontSize:11, fontWeight:800,
          letterSpacing:2, boxShadow:'0 0 20px rgba(255,40,40,0.5)' }}>
          ✕ CLOSE · ESC
        </button>
      </div>

      <div style={{ position:'absolute', bottom:120, left:24, zIndex:25,
        background:'rgba(8,0,0,0.92)', border:`1px solid ${selected ? '#ffa020' : 'rgba(255,40,40,0.4)'}`,
        padding:'16px 22px', backdropFilter:'blur(10px)', minWidth: 280,
        boxShadow: selected ? '0 0 24px rgba(255,160,32,0.25)' : 'none',
        transition:'all .2s' }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:12, marginBottom:4 }}>
          <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
            color: selected ? '#ffa020' : '#ff3030', letterSpacing:2.5, fontWeight:700 }}>
            {bigLabel}
          </div>
          {selected && (
            <button onClick={() => setSelected(null)} style={{
              background:'transparent', border:'1px solid rgba(255,160,32,0.4)',
              color:'#ffa020', fontFamily:'ui-monospace, monospace',
              fontSize:9, fontWeight:700, letterSpacing:1.5,
              padding:'3px 8px', cursor:'pointer' }}>CLEAR</button>
          )}
        </div>
        <div style={{ fontFamily:'"Space Grotesk", sans-serif',
          fontSize:32, fontWeight:800, color: selected ? '#ffd080' : '#fff',
          letterSpacing:-1, lineHeight:1 }}>
          {bigValue} <span style={{ color: selected ? '#ffa020' : '#ff3030', fontSize:13, fontWeight:700 }}>
            MEDICAID PAID
          </span>
        </div>
        <div style={{ fontFamily:'ui-monospace, monospace', fontSize:10,
          color:'rgba(255,180,180,0.65)', fontWeight:600, marginTop:10, letterSpacing:1 }}>
          {bigSub}
        </div>
      </div>

      <div style={{ position:'absolute', top:200, right:24, zIndex:25,
        background:'rgba(8,0,0,0.9)', border:'1px solid rgba(255,40,40,0.35)',
        padding:'12px 14px', backdropFilter:'blur(10px)',
        fontFamily:'ui-monospace, monospace' }}>
        <div style={{ fontSize:9, fontWeight:700, letterSpacing:2, color:'#ff3030', marginBottom:8 }}>PAID $ INTENSITY</div>
        <div style={{ width:120, height:8,
          background:'linear-gradient(90deg, #ffb0b0, #ff6060, #ff2020, #8b0a0a)', marginBottom:4 }}/>
        <div style={{ display:'flex', justifyContent:'space-between',
          fontSize:8, color:'rgba(255,160,160,0.7)', fontWeight:600 }}>
          <span>LOW</span><span>HIGH</span>
        </div>
      </div>

      {!selected && (window.CASES_BY_STATE[abbr] || []).length > 0 && (
        <div style={{ position:'absolute', top:252, left:24, width:322, zIndex:24,
          maxHeight:'calc(100vh - 520px)', overflowY:'auto',
          background:'rgba(8,0,0,0.92)', border:'1px solid rgba(255,80,80,0.4)',
          backdropFilter:'blur(10px)', padding:'12px 16px' }}>
          <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9, color:'#ff5050',
            letterSpacing:2, fontWeight:700, marginBottom:10 }}>
            ENFORCEMENT CASES · {stateName.toUpperCase()} · DOJ
          </div>
          {(window.CASES_BY_STATE[abbr] || []).map((c, i) => <CaseRow key={i} c={c} showCounty={true} />)}
        </div>
      )}

      {selected && <CountyCaseFile county={selected} year={year} onClose={() => setSelected(null)} />}

      <div style={{ position:'absolute', bottom:120, left:'50%', transform:'translateX(-50%)', zIndex:25,
        fontFamily:'ui-monospace, monospace', fontSize:9, fontWeight:700,
        letterSpacing:2, color:'rgba(255,100,100,0.55)',
        background:'rgba(8,0,0,0.7)', padding:'6px 12px',
        border:'1px solid rgba(255,40,40,0.2)', whiteSpace:'nowrap' }}>
        CLICK · SELECT    DRAG · ROTATE    RIGHT-DRAG · PAN    SCROLL · ZOOM    ESC · CLOSE
      </div>
    </div>
  );
}

/* County case file — real metrics + top billing codes from the API,
   plus editorial incident log when one exists. */
// Enforcement case card — shared by the county file, the state cases panel,
// and the DOJ rail. `locator` overrides the small location line when given.
function CaseRow({ c, showCounty, locator }) {
  const prog = (c.program_primary || (c.program_tags && c.program_tags[0]) || 'gov').replace(/_/g, ' ');
  return (
    <a href={c.source_url} target="_blank" rel="noopener noreferrer"
      style={{ display:'block', textDecoration:'none', marginBottom:12, paddingBottom:11,
        borderBottom:'1px solid rgba(255,80,80,0.14)' }}>
      <div style={{ display:'flex', gap:6, alignItems:'center', marginBottom:5, flexWrap:'wrap' }}>
        {!c.reviewed ? <span style={{ fontFamily:'ui-monospace, monospace', fontSize:8, fontWeight:700, letterSpacing:1,
          color:'#000', background:'#ffa020', padding:'2px 6px' }}>PENDING REVIEW</span> : null}
        <span style={{ fontFamily:'ui-monospace, monospace', fontSize:8, fontWeight:700, letterSpacing:1,
          color:'#000', background:'#ff5050', padding:'2px 6px' }}>{(c.status || '').toUpperCase()}</span>
        <span style={{ fontFamily:'ui-monospace, monospace', fontSize:8, fontWeight:700, letterSpacing:1,
          color:'#ff8080', border:'1px solid rgba(255,80,80,0.4)', padding:'2px 6px' }}>{prog.toUpperCase()}</span>
        {c.amount_usd ? <span style={{ fontFamily:'ui-monospace, monospace', fontSize:9, fontWeight:700, color:'#ff5050' }}>{fmtPaid(c.amount_usd)}</span> : null}
        <span style={{ marginLeft:'auto', fontFamily:'ui-monospace, monospace', fontSize:9, color:'rgba(255,150,150,0.6)' }}>{c.date}</span>
      </div>
      <div style={{ fontFamily:'"Space Grotesk", sans-serif', fontSize:13, color:'#fff', fontWeight:600, lineHeight:1.35 }}>{c.title}</div>
      {(locator || (showCounty && c.county_name)) ? <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9, color:'rgba(255,150,150,0.6)', marginTop:3, letterSpacing:1 }}>{locator || (c.county_name.toUpperCase() + ' COUNTY')}</div> : null}
      {c.summary ? <div style={{ fontSize:11, color:'rgba(230,190,190,0.72)', lineHeight:1.45, marginTop:4 }}>{c.summary}</div> : null}
      <div style={{ fontFamily:'ui-monospace, monospace', fontSize:8.5, color:'#ff7070', marginTop:5, letterSpacing:1 }}>{(c.source || 'DOJ')} SOURCE ↗</div>
    </a>
  );
}

function CountyCaseFile({ county, year }) {
  const [detail, setDetail] = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    setDetail(null);
    fwCountyDetail(county.fips).then(d => { if (alive) setDetail(d); });
    return () => { alive = false; };
  }, [county.fips]);

  const casefile = window.COUNTY_CASEFILES[county.fips];
  const paidY = (county.paid && county.paid[year]) || 0;
  const provY = (county.providers && county.providers[year]) || 0;
  const codes = detail && detail.topCodes && detail.topCodes[String(year)]
    ? detail.topCodes[String(year)].slice(0, 5) : null;

  return (
    <div style={{ position:'absolute', top:280, right:24, bottom:120, zIndex:26,
      width: 340,
      background:'rgba(8,0,0,0.94)', border:'1px solid #ffa020',
      backdropFilter:'blur(12px)',
      boxShadow:'0 0 40px rgba(255,160,32,0.2)',
      display:'flex', flexDirection:'column', overflow:'hidden' }}>
      <div style={{ padding:'14px 18px', borderBottom:'1px solid rgba(255,160,32,0.25)' }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:4 }}>
          <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
            color:'#ffa020', letterSpacing:2.5, fontWeight:700 }}>
            CASE FILE · {county.name.toUpperCase()} COUNTY
          </div>
          {casefile && casefile.sample ? (
            <span style={{ fontFamily:'ui-monospace, monospace', fontSize:8, fontWeight:700,
              letterSpacing:1.5, color:'#000', background:'#ffa020', padding:'2px 6px' }}>SAMPLE</span>
          ) : null}
        </div>
        <div style={{ fontFamily:'"Space Grotesk", sans-serif', fontSize:17,
          color:'#fff', fontWeight:700, lineHeight:1.25 }}>
          {casefile ? casefile.headline : `${county.name} — Medicaid spending profile`}
        </div>
      </div>

      <div style={{ display:'grid', gridTemplateColumns:'repeat(3, 1fr)',
        padding:'12px 18px', gap:10, borderBottom:'1px solid rgba(255,160,32,0.18)' }}>
        <div><div style={{ fontSize:8, color:'rgba(255,180,120,0.65)', letterSpacing:1.5, fontWeight:700, fontFamily:'ui-monospace, monospace' }}>POPULATION</div>
          <div style={{ fontSize:15, color:'#fff', fontWeight:800 }}>{county.pop ? fmtCount(county.pop) : '—'}</div></div>
        <div><div style={{ fontSize:8, color:'rgba(255,180,120,0.65)', letterSpacing:1.5, fontWeight:700, fontFamily:'ui-monospace, monospace' }}>PAID · {year}</div>
          <div style={{ fontSize:15, color:'#ffa020', fontWeight:800 }}>{fmtPaid(paidY)}</div></div>
        <div><div style={{ fontSize:8, color:'rgba(255,180,120,0.65)', letterSpacing:1.5, fontWeight:700, fontFamily:'ui-monospace, monospace' }}>PROVIDERS</div>
          <div style={{ fontSize:15, color:'#ffa020', fontWeight:800 }}>{fmtCount(provY)}</div></div>
      </div>

      <div style={{ flex:1, overflowY:'auto', padding:'12px 18px' }}>
        <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
          color:'rgba(255,160,100,0.7)', letterSpacing:2, fontWeight:700, marginBottom:10 }}>
          TOP BILLING CODES · {year}
        </div>
        {codes === null && (
          <div style={{ fontSize:11, color:'rgba(255,180,140,0.6)', marginBottom:14 }}>
            {detail ? 'No code-level detail for this county.' : 'Loading…'}
          </div>
        )}
        {codes && codes.map((c, i) => (
          <div key={c.hcpcs} style={{ display:'flex', justifyContent:'space-between',
            alignItems:'center', padding:'6px 0',
            borderBottom:'1px solid rgba(255,160,32,0.1)' }}>
            <div style={{ display:'flex', gap:10, alignItems:'baseline' }}>
              <span style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
                color:'rgba(200,130,80,0.6)', fontWeight:700 }}>{String(i+1).padStart(2,'0')}</span>
              <span style={{ fontFamily:'ui-monospace, monospace', fontSize:12, fontWeight:700, color:'#fff' }}>{c.hcpcs}</span>
            </div>
            <span style={{ fontFamily:'ui-monospace, monospace', fontSize:11, fontWeight:700, color:'#ffa020' }}>
              {fmtPaid(c.paid)}
            </span>
          </div>
        ))}

        {(() => {
          const cases = (window.CASES_BY_FIPS && window.CASES_BY_FIPS[county.fips]) || [];
          if (!cases.length) return null;
          return (
            <React.Fragment>
              <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
                color:'#ff5050', letterSpacing:2, fontWeight:700, margin:'18px 0 10px' }}>
                ENFORCEMENT CASES · DOJ
              </div>
              {cases.map((c, i) => <CaseRow key={i} c={c} />)}
            </React.Fragment>
          );
        })()}

        <div style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
          color:'rgba(255,160,100,0.7)', letterSpacing:2, fontWeight:700, margin:'18px 0 10px' }}>
          INCIDENT LOG
        </div>
        {casefile && casefile.incidents.length > 0 ? (
          casefile.incidents.map((s, i) => (
            <div key={i} style={{ marginBottom:14, paddingBottom:12,
              borderBottom: i === casefile.incidents.length-1 ? 'none' : '1px solid rgba(255,160,32,0.12)' }}>
              <div style={{ display:'flex', gap:10, alignItems:'center', marginBottom:4 }}>
                <span style={{ fontFamily:'ui-monospace, monospace', fontSize:8, fontWeight:700,
                  letterSpacing:1.5, color:'#ffa020',
                  border:'1px solid rgba(255,160,32,0.4)', padding:'2px 6px' }}>{s.tag.toUpperCase()}</span>
                <span style={{ fontFamily:'ui-monospace, monospace', fontSize:9,
                  color:'rgba(255,180,140,0.65)', letterSpacing:1.5, fontWeight:700 }}>{s.date}</span>
              </div>
              <div style={{ fontFamily:'"Space Grotesk", sans-serif', fontSize:13,
                color:'#fff', fontWeight:600, lineHeight:1.35 }}>{s.title}</div>
            </div>
          ))
        ) : (
          <div style={{ fontSize:12, color:'rgba(255,180,140,0.7)', lineHeight:1.5 }}>
            No editorial entries for {county.name} yet. Figures above are computed
            from HHS Medicaid claims geocoded to NPPES practice addresses.
          </div>
        )}
      </div>

      {casefile && casefile.footnote && (
        <div style={{ padding:'10px 18px',
          borderTop:'1px solid rgba(255,160,32,0.25)',
          fontFamily:'ui-monospace, monospace', fontSize:9,
          color:'rgba(255,180,140,0.7)', letterSpacing:1, lineHeight:1.4 }}>
          {casefile.footnote}
        </div>
      )}
    </div>
  );
}

Object.assign(window, { USAMap, fwIsPilot, CaseRow });
