use eulumdat::atla::{Emitter, IntensityDistribution, LuminaireOpticalData};
use eulumdat::{Eulumdat, IesParser, UnitSystem};
use eulumdat_typst::{ReportGenerator, ReportOptions};
use leptos::ev;
use leptos::prelude::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::HtmlInputElement;

// JS binding for typst PDF compilation
#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_name = compileTypstToPdf, catch)]
    async fn compile_typst_to_pdf_js(source: &str) -> Result<JsValue, JsValue>;
}

/// Compile Typst source to PDF using the WASM typst compiler.
/// SVGs are already embedded inline in the source.
async fn compile_typst_to_pdf(typst_source: &str) -> Result<Vec<u8>, String> {
    match compile_typst_to_pdf_js(typst_source).await {
        Ok(js_val) => {
            let array = js_sys::Uint8Array::new(&js_val);
            Ok(array.to_vec())
        }
        Err(e) => {
            let msg = e.as_string().unwrap_or_else(|| "Unknown error".to_string());
            Err(msg)
        }
    }
}

use crate::i18n::{use_locale, LanguageSelectorCompact};
use eulumdat_i18n::Locale;

use super::area_designer::AreaDesigner;
use super::beam_angle_diagram::BeamAngleDiagram;
use super::bevy_scene::BevySceneViewer;
use super::bim_panel::{has_bim_data, BimPanel, BimPanelEmpty};
use super::bug_rating::BugRating;
use super::butterfly_3d::Butterfly3D;
use super::cartesian_diagram::CartesianDiagram;
use super::compare_panel::ComparePanel;
use super::cone_diagram::{ConeDiagramView, ConeIlluminanceTableView};
use super::dashboard::Dashboard;
use super::data_table::DataTable;
use super::diagram_zoom::DiagramZoom;
use super::floodlight_cartesian::FloodlightCartesian;
use super::goniosim::GonioSimDemo;
use super::greenhouse_diagram::GreenhouseDiagramView;
use super::intensity_heatmap::IntensityHeatmap;
use super::isocandela_diagram::IsocandelaDiagramView;
use super::isolux_footprint::IsoluxFootprint;
use super::isolux_isometric::IsoluxIsometric;
use super::lcs_classification::LcsClassification;
use super::maps_designer::MapsDesigner;
use super::polar_diagram::PolarDiagram;
use super::skyglow_demo::SkyglowDemo;
use super::spectral_diagram::SpectralDiagramView;
use super::street_launcher::StreetLauncher;
use super::tabs::{DimensionsTab, DirectRatiosTab, GeneralTab, LampSetsTab};
use super::templates::ALL_TEMPLATES;
use super::theme::{ThemeMode, ThemeProvider};
use super::validation_panel::ValidationPanel;
use super::zonal_designer::ZonalDesigner;

// Storage keys - must match eulumdat-bevy/src/viewer/wasm_sync.rs
const LDT_STORAGE_KEY: &str = "eulumdat_current_ldt";
const LDT_TIMESTAMP_KEY: &str = "eulumdat_ldt_timestamp";

/// All features (PDF/Typst export, Maps Designer) are enabled by default.
fn is_export_enabled() -> bool {
    true
}

/// Log color data (CCT/CRI) for debugging spectral synthesis
fn log_color_data(filename: &str, doc: &LuminaireOpticalData) {
    web_sys::console::group_collapsed_1(&format!("[Color Data] {}", filename).into());

    if doc.emitters.is_empty() {
        web_sys::console::warn_1(&"No emitters found".into());
    } else {
        for (i, emitter) in doc.emitters.iter().enumerate() {
            let has_spectral = emitter.spectral_distribution.is_some();
            let cct = emitter.cct;
            let cri = emitter.color_rendering.as_ref().and_then(|cr| cr.ra);

            let status = if has_spectral {
                "Direct SPD data available".to_string()
            } else if cct.is_some() {
                format!(
                    "Can synthesize from CCT={}K{}",
                    cct.unwrap() as i32,
                    cri.map(|c| format!(", CRI={}", c as i32))
                        .unwrap_or_default()
                )
            } else {
                "No color data - will show sample spectrum".to_string()
            };

            web_sys::console::log_1(&format!("Emitter {}: {}", i, status).into());

            // Show raw values for debugging
            if cct.is_none() && !has_spectral {
                web_sys::console::log_1(
                    &"  Tip: CCT parsed from LDT 'color_appearance' field (e.g., '3000K', 'tw/6500', 'warm white')".into()
                );
                web_sys::console::log_1(
                    &"  Tip: CRI parsed from LDT 'color_rendering_group' field (e.g., '1B/86', 'Ra>90', '80')".into()
                );
            }
        }
    }

    web_sys::console::group_end();
}

/// Log color data with raw values from the LDC side (`Eulumdat` lamp sets)
/// alongside the derived ATLA emitter view.
fn log_color_data_from_ldc(filename: &str, ldc: &Eulumdat, doc: &LuminaireOpticalData) {
    web_sys::console::group_1(&format!("[Color Data] {}", filename).into());

    for (i, lamp_set) in ldc.lamp_sets.iter().enumerate() {
        web_sys::console::log_1(&format!("Lamp Set {}:", i).into());
        web_sys::console::log_1(
            &format!("  Raw color_appearance: '{}'", lamp_set.color_appearance).into(),
        );
        web_sys::console::log_1(
            &format!(
                "  Raw color_rendering_group: '{}'",
                lamp_set.color_rendering_group
            )
            .into(),
        );
    }

    if let Some(emitter) = doc.emitters.first() {
        let cct = emitter.cct;
        let cri = emitter.color_rendering.as_ref().and_then(|cr| cr.ra);
        let has_spectral = emitter.spectral_distribution.is_some();

        if has_spectral {
            web_sys::console::log_1(&"  → Direct SPD data available".into());
        } else if let Some(cct_val) = cct {
            web_sys::console::log_1(
                &format!(
                    "  → Parsed: CCT={}K, CRI={:?}",
                    cct_val as i32,
                    cri.map(|c| c as i32)
                )
                .into(),
            );
            web_sys::console::log_1(&"  → Can synthesize spectrum!".into());
        } else {
            web_sys::console::warn_1(&"  → Could not parse CCT - showing sample spectrum".into());
            web_sys::console::log_1(&"  Supported formats:".into());
            web_sys::console::log_1(
                &"    CCT: '3000K', '4000', 'tw/6500', 'ww/2700', 'warm white', 'daylight'".into(),
            );
            web_sys::console::log_1(&"    CRI: '1B/86', 'Ra>90', '80', '1A', '1B', '2A'".into());
        }
    }

    web_sys::console::group_end();
}

/// Top-level view mode: Dashboard overview vs full Editor
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum ViewMode {
    #[default]
    Dashboard,
    Editor,
}

/// Main tab groups
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum MainTab {
    #[default]
    Info,
    Data,
    Diagrams,
    Analysis,
    Validation,
    Compare,
    Bim,
    Scene3D,
    AreaDesigner,
    ZonalDesigner,
    MapsDesigner,
    GonioSim,
    StreetDesign,
}

/// Sub-tabs within each main tab group
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum Tab {
    // Info group
    #[default]
    General,
    Dimensions,
    LampSets,
    DirectRatios,
    // Data group
    Intensity,
    // Diagrams group
    Diagram2D,
    Diagram3D,
    Heatmap,
    Cone,
    FloodlightVH,
    FloodlightIsolux,
    FloodlightIsoView,
    FloodlightIsocandela,
    // Analysis group
    Spectral,
    Greenhouse,
    BugRating,
    Lcs,
    // Validation group (single tab, no sub-tabs)
    ValidationTab,
    // Compare group (single tab, no sub-tabs)
    CompareTab,
    // BIM group (single tab, no sub-tabs)
    BimTab,
    // Scene 3D group (single tab, no sub-tabs)
    Scene3DTab,
    // Area Designer group (single tab, no sub-tabs)
    AreaDesignerTab,
    // Zonal Designer group (single tab, no sub-tabs)
    ZonalDesignerTab,
    // Maps Designer group (single tab, no sub-tabs)
    MapsDesignerTab,
    // GonioSim group (single tab, no sub-tabs)
    GonioSimTab,
    // Street Design group (single tab, no sub-tabs) — lazy-loaded companion
    StreetDesignTab,
}

impl Tab {
    /// Get the main tab group this sub-tab belongs to
    pub fn main_tab(&self) -> MainTab {
        match self {
            Tab::General | Tab::Dimensions | Tab::LampSets | Tab::DirectRatios => MainTab::Info,
            Tab::Intensity => MainTab::Data,
            Tab::Diagram2D
            | Tab::Diagram3D
            | Tab::Heatmap
            | Tab::Cone
            | Tab::FloodlightVH
            | Tab::FloodlightIsolux
            | Tab::FloodlightIsoView
            | Tab::FloodlightIsocandela => MainTab::Diagrams,
            Tab::Spectral | Tab::Greenhouse | Tab::BugRating | Tab::Lcs => MainTab::Analysis,
            Tab::ValidationTab => MainTab::Validation,
            Tab::CompareTab => MainTab::Compare,
            Tab::BimTab => MainTab::Bim,
            Tab::Scene3DTab => MainTab::Scene3D,
            Tab::AreaDesignerTab => MainTab::AreaDesigner,
            Tab::ZonalDesignerTab => MainTab::ZonalDesigner,
            Tab::MapsDesignerTab => MainTab::MapsDesigner,
            Tab::GonioSimTab => MainTab::GonioSim,
            Tab::StreetDesignTab => MainTab::StreetDesign,
        }
    }

    /// Get the default sub-tab for a main tab
    pub fn default_for_main(main: MainTab) -> Tab {
        match main {
            MainTab::Info => Tab::General,
            MainTab::Data => Tab::Intensity,
            MainTab::Diagrams => Tab::Diagram2D,
            MainTab::Analysis => Tab::Spectral,
            MainTab::Validation => Tab::ValidationTab,
            MainTab::Compare => Tab::CompareTab,
            MainTab::Bim => Tab::BimTab,
            MainTab::Scene3D => Tab::Scene3DTab,
            MainTab::AreaDesigner => Tab::AreaDesignerTab,
            MainTab::ZonalDesigner => Tab::ZonalDesignerTab,
            MainTab::MapsDesigner => Tab::MapsDesignerTab,
            MainTab::GonioSim => Tab::GonioSimTab,
            MainTab::StreetDesign => Tab::StreetDesignTab,
        }
    }

