diff --git a/RFCP-Iteration7.4-Radius-Sector-UI.md b/RFCP-Iteration7.4-Radius-Sector-UI.md
new file mode 100644
index 0000000..12d53d5
--- /dev/null
+++ b/RFCP-Iteration7.4-Radius-Sector-UI.md
@@ -0,0 +1,389 @@
+# RFCP - Iteration 7.4: Radius Fix + Sector Tree UI
+
+## Issue 1: Coverage Invisible (Critical!)
+
+**Problem:** Max radius clamped to 80px is TOO SMALL for geographic scale.
+
+**At zoom 14:**
+- pixelsPerKm = 104,857
+- targetRadius = 0.4km
+- radiusPixels = 41,943px
+- **Clamped to 80px** ← This is TINY!
+- Should be ~200-500px for smooth coverage
+
+**Root cause:** Geographic scale formula calculates HUGE pixel values at high zoom, but we clamp them down, losing all coverage!
+
+### Solution A: Much Higher Clamps
+
+**File:** `frontend/src/components/map/Heatmap.tsx`
+
+```typescript
+const pixelsPerKm = Math.pow(2, mapZoom) * 6.4;
+const targetRadiusKm = 0.4; // 400m
+const radiusPixels = targetRadiusKm * pixelsPerKm;
+
+// MUCH HIGHER clamps
+const minRadius = 20;
+const maxRadius = mapZoom < 10 ? 60 : 300; // Allow up to 300px at high zoom!
+const radius = Math.max(minRadius, Math.min(maxRadius, radiusPixels));
+
+const blur = radius * 0.6;
+const maxIntensity = 0.75;
+```
+
+### Solution B: Logarithmic Scaling
+
+Instead of linear geographic scale, use log scale:
+
+```typescript
+// Log scale: grows slower at high zoom
+const baseRadius = 30;
+const zoomFactor = Math.log2(mapZoom + 1) / Math.log2(19); // Normalize to 0-1
+const radius = baseRadius + (zoomFactor * 150); // 30px at zoom 1 → 180px at zoom 18
+
+const blur = radius * 0.6;
+const maxIntensity = 0.75;
+```
+
+### Solution C: Simple Progressive Formula (Recommended)
+
+**Forget geographic scale - it's too complex for heatmap library!**
+
+Use simple zoom-dependent formula with generous values:
+
+```typescript
+export function Heatmap({ points, visible, opacity = 0.7 }: HeatmapProps) {
+ const map = useMap();
+ const [mapZoom, setMapZoom] = useState(map.getZoom());
+
+ useEffect(() => {
+ const handleZoomEnd = () => setMapZoom(map.getZoom());
+ map.on('zoomend', handleZoomEnd);
+ return () => { map.off('zoomend', handleZoomEnd); };
+ }, [map]);
+
+ if (!visible || points.length === 0) return null;
+
+ // RSRP normalization
+ const normalizeRSRP = (rsrp: number): number => {
+ const minRSRP = -130;
+ const maxRSRP = -50;
+ const normalized = (rsrp - minRSRP) / (maxRSRP - minRSRP);
+ return Math.max(0, Math.min(1, normalized));
+ };
+
+ // SIMPLE progressive formula
+ // Small zoom: smaller radius (far view)
+ // Large zoom: larger radius (close view)
+ let radius, blur, maxIntensity;
+
+ if (mapZoom < 10) {
+ // Far view: moderate radius
+ radius = 30 + (mapZoom * 2); // 32px at zoom 1 → 50px at zoom 9
+ blur = radius * 0.7;
+ maxIntensity = 0.75;
+ } else {
+ // Close view: large radius to fill gaps
+ radius = 50 + ((mapZoom - 10) * 25); // 50px at zoom 10 → 200px at zoom 16
+ blur = radius * 0.6;
+ maxIntensity = 0.65; // Slightly lower to prevent saturation
+ }
+
+ const heatmapPoints = points.map(p => [
+ p.lat,
+ p.lon,
+ normalizeRSRP(p.rsrp)
+ ] as [number, number, number]);
+
+ // Debug
+ if (import.meta.env.DEV) {
+ console.log('🔍 Heatmap:', {
+ zoom: mapZoom,
+ radius: radius.toFixed(1),
+ blur: blur.toFixed(1),
+ maxIntensity,
+ points: points.length
+ });
+ }
+
+ return (
+
+ p[1]}
+ latitudeExtractor={(p) => p[0]}
+ intensityExtractor={(p) => p[2]}
+ gradient={{
+ 0.0: '#1a237e',
+ 0.15: '#0d47a1',
+ 0.25: '#2196f3',
+ 0.35: '#00bcd4',
+ 0.45: '#00897b',
+ 0.55: '#4caf50',
+ 0.65: '#8bc34a',
+ 0.75: '#ffeb3b',
+ 0.85: '#ff9800',
+ 1.0: '#f44336',
+ }}
+ radius={radius}
+ blur={blur}
+ max={maxIntensity}
+ minOpacity={0.3}
+ />
+
+ );
+}
+```
+
+**Expected results:**
+- Zoom 8: radius=46px, blur=32px → smooth coverage
+- Zoom 12: radius=100px, blur=60px → fills gaps
+- Zoom 16: radius=200px, blur=120px → no grid visible
+
+---
+
+## Issue 2: Sector Tree UI
+
+**Problem:** Clone creates new site instead of new sector in same site.
+
+**Current:**
+```
+Sites (2)
+ - Station-1 (1800 MHz, 43 dBm, Sector 122°, 12m)
+ - Station-1-clone (1800 MHz, 43 dBm, Sector 0°, 60m)
+```
+
+**Should be:**
+```
+Sites (1)
+ - Station-1 (1800 MHz, 30m)
+ ├─ Sector 1 (122°, 65°, 18 dBi)
+ └─ Sector 2 (0°, 65°, 18 dBi)
+```
+
+### Solution: Refactor Clone to Add Sector
+
+**File:** `frontend/src/store/sites.ts`
+
+Current `cloneSector` creates new site:
+```typescript
+const cloneSector = (siteId: string) => {
+ const site = sites.find(s => s.id === siteId);
+ const clone = { ...site, id: uuid(), name: `${site.name}-clone` };
+ setSites([...sites, clone]);
+};
+```
+
+**Change to add sector:**
+```typescript
+const cloneSector = (siteId: string, sectorId?: string) => {
+ const site = sites.find(s => s.id === siteId);
+
+ // If sectorId provided, clone that specific sector
+ // Otherwise clone the first sector
+ const sourceSector = sectorId
+ ? site.sectors.find(s => s.id === sectorId)
+ : site.sectors[0];
+
+ const newSector: Sector = {
+ ...sourceSector,
+ id: `sector-${Date.now()}`,
+ azimuth: (sourceSector.azimuth + 30) % 360, // Offset by 30°
+ };
+
+ // Add sector to existing site
+ updateSite(siteId, {
+ sectors: [...site.sectors, newSector]
+ });
+};
+```
+
+### UI: Tree View for Sectors
+
+**File:** `frontend/src/components/panels/SiteList.tsx`
+
+```typescript
+export function SiteList() {
+ const { sites, selectedSiteIds, toggleSiteSelection } = useSitesStore();
+ const [expandedSites, setExpandedSites] = useState>(new Set());
+
+ const toggleExpand = (siteId: string) => {
+ const newExpanded = new Set(expandedSites);
+ if (newExpanded.has(siteId)) {
+ newExpanded.delete(siteId);
+ } else {
+ newExpanded.add(siteId);
+ }
+ setExpandedSites(newExpanded);
+ };
+
+ return (
+
+
Sites ({sites.length})
+
+ {sites.map(site => {
+ const isExpanded = expandedSites.has(site.id);
+ const isSelected = selectedSiteIds.includes(site.id);
+
+ return (
+
+ {/* Site header */}
+
+
toggleSiteSelection(site.id)}
+ />
+
+
+
+
+ {site.name}
+ {site.frequency} MHz · {site.height}m · {site.sectors.length} sectors
+
+
+
+
+
+
+ {/* Sectors (when expanded) */}
+ {isExpanded && (
+
+ {site.sectors.map((sector, idx) => (
+
+
toggleSector(site.id, sector.id)}
+ />
+
+
+ Sector {idx + 1}
+ {sector.beamwidth < 360 && (
+
+ {sector.azimuth}° · {sector.beamwidth}° · {sector.gain} dBi
+
+ )}
+ {sector.beamwidth === 360 && (
+ Omni · {sector.gain} dBi
+ )}
+
+
+
+
+ {site.sectors.length > 1 && (
+
+ )}
+
+ ))}
+
+
+
+ )}
+
+ );
+ })}
+
+ );
+}
+```
+
+### Simplified Alternative (Quick Fix)
+
+If tree view is too complex, just fix the clone function:
+
+**File:** `frontend/src/components/panels/SiteList.tsx`
+
+```typescript
+// Change button label
+
+
+// Update store function
+const cloneSector = (siteId: string) => {
+ const site = sites.find(s => s.id === siteId);
+ const lastSector = site.sectors[site.sectors.length - 1];
+
+ const newSector = {
+ ...lastSector,
+ id: `sector-${Date.now()}`,
+ azimuth: (lastSector.azimuth + 120) % 360, // 120° spacing for tri-sector
+ };
+
+ updateSite(siteId, {
+ sectors: [...site.sectors, newSector]
+ });
+};
+```
+
+---
+
+## Testing
+
+### Heatmap Visibility:
+- [ ] Zoom 8: Coverage visible with gradient
+- [ ] Zoom 12: Full gradient blue→yellow→red
+- [ ] Zoom 16: No grid pattern, smooth coverage
+- [ ] All zoom levels show coverage extent
+
+### Sector UI:
+- [ ] "Clone Sector" adds sector to SAME site
+- [ ] Site count shows correct number (1 site, 2 sectors = "Sites (1)")
+- [ ] Each sector has edit/remove buttons
+- [ ] Can enable/disable individual sectors
+
+---
+
+## Build & Deploy
+
+```bash
+cd /opt/rfcp/frontend
+npm run build
+sudo systemctl reload caddy
+```
+
+---
+
+## Commit Message
+
+```
+fix(heatmap): increase radius range for visible coverage
+
+- Remove geographic scale formula (too complex)
+- Use simple progressive formula: 30-50px (zoom <10), 50-200px (zoom ≥10)
+- Larger blur at high zoom to fill grid gaps
+- Coverage now visible at all zoom levels
+
+fix(ui): clone creates sector not new site
+
+- Changed cloneSector to add sector to existing site
+- Updated UI: "Clone Sector" instead of "Clone"
+- Site count now accurate (counts sites, not sectors)
+- Each sector independently editable/removable
+
+refactor(ui): sector tree view (optional)
+
+- Expandable site headers
+- Nested sector list with enable/disable toggles
+- Per-sector edit/clone/remove buttons
+- Clear visual hierarchy: Site → Sectors
+```
+
+🚀 Ready for Iteration 7.4!