    /// Get all sub-tabs for a main tab
    pub fn tabs_for_main(main: MainTab) -> &'static [Tab] {
        match main {
            MainTab::Info => &[
                Tab::General,
                Tab::Dimensions,
                Tab::LampSets,
                Tab::DirectRatios,
            ],
            MainTab::Data => &[Tab::Intensity],
            MainTab::Diagrams => &[
                Tab::Diagram2D,
                Tab::Diagram3D,
                Tab::Heatmap,
                Tab::Cone,
                Tab::FloodlightVH,
                Tab::FloodlightIsolux,
                Tab::FloodlightIsoView,
                Tab::FloodlightIsocandela,
            ],
            MainTab::Analysis => &[Tab::Spectral, Tab::Greenhouse, Tab::BugRating, Tab::Lcs],
            MainTab::Validation => &[Tab::ValidationTab],
            MainTab::Compare => &[Tab::CompareTab],
            MainTab::Bim => &[Tab::BimTab],
            MainTab::Scene3D => &[Tab::Scene3DTab],
            MainTab::AreaDesigner => &[Tab::AreaDesignerTab],
            MainTab::ZonalDesigner => &[Tab::ZonalDesignerTab],
            MainTab::MapsDesigner => &[Tab::MapsDesignerTab],
            MainTab::GonioSim => &[Tab::GonioSimTab],
            MainTab::StreetDesign => &[Tab::StreetDesignTab],
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum DiagramType {
    #[default]
    Polar,
    Cartesian,
    BeamAngle,
}

/// Create a default ATLA document for new files
fn create_default_atla() -> LuminaireOpticalData {
    let mut doc = LuminaireOpticalData::new();
    doc.header.manufacturer = Some("New Luminaire".to_string());
    doc.header.description = Some("Default luminaire".to_string());

    // Create default emitter with intensity distribution
    let g_angles: Vec<f64> = (0..19).map(|i| i as f64 * 5.0).collect();
    let intensities = vec![vec![
        100.0, 99.0, 96.0, 91.0, 84.0, 75.0, 64.0, 51.0, 36.0, 25.0, 16.0, 9.0, 4.0, 2.0, 1.0, 0.5,
        0.2, 0.1, 0.0,
    ]];

    doc.emitters.push(Emitter {
        quantity: 1,
        description: Some("LED".to_string()),
        rated_lumens: Some(1000.0),
        input_watts: Some(10.0),
        cct: Some(3000.0),
        color_rendering: Some(eulumdat::atla::ColorRendering {
            ra: Some(80.0),
            ..Default::default()
        }),
        intensity_distribution: Some(IntensityDistribution {
            horizontal_angles: vec![0.0],
            vertical_angles: g_angles,
            intensities,
            ..Default::default()
        }),
        ..Default::default()
    });

    doc
}

fn detect_system_theme() -> ThemeMode {
    super::theme::detect_system_theme()
}

/// Signal type for the unit system context.
pub type UnitSystemSignal = (ReadSignal<UnitSystem>, WriteSignal<UnitSystem>);

/// Get the unit system signal from context.
pub fn use_unit_system() -> ReadSignal<UnitSystem> {
    let (unit_system, _) = use_context::<UnitSystemSignal>().expect("UnitSystem context not found");
    unit_system
}

/// Load unit system from URL param (`?units=imperial`), then localStorage, defaulting to Metric.
fn load_unit_system() -> UnitSystem {
    // URL param takes priority
    if let Some(val) = crate::i18n::get_url_param("units") {
        if val.eq_ignore_ascii_case("imperial") || val.eq_ignore_ascii_case("imp") {
            return UnitSystem::Imperial;
        }
        if val.eq_ignore_ascii_case("metric") || val.eq_ignore_ascii_case("si") {
            return UnitSystem::Metric;
        }
    }
    // Then localStorage
    if let Some(window) = web_sys::window() {
        if let Ok(Some(storage)) = window.local_storage() {
            if let Ok(Some(val)) = storage.get_item("eulumdat_unit_system") {
                if val == "imperial" {
                    return UnitSystem::Imperial;
                }
            }
        }
    }
    UnitSystem::Metric
}

/// Save unit system to localStorage.
fn save_unit_system(units: UnitSystem) {
    if let Some(window) = web_sys::window() {
        if let Ok(Some(storage)) = window.local_storage() {
            let val = match units {
                UnitSystem::Metric => "metric",
                UnitSystem::Imperial => "imperial",
            };
            let _ = storage.set_item("eulumdat_unit_system", val);
        }
    }
}

use super::templates::Template;

/// Load a template file (content is lazily fetched from templates WASM module)
fn load_template(
    template: &Template,
    set_atla_doc: WriteSignal<LuminaireOpticalData>,
    set_current_file: WriteSignal<Option<String>>,
    set_selected_lamp_set: WriteSignal<usize>,
    set_templates_loading: WriteSignal<bool>,
    rotate_c_planes: ReadSignal<bool>,
) {
    use super::templates::TemplateFormat;

    let ext = match template.format {
        TemplateFormat::Ldt => "ldt",
        TemplateFormat::IesLm63 => "ies",
        TemplateFormat::AtlaXml => "xml",
        TemplateFormat::AtlaJson => "json",
    };
    let filename = format!(
        "{}.{}",
        template
            .name
            .to_lowercase()
            .replace(' ', "_")
            .replace("(", "")
            .replace(")", ""),
        ext
    );

    let id = template.id.to_string();
    let format = template.format;

    set_templates_loading.set(true);

    web_sys::console::log_1(
        &format!("[Editor] load_template: id='{}', file='{}'", id, filename).into(),
    );

    if let Some(content) = eulumdat_wasm_templates::get_template_content(&id) {
        web_sys::console::log_1(
            &format!("[Editor] template content loaded: {} chars", content.len()).into(),
        );
        match format {
            TemplateFormat::Ldt => {
                match Eulumdat::parse(&content) {
                    Ok(ldc) => {
                        web_sys::console::log_1(
                            &format!("[Editor] LDT parsed: '{}'", ldc.luminaire_name).into(),
                        );
                        // Build the atla doc from the lossless LDC, then
                        // register the LDC as the pristine source so the
                        // save-Effect emits the lossless LDT text (instead
                        // of the lossy atla-round-trip) until the user
                        // actually edits the document.
                        let doc = LuminaireOpticalData::from_eulumdat(&ldc);
                        record_pristine_ldc(&ldc, &doc.to_eulumdat());
                        set_atla_doc.set(doc);
                        set_current_file.set(Some(filename));
                        set_selected_lamp_set.set(0);
                    }
                    Err(e) => {
                        web_sys::console::error_1(
                            &format!("[Editor] LDT parse failed: {e}").into(),
                        );
                    }
                }
            }
            TemplateFormat::IesLm63 => {
                let opts = eulumdat::IesImportOptions {
                    rotate_c_planes: if rotate_c_planes.get_untracked() {
                        90.0
                    } else {
                        0.0
                    },
                };
                if let Ok(ldc) = IesParser::parse_with_options(&content, &opts) {
                    let doc = LuminaireOpticalData::from_eulumdat(&ldc);
                    record_pristine_ldc(&ldc, &doc.to_eulumdat());
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(filename));
                    set_selected_lamp_set.set(0);
                }
            }
            TemplateFormat::AtlaXml => {
                if let Ok(doc) = eulumdat::atla::xml::parse(&content) {
                    clear_pristine_ldc();
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(filename));
                    set_selected_lamp_set.set(0);
                }
            }
            TemplateFormat::AtlaJson => {
                if let Ok(doc) = eulumdat::atla::json::parse(&content) {
                    clear_pristine_ldc();
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(filename));
                    set_selected_lamp_set.set(0);
                }
            }
        }
    } else {
        web_sys::console::error_1(
            &format!(
                "[Editor] Template '{}' not found — templates module may not be loaded yet",
                id
            )
            .into(),
        );
    }
    set_templates_loading.set(false);
}

/// Get localized template name
fn get_template_name(template_name: &str, locale: &Locale) -> String {
    match template_name {
        "Downlight" => locale.ui.template.downlight.clone(),
        "Projector" => locale.ui.template.projector.clone(),
        "Linear Luminaire" => locale.ui.template.linear.clone(),
        "Fluorescent Luminaire" => locale.ui.template.fluorescent.clone(),
        "Road Luminaire" => locale.ui.template.road.clone(),
        "Floor Uplight" => locale.ui.template.uplight.clone(),
        "_atla Fluorescent (XML)" => locale.ui.template.atla_fluorescent_xml.clone(),
        "_atla Fluorescent (JSON)" => locale.ui.template.atla_fluorescent_json.clone(),
        "_atla Grow Light (Full Spectrum)" => locale.ui.template.atla_grow_light_fs.clone(),
        "_atla Grow Light (Red/Blue)" => locale.ui.template.atla_grow_light_rb.clone(),
        "_atla Halogen Lamp (IR)" => locale.ui.template.halogen.clone(),
        "_atla Incandescent (IR)" => locale.ui.template.incandescent.clone(),
        "_atla Heat Lamp (High IR)" => locale.ui.template.heat_lamp.clone(),
        "_atla UV Blacklight (UV-A)" => locale.ui.template.uv_blacklight.clone(),
        _ => template_name.to_string(),
    }
}

/// Replace file extension with a new one
fn replace_extension(filename: &str, new_ext: &str) -> String {
    if let Some(dot_pos) = filename.rfind('.') {
        format!("{}.{}", &filename[..dot_pos], new_ext)
    } else {
        format!("{}.{}", filename, new_ext)
    }
}

// Save-to-storage machinery.
//
// The atla→ldc converter is lossy for some real files (e.g.
// `road_luminaire.ldt` — extra intensity rows, wrong symmetry). To protect
// consumers (Bevy 3D viewer, Street designer), callers that have a
// lossless `Eulumdat` already (freshly-parsed template or uploaded LDT
// file) register it via `record_pristine_ldc`. The ATLA Effect then
// prefers the pristine value while the atla doc still represents the
// same luminaire.
thread_local! {
    /// Last known lossless LDC snapshot + the atla-round-tripped LDT
    /// serialization it maps to. While the current atla_doc still
    /// serializes to that same string, we keep writing the pristine
    /// version to storage. A genuine user edit (which *changes* the atla
    /// serialization) invalidates this cache and we fall back to writing
    /// whatever the atla-derived Eulumdat produces.
    static PRISTINE_LDC: std::cell::RefCell<Option<PristineLdc>> =
        const { std::cell::RefCell::new(None) };
}

struct PristineLdc {
    /// Lossless LDT text — what we actually want to store.
    lossless_text: String,
    /// The atla-round-tripped text at the time we recorded the pristine
    /// value. Used as the cache-validity key.
    atla_roundtrip_text: String,
    /// For diagnostic logging.
    luminaire_name: String,
}

/// Record a freshly-parsed, lossless LDC. The ATLA save-Effect will use
/// this snapshot instead of the lossy atla-round-trip as long as the
/// user hasn't edited the document.
fn record_pristine_ldc(ldc: &Eulumdat, atla_derived: &Eulumdat) {
    let pristine = PristineLdc {
        lossless_text: ldc.to_ldt(),
        atla_roundtrip_text: atla_derived.to_ldt(),
        luminaire_name: ldc.luminaire_name.clone(),
    };
    PRISTINE_LDC.with(|cell| *cell.borrow_mut() = Some(pristine));
}

/// Clear the pristine cache — call on `New File` / default-reset paths
/// so we don't keep sourcing the previous luminaire.
fn clear_pristine_ldc() {
    PRISTINE_LDC.with(|cell| cell.borrow_mut().take());
}

/// Write the current LDC to localStorage.
///
/// If a pristine snapshot is on file AND the atla-derived value currently
/// round-trips to the same text we recorded, we write the pristine text
/// instead — preserving fidelity through the atla round-trip.
fn save_to_storage(atla_derived: &Eulumdat) {
    let atla_text = atla_derived.to_ldt();

    let (ldt_string, source_label, luminaire_name) = PRISTINE_LDC.with(|cell| {
        let guard = cell.borrow();
        match guard.as_ref() {
            Some(p) if p.atla_roundtrip_text == atla_text => (
                p.lossless_text.clone(),
                "pristine",
                p.luminaire_name.clone(),
            ),
            _ => (
                atla_text,
                "atla-derived",
                atla_derived.luminaire_name.clone(),
            ),
        }
    });

    if let Some(window) = web_sys::window() {
        if let Ok(Some(storage)) = window.local_storage() {
            let _ = storage.set_item(LDT_STORAGE_KEY, &ldt_string);
            let timestamp = js_sys::Date::now().to_string();
            let _ = storage.set_item(LDT_TIMESTAMP_KEY, &timestamp);
            web_sys::console::log_1(
                &format!(
                    "[Editor] saved to storage ({source_label}): '{}' ({} chars, ts={})",
                    luminaire_name,
                    ldt_string.len(),
                    timestamp
                )
                .into(),
            );

            // Save as JSON for Maps Designer (with intensities for calculations)
            if let Ok(json) = serde_json::to_string(&serde_json::json!({
                "intensities": atla_derived.intensities,
                "c_angles": atla_derived.c_angles,
                "g_angles": atla_derived.g_angles,
                "lumens": atla_derived.total_luminous_flux(),
                "luminaire_name": atla_derived.luminaire_name,
            })) {
                let _ = storage.set_item("eulumdat_current_json", &json);
            }
        }
    }
}

#[component]
pub fn App() -> impl IntoView {
    // Check for ?wasm= query parameter — render standalone WASM demos
    if let Some(demo) = crate::i18n::get_url_param("wasm") {
        // `obscura_demo` is the legacy URL kept for backwards compatibility
        // with existing links / videos. New links use `skyglow_demo`.
        if demo == "skyglow_demo" || demo == "obscura_demo" {
            return view! { <SkyglowDemo /> }.into_any();
        }
        if demo == "goniosim" {
            return view! { <GonioSimDemo /> }.into_any();
        }
    }

    // Primary state: ATLA document (source of truth)
    let (atla_doc, set_atla_doc) = signal(create_default_atla());

    // Secondary state: light distribution curve (LDC) view for existing
    // components. Currently an `Eulumdat` value, derived from the ATLA
    // source of truth and synced back to ATLA on downstream edits.
    let (ldc, set_ldc_internal) = signal(create_default_atla().to_eulumdat());

    // Sync ATLA → LDC whenever ATLA changes
    Effect::new(move |_| {
        set_ldc_internal.set(atla_doc.get().to_eulumdat());
    });

    // Custom setter that syncs LDC changes back to ATLA
    let set_ldc = set_ldc_internal;

    // Also sync LDC → ATLA (for when child components modify ldc)
    Effect::new(move |_| {
        let current_ldc = ldc.get();
        let current_atla_as_ldc = atla_doc.get_untracked().to_eulumdat();
        // Only update if actually different (avoid infinite loop)
        if current_ldc.to_ldt() != current_atla_as_ldc.to_ldt() {
            set_atla_doc.set(LuminaireOpticalData::from_eulumdat(&current_ldc));
        }
    });

    let (view_mode, set_view_mode) = signal(ViewMode::default());
    let (current_file, set_current_file) = signal::<Option<String>>(None);
    // If URL hash starts with #designer, open the designer tab directly
    let initial_tab = web_sys::window()
        .and_then(|w| w.location().hash().ok())
        .map(|h| {
            if h.starts_with("#designer") {
                Tab::AreaDesignerTab
            } else {
                Tab::default()
            }
        })
        .unwrap_or_default();
    let (active_tab, set_active_tab) = signal(initial_tab);
    let (selected_lamp_set, set_selected_lamp_set) = signal(0_usize);
    let (templates_loading, set_templates_loading) = signal(false);

    // Compare panel: File B state lives here so it persists across tab switches
    let (compare_ldc_b, set_compare_ldc_b) = signal::<Option<Eulumdat>>(None);
    let (compare_label_b, set_compare_label_b) = signal::<Option<String>>(None);

    // Derive the active main tab from the active sub-tab
    let active_main_tab = Memo::new(move |_| active_tab.get().main_tab());

    // i18n locale for UI strings
    let locale = use_locale();
    let (drag_active, set_drag_active) = signal(false);
    let (diagram_type, set_diagram_type) = signal(DiagramType::default());
    let (mounting_height, set_mounting_height) = signal(3.0_f64); // Default 3m mounting height for cone diagram
    let (selected_c_plane, set_selected_c_plane) = signal(None::<f64>); // None = overall, Some(angle) = specific C-plane
    let (greenhouse_height, set_greenhouse_height) = signal(2.0_f64); // Default 2m for greenhouse PPFD
    let (theme_mode, set_theme_mode) = signal(detect_system_theme());
    let (unit_system, set_unit_system) = signal(load_unit_system());
    // Save unit system to localStorage on change
    Effect::new(move |_| {
        save_unit_system(unit_system.get());
    });
    provide_context((unit_system, set_unit_system));
    let (show_about, set_show_about) = signal(false);

    // C-plane rotation for IES↔LDT axis correction (EU C0‖length vs US C0⊥length)
    let (rotate_c_planes, set_rotate_c_planes) = signal(false);

    // Check if PDF/Typst export is enabled (via secret URL)
    let export_enabled = is_export_enabled();

    // Save to localStorage whenever the atla document changes. Converts
    // back to Eulumdat at the boundary — callers that *know* they have a
    // lossless Eulumdat already (e.g. freshly-parsed template file) should
    // call `save_to_storage` directly with that value first, so consumers
    // see the lossless form even if this Effect fires a moment later.
    Effect::new(move |_| {
        save_to_storage(&atla_doc.get().to_eulumdat());
    });

    // File loading helper - ALL formats convert to ATLA (lossless)
    let load_file_content = move |name: String, content: String| {
        let lower_name = name.to_lowercase();
        let is_ies = lower_name.ends_with(".ies");
        let is_atla_xml = lower_name.ends_with(".xml");
        let is_atla_json = lower_name.ends_with(".json");
        let is_ldt = lower_name.ends_with(".ldt");
        let is_spdx = lower_name.ends_with(".spdx");
        // OxyTech LITESTAR exports — `.oxl` carries photometry, `.oxc`
        // is a commercial-only sibling. Both share the LitePack XML
        // schema; our parser handles them identically.
        let is_oxl = lower_name.ends_with(".oxl") || lower_name.ends_with(".oxc");

        // Parse to ATLA format (source of truth)
        if is_spdx {
            // SPDX (IES TM-27-14) → ATLA (spectral only, no photometric data)
            match eulumdat::atla::spdx::parse(&content) {
                Ok(spdx_data) => {
                    // Log warnings about missing data
                    let warnings = eulumdat::atla::spdx::get_warnings(&spdx_data);
                    for warning in &warnings {
                        web_sys::console::warn_1(&format!("SPDX: {}", warning).into());
                    }

                    let doc = eulumdat::atla::spdx::to_atla(&spdx_data);
                    clear_pristine_ldc();
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(name));
                    set_selected_lamp_set.set(0);

                    // Show first warning to user
                    if let Some(first_warning) = warnings.first() {
                        web_sys::console::info_1(
                            &format!("Loaded SPDX file (spectral data only): {}", first_warning)
                                .into(),
                        );
                    }
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("Failed to parse SPDX: {}", e).into());
                }
            }
        } else if is_ies {
            // IES → Eulumdat → ATLA (with optional C-plane rotation)
            let opts = eulumdat::IesImportOptions {
                rotate_c_planes: if rotate_c_planes.get_untracked() {
                    90.0
                } else {
                    0.0
                },
            };
            match IesParser::parse_with_options(&content, &opts) {
                Ok(ldc) => {
                    let doc = LuminaireOpticalData::from_eulumdat(&ldc);
                    log_color_data_from_ldc(&name, &ldc, &doc);
                    record_pristine_ldc(&ldc, &doc.to_eulumdat());
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(name));
                    set_selected_lamp_set.set(0);
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("Failed to parse IES: {}", e).into());
                }
            }
        } else if is_atla_xml {
            // ATLA XML → ATLA (direct, no conversion)
            match eulumdat::atla::xml::parse(&content) {
                Ok(doc) => {
                    log_color_data(&name, &doc);
                    clear_pristine_ldc();
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(name));
                    set_selected_lamp_set.set(0);
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("Failed to parse ATLA XML: {}", e).into());
                }
            }
        } else if is_atla_json {
            // ATLA JSON → ATLA (direct, no conversion)
            match eulumdat::atla::json::parse(&content) {
                Ok(doc) => {
                    log_color_data(&name, &doc);
                    clear_pristine_ldc();
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(name));
                    set_selected_lamp_set.set(0);
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("Failed to parse ATLA JSON: {}", e).into());
                }
            }
        } else if is_ldt {
            // LDT file → LDC (in-memory distribution) → ATLA. Register
            // the pristine LDC so the atla save-Effect emits the lossless
            // LDT text until the user edits the document.
            match Eulumdat::parse(&content) {
                Ok(ldc) => {
                    let doc = LuminaireOpticalData::from_eulumdat(&ldc);
                    log_color_data_from_ldc(&name, &ldc, &doc);
                    record_pristine_ldc(&ldc, &doc.to_eulumdat());
                    set_atla_doc.set(doc);
                    set_current_file.set(Some(name));
                    set_selected_lamp_set.set(0);
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("Failed to parse LDT: {}", e).into());
                }
            }
        } else if is_oxl {
            // OXL/OXC → multi-luminaire LitePack package → ATLA. We
            // import the first luminaire of the package; if the package
            // carries more, the others are reachable today only via the
            // library API (`eulumdat::atla::oxl::parse`). A "pick which
            // luminaire" picker is a deferred UX task.
            match eulumdat::atla::oxl::parse(&content) {
                Ok(pkg) => {
                    if pkg.luminaires.is_empty() {
                        web_sys::console::warn_1(
                            &"OXL/OXC contains no luminaires (commercial-only file?)".into(),
                        );
                    } else {
                        if pkg.luminaires.len() > 1 {
                            web_sys::console::warn_1(
                                &format!(
                                    "OXL package contains {} luminaires; loading the first only.",
                                    pkg.luminaires.len()
                                )
                                .into(),
                            );
                        }
                        let doc = pkg.luminaires.into_iter().next().unwrap();
                        log_color_data(&name, &doc);
                        clear_pristine_ldc();
                        set_atla_doc.set(doc);
                        set_current_file.set(Some(name));
                        set_selected_lamp_set.set(0);
                    }
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("Failed to parse OXL: {}", e).into());
                }
            }
        } else {
            web_sys::console::error_1(&"Unknown file format".into());
        }
    };

    // Handlers
    let on_new_file = move |_| {
        clear_pristine_ldc();
        set_atla_doc.set(create_default_atla());
        set_current_file.set(None);
        set_selected_lamp_set.set(0);
    };

    let on_save_ldt = move |_| {
        // Export from ATLA → LDT
        let content = atla_doc.get().to_eulumdat().to_ldt();
        let filename = current_file
            .get()
            .map(|f| replace_extension(&f, "ldt"))
            .unwrap_or_else(|| "luminaire.ldt".to_string());
        super::file_handler::download_file(&filename, &content, "text/plain");
    };

    let on_export_ies = move |_| {
        // Export from ATLA → Eulumdat → IES
        let opts = eulumdat::IesExportOptions {
            rotate_c_planes: if rotate_c_planes.get() { -90.0 } else { 0.0 },
            ..Default::default()
        };
        let content =
            eulumdat::IesExporter::export_with_options(&atla_doc.get().to_eulumdat(), &opts);
        let filename = current_file
            .get()
            .map(|f| replace_extension(&f, "ies"))
            .unwrap_or_else(|| "luminaire.ies".to_string());
        super::file_handler::download_ies(&filename, &content);
    };

    let on_export_atla_xml = move |_| {
        // Export directly from ATLA (no conversion needed!)
        match eulumdat::atla::xml::write(&atla_doc.get()) {
            Ok(content) => {
                let filename = current_file
                    .get()
                    .map(|f| replace_extension(&f, "xml"))
                    .unwrap_or_else(|| "luminaire.xml".to_string());
                super::file_handler::download_atla_xml(&filename, &content);
            }
            Err(e) => {
                web_sys::console::error_1(&format!("Failed to export ATLA XML: {}", e).into());
            }
        }
    };

    let on_export_atla_json = move |_| {
        // Export directly from ATLA (no conversion needed!)
        match eulumdat::atla::json::write(&atla_doc.get()) {
            Ok(content) => {
                let filename = current_file
                    .get()
                    .map(|f| replace_extension(&f, "json"))
                    .unwrap_or_else(|| "luminaire.json".to_string());
                super::file_handler::download_atla_json(&filename, &content);
            }
            Err(e) => {
                web_sys::console::error_1(&format!("Failed to export ATLA JSON: {}", e).into());
            }
        }
    };

    let on_export_report_typ = move |_| {
        // Generate Typst report from the current LDT
        let ldc_val = ldc.get();
        let generator = ReportGenerator::new(&ldc_val);
        let content = generator.generate_typst(&ReportOptions::default());
        let filename = current_file
            .get()
            .map(|f| replace_extension(&f, "typ"))
            .unwrap_or_else(|| "photometric_report.typ".to_string());
        super::file_handler::download_file(&filename, &content, "text/plain");
    };

    // Signal to track PDF export loading state
    let (pdf_exporting, set_pdf_exporting) = signal(false);

    let on_export_report_pdf = move |_| {
        // Generate Typst report with inline SVGs and compile to PDF via WASM
        let ldc_val = ldc.get();
        let (typst_source, _) =
            eulumdat_typst::generate_typst_with_files(&ldc_val, &ReportOptions::default().sections);

        let filename = current_file
            .get()
            .map(|f| replace_extension(&f, "pdf"))
            .unwrap_or_else(|| "photometric_report.pdf".to_string());

        set_pdf_exporting.set(true);

        // Compile using typst WASM
        wasm_bindgen_futures::spawn_local(async move {
            match compile_typst_to_pdf(&typst_source).await {
                Ok(pdf_bytes) => {
                    super::file_handler::download_bytes(&filename, &pdf_bytes, "application/pdf");
                }
                Err(e) => {
                    web_sys::console::error_1(&format!("PDF export failed: {}", e).into());
                    // Fallback: download .typ file
                    web_sys::window()
                        .unwrap()
                        .alert_with_message(&format!(
                            "PDF export failed: {}. Downloading .typ file instead.",
                            e
                        ))
                        .ok();
                    let typ_filename = filename.replace(".pdf", ".typ");
                    super::file_handler::download_file(&typ_filename, &typst_source, "text/plain");
                }
            }
            set_pdf_exporting.set(false);
        });
    };

    // Helper to generate SVG for the current diagram
    let generate_current_svg = move || -> Option<(String, String)> {
        let current_tab = active_tab.get();
        let ldc_val = ldc.get();
        let theme = eulumdat::diagram::SvgTheme::light(); // Use light theme for export

        match current_tab {
            Tab::Diagram2D => {
                // Use the current diagram type (Polar, Cartesian, or BeamAngle)
                match diagram_type.get() {
                    DiagramType::Polar => {
                        let polar = eulumdat::diagram::PolarDiagram::from_eulumdat(&ldc_val);
                        let summary = eulumdat::PhotometricSummary::from_eulumdat(&ldc_val);
                        let svg = polar.to_svg_with_summary(600.0, 600.0, &theme, &summary);
                        Some((svg, "polar_diagram.svg".to_string()))
                    }
                    DiagramType::Cartesian => {
                        let cartesian = eulumdat::diagram::CartesianDiagram::from_eulumdat(
                            &ldc_val, 600.0, 450.0, 8,
                        );
                        let summary = eulumdat::PhotometricSummary::from_eulumdat(&ldc_val);
                        let svg = cartesian.to_svg_with_summary(600.0, 450.0, &theme, &summary);
                        Some((svg, "cartesian_diagram.svg".to_string()))
                    }
                    DiagramType::BeamAngle => {
                        let polar = eulumdat::diagram::PolarDiagram::from_eulumdat(&ldc_val);
                        let analysis =
                            eulumdat::PhotometricCalculations::beam_field_analysis(&ldc_val);
                        let show_both = analysis.is_batwing;
                        let svg = polar.to_svg_with_beam_field_angles(
                            600.0, 600.0, &theme, &analysis, show_both,
                        );
                        Some((svg, "beam_angle_diagram.svg".to_string()))
                    }
                }
            }
            Tab::Diagram3D => {
                // Butterfly/3D diagram
                let butterfly = eulumdat::diagram::ButterflyDiagram::from_eulumdat(
                    &ldc_val, 600.0, 500.0, 60.0,
                );
                let svg = butterfly.to_svg(600.0, 500.0, &theme);
                Some((svg, "butterfly_3d_diagram.svg".to_string()))
            }
            Tab::Heatmap => {
                let heatmap =
                    eulumdat::diagram::HeatmapDiagram::from_eulumdat(&ldc_val, 700.0, 500.0);
                let summary = eulumdat::PhotometricSummary::from_eulumdat(&ldc_val);
                let svg = heatmap.to_svg_with_summary(700.0, 500.0, &theme, &summary);
                Some((svg, "intensity_heatmap.svg".to_string()))
            }
            Tab::Cone => {
                let height = mounting_height.get();
                let cone = match selected_c_plane.get() {
                    Some(c) => {
                        eulumdat::diagram::ConeDiagram::from_eulumdat_for_plane(&ldc_val, height, c)
                    }
                    None => eulumdat::diagram::ConeDiagram::from_eulumdat(&ldc_val, height),
                };
                let loc = locale.get();
                let labels = eulumdat::diagram::ConeDiagramLabels {
                    beam_angle: loc.diagram.cone.beam_angle.clone(),
                    field_angle: loc.diagram.cone.field_angle.clone(),
                    mounting_height: loc.diagram.cone.mounting_height.clone(),
                    beam_diameter: loc.diagram.cone.beam_diameter.clone(),
                    field_diameter: loc.diagram.cone.field_diameter.clone(),
                    intensity_50: loc.diagram.cone.intensity_50.clone(),
                    intensity_10: loc.diagram.cone.intensity_10.clone(),
                    floor: loc.diagram.cone.floor.clone(),
                    meter: loc.diagram.cone.meter.clone(),
                    c_plane_label: loc.diagram.cone.c_plane.clone(),
                };
                let svg = cone.to_svg_with_units(600.0, 450.0, &theme, &labels, unit_system.get());
                Some((svg, "cone_diagram.svg".to_string()))
            }
            Tab::Spectral => {
                // Spectral diagram from ATLA doc
                let doc = atla_doc.get();
                let atla_theme = eulumdat::atla::spectral::SpectralTheme::light();

                // Try to get spectral data
                if let Some(spd) = doc
                    .emitters
                    .iter()
                    .filter_map(|e| e.spectral_distribution.as_ref())
                    .next()
                {
                    let diagram = eulumdat::atla::spectral::SpectralDiagram::from_spectral(spd);
                    let svg = diagram.to_svg(700.0, 400.0, &atla_theme);
                    Some((svg, "spectral_diagram.svg".to_string()))
                } else if let Some(emitter) = doc.emitters.first() {
                    if let Some(cct) = emitter.cct {
                        let cri = emitter.color_rendering.as_ref().and_then(|cr| cr.ra);
                        let spd = eulumdat::atla::spectral::synthesize_spectrum(cct, cri);
                        let diagram =
                            eulumdat::atla::spectral::SpectralDiagram::from_spectral(&spd);
                        let svg = diagram.to_svg(700.0, 400.0, &atla_theme);
                        Some((svg, "spectral_diagram.svg".to_string()))
                    } else {
                        None
                    }
                } else {
                    None
                }
            }
            Tab::Greenhouse => {
                let doc = atla_doc.get();
                let height = greenhouse_height.get();
                let diagram = eulumdat::atla::greenhouse::GreenhouseDiagram::from_atla_with_height(
                    &doc, height,
                );
                let gh_theme = eulumdat::atla::greenhouse::GreenhouseTheme::light();
                let svg = diagram.to_svg(600.0, 450.0, &gh_theme);
                Some((svg, "greenhouse_ppfd.svg".to_string()))
            }
            Tab::BugRating => {
                let diagram = eulumdat::BugDiagram::from_eulumdat(&ldc_val);
                let svg = diagram.to_svg_with_details(600.0, 400.0, &theme);
                Some((svg, "bug_rating.svg".to_string()))
            }
            Tab::Lcs => {
                let diagram = eulumdat::BugDiagram::from_eulumdat(&ldc_val);
                let svg = diagram.to_lcs_svg(600.0, 400.0, &theme);
                Some((svg, "lcs_classification.svg".to_string()))
            }
            Tab::FloodlightVH => {
                let y_scale = eulumdat::diagram::YScale::Linear;
                let diagram = eulumdat::diagram::FloodlightCartesianDiagram::from_eulumdat(
                    &ldc_val, 600.0, 400.0, y_scale,
                );
                let svg = diagram.to_svg(600.0, 400.0, &theme);
                Some((svg, "floodlight_vh.svg".to_string()))
            }
            Tab::FloodlightIsolux => {
                let params = eulumdat::diagram::IsoluxParams::default();
                let diagram =
                    eulumdat::diagram::IsoluxDiagram::from_eulumdat(&ldc_val, 600.0, 500.0, params);
                let svg = diagram.to_svg(600.0, 500.0, &theme);
                Some((svg, "isolux_footprint.svg".to_string()))
            }
            Tab::FloodlightIsoView => {
                let params = eulumdat::diagram::IsoluxParams {
                    area_half_width: 30.0,
                    area_half_depth: 15.0,
                    ..eulumdat::diagram::IsoluxParams::default()
                };
                let diagram = eulumdat::diagram::IsoluxDiagram::from_eulumdat(
                    &ldc_val, 1200.0, 500.0, params,
                );
                let title = if !ldc_val.luminaire_name.is_empty() {
                    format!("ISO view {}", ldc_val.luminaire_name)
                } else {
                    "ISO view".to_string()
                };
                let svg = diagram.to_svg_isometric(
                    1200.0,
                    500.0,
                    &theme,
                    &eulumdat::diagram::IsometricConfig::default(),
                    UnitSystem::default(),
                    &ldc_val,
                    &title,
                );
                Some((svg, "iso_view.svg".to_string()))
            }
            Tab::FloodlightIsocandela => {
                let diagram =
                    eulumdat::diagram::IsocandelaDiagram::from_eulumdat(&ldc_val, 600.0, 500.0);
                let svg = diagram.to_svg(600.0, 500.0, &theme);
                Some((svg, "isocandela_contour.svg".to_string()))
            }
            // Non-diagram tabs - no SVG export available
            Tab::General
            | Tab::Dimensions
            | Tab::LampSets
            | Tab::DirectRatios
            | Tab::Intensity
            | Tab::ValidationTab
            | Tab::CompareTab
            | Tab::BimTab
            | Tab::Scene3DTab
            | Tab::AreaDesignerTab
            | Tab::ZonalDesignerTab
            | Tab::MapsDesignerTab
            | Tab::GonioSimTab
            | Tab::StreetDesignTab => None,
        }
    };

    let on_export_svg = move |_| {
        if let Some((svg_content, default_filename)) = generate_current_svg() {
            let filename = current_file
                .get()
                .map(|f| replace_extension(&f, "svg"))
                .unwrap_or(default_filename);
            super::file_handler::download_svg(&filename, &svg_content);
        } else {
            web_sys::console::warn_1(&"No diagram to export on this tab".into());
        }
    };

    let on_export_png = move |_| {
        if let Some((svg_content, default_filename)) = generate_current_svg() {
            let filename = current_file
                .get()
                .map(|f| replace_extension(&f, "png"))
                .unwrap_or_else(|| default_filename.replace(".svg", ".png"));
            super::file_handler::export_svg_as_png(&filename, &svg_content, 2.0);
        }
    };

    let on_export_jpeg = move |_| {
        if let Some((svg_content, default_filename)) = generate_current_svg() {
            let filename = current_file
                .get()
                .map(|f| replace_extension(&f, "jpg"))
                .unwrap_or_else(|| default_filename.replace(".svg", ".jpg"));
            super::file_handler::export_svg_as_jpeg(&filename, &svg_content, 2.0, 0.92);
        }
    };

    let on_toggle_theme = move |_| {
        set_theme_mode.update(|m| *m = m.toggle());
    };

    let on_file_input = move |ev: ev::Event| {
        let input: HtmlInputElement = ev.target().unwrap().unchecked_into();
        if let Some(files) = input.files() {
            if let Some(file) = files.get(0) {
                let name = file.name();
                let load_content = load_file_content;
                wasm_bindgen_futures::spawn_local(async move {
                    let text = gloo_file::futures::read_as_text(&file.into()).await;
                    if let Ok(content) = text {
                        load_content(name, content);
                    }
                });
            }
        }
    };

    let on_dragover = move |ev: ev::DragEvent| {
        ev.prevent_default();
        set_drag_active.set(true);
    };

    let on_dragleave = move |_: ev::DragEvent| {
        set_drag_active.set(false);
    };

    let on_drop = move |ev: ev::DragEvent| {
        ev.prevent_default();
        set_drag_active.set(false);

        if let Some(data_transfer) = ev.data_transfer() {
            if let Some(files) = data_transfer.files() {
                if let Some(file) = files.get(0) {
                    let name = file.name();
                    let load_content = load_file_content;
                    wasm_bindgen_futures::spawn_local(async move {
                        let text = gloo_file::futures::read_as_text(&file.into()).await;
                        if let Ok(content) = text {
                            load_content(name, content);
                        }
                    });
                }
            }
        }
    };

    // Note: Child components use the `ldc` Memo which derives from atla_doc.
    // When they call set_ldc, it converts back to ATLA internally.

    view! {
        <ThemeProvider mode=theme_mode>
            <div class=move || format!("app {}", theme_mode.get().class_name()) role="application" aria-label="Eulumdat Editor">
                // Skip to main content link for keyboard users
                <a href="#main-content" class="skip-link">{move || locale.get().ui.header.skip_link.clone()}</a>

                // Header with navigation landmark
                <header class="header" role="banner">
                    <h1>{move || locale.get().ui.header.title.clone()}</h1>
                    <div class="header-actions">
                        // File menu dropdown
                        <div class="file-menu">
                            <button class="btn btn-secondary file-menu-toggle">
                                {move || locale.get().ui.header.file.clone()}
                                <span class="dropdown-arrow">" ▾"</span>
                            </button>
                            <div class="file-menu-dropdown">
                                <button class="menu-item" on:click=on_new_file>
                                    {move || locale.get().ui.header.new.clone()}
                                </button>
                                <label class="menu-item" role="button" tabindex="0">
                                    {move || locale.get().ui.header.open.clone()}
                                    <input
                                        type="file"
                                        accept=".ldt,.LDT,.ies,.IES,.xml,.XML,.json,.JSON,.spdx,.SPDX,.oxl,.OXL,.oxc,.OXC"
                                        style="display: none;"
                                        on:change=on_file_input
                                        aria-label="Open LDT, IES, SPDX, XML, or JSON file"
                                    />
                                </label>
                                <div class="menu-divider"></div>
                                <label class="menu-item checkbox-item" title=move || locale.get().ui.header.rotate_c_planes_tooltip.clone()>
                                    <input
                                        type="checkbox"
                                        prop:checked=move || rotate_c_planes.get()
                                        on:change=move |ev| {
                                            let checked = ev.target().unwrap().unchecked_into::<HtmlInputElement>().checked();
                                            set_rotate_c_planes.set(checked);
                                        }
                                    />
                                    {move || format!(" {}", locale.get().ui.header.rotate_c_planes)}
                                </label>
                                <div class="menu-divider"></div>
                                <button class="menu-item" on:click=on_save_ldt>
                                    {move || locale.get().ui.header.save_ldt.clone()}
                                </button>
                                <button class="menu-item" on:click=on_export_ies>
                                    {move || locale.get().ui.header.export_ies.clone()}
                                </button>
                                <button class="menu-item" on:click=on_export_atla_xml>
                                    {move || locale.get().ui.header.atla_xml.clone()}
                                </button>
                                <button class="menu-item" on:click=on_export_atla_json>
                                    {move || locale.get().ui.header.atla_json.clone()}
                                </button>
                                <div class="menu-divider"></div>
                                <button
                                    class=move || {
                                        if generate_current_svg().is_some() { "menu-item" } else { "menu-item disabled" }
                                    }
                                    on:click=on_export_svg
                                    disabled=move || generate_current_svg().is_none()
                                >
                                    {move || {
                                        let tab = active_tab.get();
                                        let loc = locale.get();
                                        let prefix = loc.ui.header.export_svg_prefix.clone();
                                        match tab {
                                            Tab::Diagram2D => match diagram_type.get() {
                                                DiagramType::Polar => format!("{} ({})", prefix, loc.ui.tabs.polar),
                                                DiagramType::Cartesian => format!("{} ({})", prefix, loc.ui.tabs.cartesian),
                                                DiagramType::BeamAngle => format!("{} ({})", prefix, loc.ui.tabs.beam_angle),
                                            },
                                            Tab::Diagram3D => format!("{} ({})", prefix, loc.ui.tabs.diagram_3d),
                                            Tab::Heatmap => format!("{} ({})", prefix, loc.ui.tabs.heatmap),
                                            Tab::Cone => format!("{} ({})", prefix, loc.ui.tabs.cone),
                                            Tab::Spectral => format!("{} ({})", prefix, loc.ui.tabs.spectral),
                                            Tab::Greenhouse => format!("{} ({})", prefix, loc.ui.tabs.greenhouse),
                                            Tab::BugRating => format!("{} ({})", prefix, loc.ui.tabs.bug_rating),
                                            Tab::Lcs => format!("{} ({})", prefix, loc.ui.tabs.lcs),
                                            Tab::FloodlightVH => format!("{} ({})", prefix, loc.ui.tabs.floodlight_vh),
                                            Tab::FloodlightIsolux => format!("{} ({})", prefix, loc.ui.tabs.floodlight_isolux),
                                            Tab::FloodlightIsoView => format!("{} ({})", prefix, loc.ui.tabs.iso_view),
                                            Tab::FloodlightIsocandela => format!("{} ({})", prefix, loc.ui.tabs.floodlight_isocandela),
                                            _ => prefix,
                                        }
                                    }}
                                </button>
                                <button
                                    class=move || {
                                        if generate_current_svg().is_some() { "menu-item" } else { "menu-item disabled" }
                                    }
                                    on:click=on_export_png
                                    disabled=move || generate_current_svg().is_none()
                                >
                                    {move || locale.get().ui.header.export_png.clone()}
                                </button>
                                <button
                                    class=move || {
                                        if generate_current_svg().is_some() { "menu-item" } else { "menu-item disabled" }
                                    }
                                    on:click=on_export_jpeg
                                    disabled=move || generate_current_svg().is_none()
                                >
                                    {move || locale.get().ui.header.export_jpeg.clone()}
                                </button>
                                // PDF/Typst export - only shown on secret URL
                                {move || export_enabled.then(|| view! {
                                    <button
                                        class="menu-item"
                                        on:click=on_export_report_pdf
                                        disabled=move || pdf_exporting.get()
                                        title=move || locale.get().ui.header.export_pdf_tooltip.clone()
                                    >
                                        {move || if pdf_exporting.get() { locale.get().ui.header.generating_pdf.clone() } else { locale.get().ui.header.export_pdf.clone() }}
                                    </button>
                                    <button
                                        class="menu-item"
                                        on:click=on_export_report_typ
                                        title=move || locale.get().ui.header.export_typ_tooltip.clone()
                                    >
                                        {move || locale.get().ui.header.export_typ.clone()}
                                    </button>
                                })}
                                <div class="menu-divider"></div>
                                <a
                                    class="menu-item"
                                    href="https://github.com/holg/eulumdat-rs"
                                    target="_blank"
                                    rel="noopener noreferrer"
                                >
                                    {move || locale.get().ui.header.github.clone()}
                                </a>
                                <button
                                    class="menu-item"
                                    on:click=move |_| set_show_about.set(true)
                                >
                                    {move || locale.get().ui.header.about.clone()}
                                </button>
                            </div>
                        </div>
                        // Templates dropdown (separate)
                        <div class="file-menu">
                            <button class="btn btn-secondary file-menu-toggle">
                                {move || locale.get().ui.header.templates.clone()}
                                <span class="dropdown-arrow">" ▾"</span>
                            </button>
                            <div class="file-menu-dropdown templates-dropdown">
                                {move || {
                                    let l = locale.get();
                                    ALL_TEMPLATES.iter().enumerate().map(|(i, t)| {
                                        let name = get_template_name(t.name, &l);
                                        let idx = i;
                                        view! {
                                            <button
                                                class="menu-item"
                                                title=t.description
                                                on:click=move |_| {
                                                    if let Some(template) = ALL_TEMPLATES.get(idx) {
                                                        load_template(template, set_atla_doc, set_current_file, set_selected_lamp_set, set_templates_loading, rotate_c_planes);
                                                    }
                                                }
                                            >
                                                {name}
                                            </button>
                                        }
                                    }).collect_view()
                                }}
                            </div>
                        </div>
                        // Templates loading indicator
                        {move || templates_loading.get().then(|| view! {
                            <span class="templates-loading">{move || locale.get().ui.header.loading.clone()}</span>
                        })}
                        // Settings
                        <button
                            class="btn btn-secondary theme-toggle"
                            on:click=on_toggle_theme
                            title=move || match theme_mode.get() {
                                ThemeMode::Light => locale.get().ui.header.switch_to_dark.clone(),
                                ThemeMode::Dark => locale.get().ui.header.switch_to_light.clone(),
                            }
                        >
                            {move || match theme_mode.get() {
                                ThemeMode::Light => "🌙",
                                ThemeMode::Dark => "☀️",
                            }}
                        </button>
                        <button
                            class="btn btn-secondary"
                            on:click=move |_| {
                                set_unit_system.update(|u| {
                                    *u = match u {
                                        UnitSystem::Metric => UnitSystem::Imperial,
                                        UnitSystem::Imperial => UnitSystem::Metric,
                                    };
                                });
                            }
                            title=move || match unit_system.get() {
                                UnitSystem::Metric => locale.get().ui.header.switch_to_imperial.clone(),
                                UnitSystem::Imperial => locale.get().ui.header.switch_to_metric.clone(),
                            }
                        >
                            {move || match unit_system.get() {
                                UnitSystem::Metric => locale.get().ui.header.unit_si.clone(),
                                UnitSystem::Imperial => locale.get().ui.header.unit_imp.clone(),
                            }}
                        </button>
                        <LanguageSelectorCompact />
                    </div>
                </header>

                // File info
                {move || current_file.get().map(|filename| view! {
                    <div class="file-info">
                        {locale.get().ui.dropzone.current_file.clone()}" "<strong>{filename}</strong>
                    </div>
                })}

                // Drop zone
                <div
                    class=move || format!("drop-zone{}", if drag_active.get() { " active" } else { "" })
                    on:dragover=on_dragover
                    on:dragleave=on_dragleave
                    on:drop=on_drop
                >
                    <p>{move || locale.get().ui.dropzone.text.clone()}</p>
                </div>

                // View mode switch: Dashboard overview or Editor
                {move || match view_mode.get() {
                    ViewMode::Dashboard => view! {
                        <Dashboard
                            ldc=ldc
                            on_select=Callback::new(move |(new_ldc, name): (Eulumdat, String)| {
                                // The dashboard hands us the lossless
                                // `Eulumdat` it parsed from an embedded
                                // template. Register it as the pristine
                                // source *before* nudging the signals, so
                                // the save-Effect that fires when the LDC
                                // → ATLA sync runs keeps the lossless
                                // bytes instead of an atla-round-tripped
                                // (potentially lossy) re-serialization.
                                let atla = LuminaireOpticalData::from_eulumdat(&new_ldc);
                                record_pristine_ldc(&new_ldc, &atla.to_eulumdat());
                                set_ldc.set(new_ldc);
                                set_current_file.set(Some(name));
                            })
                            on_compare=Callback::new(move |(cmp_ldt, name): (Eulumdat, String)| {
                                set_compare_ldc_b.set(Some(cmp_ldt));
                                set_compare_label_b.set(Some(name));
                            })
                            on_edit=Callback::new(move |_| set_view_mode.set(ViewMode::Editor))
                            on_compare_view=Callback::new(move |_| {
                                set_view_mode.set(ViewMode::Editor);
                                set_active_tab.set(Tab::CompareTab);
                            })
                            on_designer=Callback::new(move |_| {
                                set_view_mode.set(ViewMode::Editor);
                                set_active_tab.set(Tab::AreaDesignerTab);
                            })
                            on_export_pdf=Callback::new(move |export_ldt: Eulumdat| {
                                let (typst_source, _) = eulumdat_typst::generate_typst_with_files(
                                    &export_ldt,
                                    &ReportOptions::default().sections,
                                );
                                let filename = current_file
                                    .get()
                                    .map(|f| replace_extension(&f, "pdf"))
                                    .unwrap_or_else(|| "photometric_report.pdf".to_string());
                                set_pdf_exporting.set(true);
                                wasm_bindgen_futures::spawn_local(async move {
                                    match compile_typst_to_pdf(&typst_source).await {
                                        Ok(pdf_bytes) => {
                                            super::file_handler::download_bytes(
                                                &filename,
                                                &pdf_bytes,
                                                "application/pdf",
                                            );
                                        }
                                        Err(e) => {
                                            web_sys::console::error_1(
                                                &format!("PDF export failed: {}", e).into(),
                                            );
                                            let typ_filename = filename.replace(".pdf", ".typ");
                                            super::file_handler::download_file(
                                                &typ_filename,
                                                &typst_source,
                                                "text/plain",
                                            );
                                        }
                                    }
                                    set_pdf_exporting.set(false);
                                });
                            })
                        />
                    }.into_any(),
                    ViewMode::Editor => view! {
                        <main id="main-content" class="main-content" role="main" aria-label="Editor content">
                            <div class="editor-back-bar">
                                <button
                                    class="btn btn-secondary btn-sm"
                                    on:click=move |_| set_view_mode.set(ViewMode::Dashboard)
                                >
                                    {move || format!("\u{2190} {}", locale.get().dashboard.back)}
                                </button>
                            </div>
                    <div class="panel">
                        // Main Tabs - navigation landmark
                        <nav class="tabs main-tabs" role="tablist" aria-label="Editor sections">
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Info { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Info))
                            >
                                {move || locale.get().ui.tabs.info.clone()}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Data { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Data))
                            >
                                {move || locale.get().ui.tabs.data.clone()}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Diagrams { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Diagrams))
                            >
                                {move || locale.get().ui.tabs.diagrams.clone()}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Analysis { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Analysis))
                            >
                                {move || locale.get().ui.tabs.analysis.clone()}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Validation { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Validation))
                            >
                                {move || locale.get().ui.tabs.validation.clone()}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Compare { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Compare))
                            >
                                {move || locale.get().ui.tabs.compare.clone()}
                            </button>
                            // BIM tab - only shown when file has BIM data
                            {move || {
                                if has_bim_data(&atla_doc.get()) {
                                    Some(view! {
                                        <button
                                            class=move || format!("tab{}", if active_main_tab.get() == MainTab::Bim { " active" } else { "" })
                                            on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Bim))
                                            title=move || locale.get().ui.bim.tooltip.clone()
                                        >
                                            {move || locale.get().ui.tabs.bim.clone()}
                                        </button>
                                    })
                                } else {
                                    None
                                }
                            }}
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::Scene3D { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::Scene3D))
                            >
                                {move || locale.get().ui.tabs.scene_3d.clone()}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::AreaDesigner { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::AreaDesigner))
                            >
                                {move || format!("📐 {}", locale.get().ui.tabs.area_designer)}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::ZonalDesigner { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::ZonalDesigner))
                            >
                                {move || format!("🏢 {}", locale.get().ui.tabs.zonal_designer)}
                            </button>
                            // Maps Designer - only shown on secret URL (requires Google Maps API key)
                            {move || export_enabled.then(|| view! {
                                <button
                                    class=move || format!("tab{}", if active_main_tab.get() == MainTab::MapsDesigner { " active" } else { "" })
                                    on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::MapsDesigner))
                                >
                                    {move || format!("🗺️ {}", locale.get().ui.tabs.maps_designer)}
                                </button>
                            })}
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::GonioSim { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::GonioSim))
                            >
                                {move || format!("🔬 {}", locale.get().goniosim.title)}
                            </button>
                            <button
                                class=move || format!("tab{}", if active_main_tab.get() == MainTab::StreetDesign { " active" } else { "" })
                                on:click=move |_| set_active_tab.set(Tab::default_for_main(MainTab::StreetDesign))
                            >
                                {move || locale.get().ui.tabs.street_design.clone()}
                            </button>
                        </nav>

                        // Sub-tabs (shown only when main tab has multiple sub-tabs)
                        {move || {
                            let main = active_main_tab.get();
                            let sub_tabs = Tab::tabs_for_main(main);
                            if sub_tabs.len() > 1 {
                                Some(view! {
                                    <div class="tabs sub-tabs">
                                        {sub_tabs.iter().map(|&tab| {
                                            let label = match tab {
                                                Tab::General => locale.get().ui.tabs.general.clone(),
                                                Tab::Dimensions => locale.get().ui.tabs.dimensions.clone(),
                                                Tab::LampSets => locale.get().ui.tabs.lamp_sets.clone(),
                                                Tab::DirectRatios => locale.get().ui.tabs.direct_ratios.clone(),
                                                Tab::Intensity => locale.get().ui.tabs.intensity.clone(),
                                                Tab::Diagram2D => locale.get().ui.tabs.diagram_2d.clone(),
                                                Tab::Diagram3D => locale.get().ui.tabs.diagram_3d.clone(),
                                                Tab::Heatmap => locale.get().ui.tabs.heatmap.clone(),
                                                Tab::Cone => locale.get().ui.tabs.cone.clone(),
                                                Tab::Spectral => locale.get().ui.tabs.spectral.clone(),
                                                Tab::Greenhouse => locale.get().ui.tabs.greenhouse.clone(),
                                                Tab::BugRating => locale.get().ui.tabs.bug_rating.clone(),
                                                Tab::Lcs => locale.get().ui.tabs.lcs.clone(),
                                                Tab::FloodlightVH => locale.get().ui.tabs.floodlight_vh.clone(),
                                                Tab::FloodlightIsolux => locale.get().ui.tabs.floodlight_isolux.clone(),
                                                Tab::FloodlightIsoView => locale.get().ui.tabs.iso_view.clone(),
                                                Tab::FloodlightIsocandela => locale.get().ui.tabs.floodlight_isocandela.clone(),
                                                Tab::ValidationTab => locale.get().ui.tabs.validation.clone(),
                                                Tab::CompareTab => locale.get().ui.tabs.compare.clone(),
                                                Tab::BimTab => locale.get().ui.tabs.bim.clone(),
                                                Tab::Scene3DTab => locale.get().ui.tabs.scene_3d.clone(),
                                                Tab::AreaDesignerTab => locale.get().ui.tabs.area_designer.clone(),
                                                Tab::ZonalDesignerTab => locale.get().ui.tabs.zonal_designer.clone(),
                                                Tab::MapsDesignerTab => locale.get().ui.tabs.maps_designer.clone(),
                                                Tab::GonioSimTab => locale.get().goniosim.title.clone(),
                                                Tab::StreetDesignTab => locale.get().ui.tabs.street_design.clone(),
                                            };
                                            view! {
                                                <button
                                                    class=move || format!("tab{}", if active_tab.get() == tab { " active" } else { "" })
                                                    on:click=move |_| set_active_tab.set(tab)
                                                >
                                                    {label}
                                                </button>
                                            }
                                        }).collect_view()}
                                    </div>
                                })
                            } else {
                                None
                            }
                        }}

                        // Tab content
                        <div class="tab-content">
                            {move || match active_tab.get() {
                                Tab::General => view! {
                                    <GeneralTab ldc=ldc set_ldc=set_ldc />
                                }.into_any(),
                                Tab::Dimensions => view! {
                                    <DimensionsTab ldc=ldc set_ldc=set_ldc />
                                }.into_any(),
                                Tab::LampSets => view! {
                                    <LampSetsTab ldc=ldc set_ldc=set_ldc selected=selected_lamp_set set_selected=set_selected_lamp_set />
                                }.into_any(),
                                Tab::DirectRatios => view! {
                                    <DirectRatiosTab ldc=ldc set_ldc=set_ldc />
                                }.into_any(),
                                Tab::Intensity => view! {
                                    <div class="intensity-tab">
                                        <div class="intensity-table-section">
                                            <div class="table-header">
                                                <span>{move || locale.get().ui.intensity.title.clone()}</span>
                                                <span class="table-info">
                                                    {move || {
                                                        let l = ldc.get();
                                                        let template = locale.get().ui.intensity.table_info.clone();
                                                        template.replace("{c_planes}", &l.c_angles.len().to_string())
                                                            .replace("{g_angles}", &l.g_angles.len().to_string())
                                                    }}
                                                </span>
                                            </div>
                                            <DataTable ldc=ldc set_ldc=set_ldc />
                                        </div>
                                    </div>
                                }.into_any(),
                                Tab::Diagram2D => view! {
                                    <div class="diagram-2d-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_2d.clone()}</span>
                                            <div class="diagram-controls">
                                                <div class="diagram-toggle">
                                                    <button
                                                        class=move || format!("btn-toggle{}", if diagram_type.get() == DiagramType::Polar { " active" } else { "" })
                                                        on:click=move |_| set_diagram_type.set(DiagramType::Polar)
                                                    >
                                                        {move || locale.get().ui.diagram.polar.clone()}
                                                    </button>
                                                    <button
                                                        class=move || format!("btn-toggle{}", if diagram_type.get() == DiagramType::Cartesian { " active" } else { "" })
                                                        on:click=move |_| set_diagram_type.set(DiagramType::Cartesian)
                                                    >
                                                        {move || locale.get().ui.diagram.cartesian.clone()}
                                                    </button>
                                                    <button
                                                        class=move || format!("btn-toggle{}", if diagram_type.get() == DiagramType::BeamAngle { " active" } else { "" })
                                                        on:click=move |_| set_diagram_type.set(DiagramType::BeamAngle)
                                                        title=move || locale.get().ui.diagram.beam_angle_tooltip.clone()
                                                    >
                                                        {move || locale.get().ui.diagram.beam_angle.clone()}
                                                    </button>
                                                </div>
                                                <span class="zoom-hint">{move || locale.get().ui.diagram.zoom_hint.clone()}</span>
                                            </div>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                {move || match diagram_type.get() {
                                                    DiagramType::Polar => view! { <PolarDiagram ldc=ldc /> }.into_any(),
                                                    DiagramType::Cartesian => view! { <CartesianDiagram ldc=ldc /> }.into_any(),
                                                    DiagramType::BeamAngle => view! { <BeamAngleDiagram ldc=ldc /> }.into_any(),
                                                }}
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::Diagram3D => view! {
                                    <div class="diagram-3d-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_3d.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.diagram.rotate_hint.clone()}</span>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                <Butterfly3D ldc=ldc />
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::Heatmap => view! {
                                    <div class="heatmap-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_heatmap.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.diagram.zoom_hint.clone()}</span>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                <IntensityHeatmap ldc=ldc />
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::Cone => {
                                    // Get expanded C-plane angles for the slider
                                    let c_angles = Memo::new(move |_| {
                                        let l = ldc.get();
                                        eulumdat::SymmetryHandler::expand_c_angles(&l)
                                    });
                                    let has_variation = Memo::new(move |_| {
                                        eulumdat::diagram::ConeDiagram::has_c_plane_variation(&ldc.get())
                                    });

                                    view! {
                                        <div class="cone-tab">
                                            <div class="diagram-header">
                                                <span class="diagram-title">{move || locale.get().diagram.title.cone.clone()}</span>
                                                <div class="mounting-height-control">
                                                    <label>{move || locale.get().diagram.cone.mounting_height.clone()}</label>
                                                    <input
                                                        type="range"
                                                        prop:min=move || match unit_system.get() { UnitSystem::Metric => "1", _ => "3" }
                                                        prop:max=move || match unit_system.get() { UnitSystem::Metric => "15", _ => "50" }
                                                        prop:step=move || match unit_system.get() { UnitSystem::Metric => "0.5", _ => "1" }
                                                        prop:value=move || unit_system.get().convert_meters(mounting_height.get()).to_string()
                                                        on:input=move |ev| {
                                                            if let Ok(value) = event_target_value(&ev).parse::<f64>() {
                                                                set_mounting_height.set(unit_system.get().to_meters(value));
                                                            }
                                                        }
                                                    />
                                                    <span class="value-display">
                                                        {move || unit_system.get().format_distance(mounting_height.get())}
                                                    </span>
                                                </div>
                                            </div>
                                            // C-plane selector
                                            {move || {
                                                if has_variation.get() {
                                                    let angles = c_angles.get();
                                                    let max_idx = if angles.is_empty() { 0 } else { angles.len() - 1 };
                                                    view! {
                                                        <div class="c-plane-control">
                                                            <label>{move || locale.get().diagram.cone.c_plane.clone()}</label>
                                                            <input
                                                                type="range"
                                                                min="0"
                                                                max=max_idx.to_string()
                                                                step="1"
                                                                prop:value=move || {
                                                                    match selected_c_plane.get() {
                                                                        Some(cp) => {
                                                                            let a = c_angles.get();
                                                                            a.iter().position(|&x| (x - cp).abs() < 0.01)
                                                                                .unwrap_or(0)
                                                                                .to_string()
                                                                        }
                                                                        None => "0".to_string(),
                                                                    }
                                                                }
                                                                on:input=move |ev| {
                                                                    if let Ok(idx) = event_target_value(&ev).parse::<usize>() {
                                                                        let a = c_angles.get();
                                                                        if let Some(&angle) = a.get(idx) {
                                                                            set_selected_c_plane.set(Some(angle));
                                                                        }
                                                                    }
                                                                }
                                                            />
                                                            <span class="value-display">
                                                                {move || {
                                                                    match selected_c_plane.get() {
                                                                        Some(cp) => format!("C {cp:.0}°"),
                                                                        None => locale.get().diagram.cone.all_planes.clone(),
                                                                    }
                                                                }}
                                                            </span>
                                                        </div>
                                                    }.into_any()
                                                } else {
                                                    view! {
                                                        <div class="c-plane-control symmetric-note">
                                                            <span class="text-muted">{move || locale.get().diagram.cone.symmetric_note.clone()}</span>
                                                        </div>
                                                    }.into_any()
                                                }
                                            }}
                                            <DiagramZoom>
                                                <div class="diagram-fullwidth">
                                                    <ConeDiagramView ldc=ldc mounting_height=mounting_height c_plane=selected_c_plane />
                                                </div>
                                            </DiagramZoom>
                                            <ConeIlluminanceTableView ldc=ldc mounting_height=mounting_height c_plane=selected_c_plane />
                                        </div>
                                    }.into_any()
                                },
                                Tab::Spectral => {
                                    let is_dark = Memo::new(move |_| theme_mode.get() == ThemeMode::Dark);
                                    view! {
                                        <div class="spectral-tab">
                                            <div class="diagram-header">
                                                <span class="diagram-title">{move || locale.get().ui.diagram.title_spectral.clone()}</span>
                                                <span class="text-muted">{move || locale.get().ui.spectral.subtitle.clone()}</span>
                                            </div>
                                            <DiagramZoom>
                                                <div class="diagram-fullwidth">
                                                    <SpectralDiagramView atla_doc=atla_doc dark=is_dark />
                                                </div>
                                            </DiagramZoom>
                                        </div>
                                    }.into_any()
                                },
                                Tab::Greenhouse => {
                                    let is_dark = Memo::new(move |_| theme_mode.get() == ThemeMode::Dark);
                                    view! {
                                        <div class="greenhouse-tab">
                                            <div class="diagram-header">
                                                <span class="diagram-title">{move || locale.get().ui.diagram.title_greenhouse.clone()}</span>
                                                <div class="mounting-height-control">
                                                    <label>{move || locale.get().diagram.greenhouse.max_height.clone()}</label>
                                                    <input
                                                        type="range"
                                                        prop:min=move || match unit_system.get() { UnitSystem::Metric => "0.5", _ => "2" }
                                                        prop:max=move || match unit_system.get() { UnitSystem::Metric => "6", _ => "20" }
                                                        prop:step=move || match unit_system.get() { UnitSystem::Metric => "0.5", _ => "1" }
                                                        prop:value=move || unit_system.get().convert_meters(greenhouse_height.get()).to_string()
                                                        on:input=move |ev| {
                                                            if let Ok(value) = event_target_value(&ev).parse::<f64>() {
                                                                set_greenhouse_height.set(unit_system.get().to_meters(value));
                                                            }
                                                        }
                                                    />
                                                    <span class="value-display">
                                                        {move || unit_system.get().format_distance(greenhouse_height.get())}
                                                    </span>
                                                </div>
                                            </div>
                                            <DiagramZoom>
                                                <div class="diagram-fullwidth">
                                                    <GreenhouseDiagramView atla_doc=atla_doc dark=is_dark max_height=greenhouse_height />
                                                </div>
                                            </DiagramZoom>
                                        </div>
                                    }.into_any()
                                },
                                Tab::BugRating => view! {
                                    <div class="bug-rating-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_bug.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.spectral.bug_subtitle.clone()}</span>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                <BugRating ldc=ldc />
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::Lcs => view! {
                                    <div class="lcs-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_lcs.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.spectral.lcs_subtitle.clone()}</span>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                <LcsClassification ldc=ldc />
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::FloodlightVH => view! {
                                    <div class="floodlight-vh-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_floodlight_vh.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.floodlight.vh_subtitle.clone()}</span>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                <FloodlightCartesian ldc=ldc />
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::FloodlightIsolux => view! {
                                    <div class="isolux-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_isolux.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.floodlight.isolux_subtitle.clone()}</span>
                                        </div>
                                        <IsoluxFootprint ldc=ldc />
                                    </div>
                                }.into_any(),
                                Tab::FloodlightIsoView => view! {
                                    <div class="iso-view-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_iso_view.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.diagram.desc_iso_view.clone()}</span>
                                        </div>
                                        <IsoluxIsometric ldc=ldc />
                                    </div>
                                }.into_any(),
                                Tab::FloodlightIsocandela => view! {
                                    <div class="isocandela-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_isocandela.clone()}</span>
                                            <span class="text-muted">{move || locale.get().ui.floodlight.isocandela_subtitle.clone()}</span>
                                        </div>
                                        <DiagramZoom>
                                            <div class="diagram-fullwidth">
                                                <IsocandelaDiagramView ldc=ldc />
                                            </div>
                                        </DiagramZoom>
                                    </div>
                                }.into_any(),
                                Tab::ValidationTab => view! {
                                    <div class="validation-tab">
                                        <h3>{move || locale.get().ui.validation.title.clone()}</h3>
                                        <ValidationPanel ldc=ldc />
                                    </div>
                                }.into_any(),
                                Tab::CompareTab => view! {
                                    <div class="compare-tab">
                                        <ComparePanel
                                            ldc=ldc
                                            current_file=current_file
                                            ldc_b=compare_ldc_b
                                            set_ldc_b=set_compare_ldc_b
                                            label_b=compare_label_b
                                            set_label_b=set_compare_label_b
                                        />
                                    </div>
                                }.into_any(),
                                Tab::BimTab => view! {
                                    <div class="bim-tab">
                                        {move || {
                                            if has_bim_data(&atla_doc.get()) {
                                                view! { <BimPanel atla_doc=atla_doc /> }.into_any()
                                            } else {
                                                view! { <BimPanelEmpty /> }.into_any()
                                            }
                                        }}
                                    </div>
                                }.into_any(),
                                Tab::Scene3DTab => view! {
                                    <div class="scene-3d-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.diagram.title_scene.clone()}</span>
                                        </div>
                                        <div class="scene-container" style="height: 600px; position: relative;">
                                            <BevySceneViewer />
                                        </div>
                                        <div class="scene-controls">
                                            <p class="text-muted">
                                                {move || locale.get().ui.diagram.scene_controls.clone()}
                                            </p>
                                        </div>
                                    </div>
                                }.into_any(),
                                Tab::AreaDesignerTab => view! {
                                    <div class="area-designer-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.tabs.area_designer.clone()}</span>
                                        </div>
                                        <AreaDesigner ldc=ldc />
                                    </div>
                                }.into_any(),
                                Tab::ZonalDesignerTab => view! {
                                    <div class="zonal-designer-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.tabs.zonal_designer.clone()}</span>
                                        </div>
                                        <ZonalDesigner ldc=ldc />
                                    </div>
                                }.into_any(),
                                Tab::MapsDesignerTab => view! {
                                    <div class="maps-designer-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">{move || locale.get().ui.tabs.maps_designer.clone()}</span>
                                        </div>
                                        <div class="maps-container" style="height: 600px; position: relative;">
                                            <MapsDesigner />
                                        </div>
                                    </div>
                                }.into_any(),
                                Tab::GonioSimTab => view! {
                                    <GonioSimDemo ldc=ldc />
                                }.into_any(),
                                Tab::StreetDesignTab => view! {
                                    <div class="street-design-tab">
                                        <div class="diagram-header">
                                            <span class="diagram-title">
                                                {move || locale.get().ui.tabs.street_design.clone()}
                                            </span>
                                            <span class="text-muted">
                                                "Multi-luminaire RP-8 / EN 13201 / CJJ 45 / MLO compliance"
                                            </span>
                                        </div>
                                        <StreetLauncher />
                                    </div>
                                }.into_any(),
                            }}
                        </div>
                    </div>
                        </main>
                    }.into_any()
                }}
            </div>

            // About Modal
            {move || {
                if show_about.get() {
                    view! {
                        <div class="modal-overlay" on:click=move |_| set_show_about.set(false)>
                            <div class="modal-content about-modal" on:click=|e| e.stop_propagation()>
                                <h2>{move || locale.get().ui.about.title.clone()}</h2>
                                <p class="about-subtitle">{move || locale.get().ui.about.subtitle.clone()}</p>
                                <div class="about-description">
                                    <p>{move || locale.get().ui.about.desc_parsing.clone()}</p>
                                    <p>{move || locale.get().ui.about.desc_diagrams.clone()}</p>
                                    <p class="about-highlight">{move || locale.get().ui.about.desc_platforms.clone()}</p>
                                </div>
                                <div class="about-links">
                                    <a href="https://github.com/holg/eulumdat-rs" target="_blank" rel="noopener noreferrer">
                                        {move || locale.get().ui.header.github.clone()}
                                    </a>
                                    <span class="about-version">"v0.4.0"</span>
                                </div>
                                <button class="btn btn-primary" on:click=move |_| set_show_about.set(false)>
                                    {move || locale.get().ui.about.close.clone()}
                                </button>
                            </div>
                        </div>
                    }.into_any()
                } else {
                    // Empty fragment when modal is hidden
                    view! { <div style="display:none"></div> }.into_any()
                }
            }}
        </ThemeProvider>
    }.into_any()
}
