Сканер структуры файлов

Корневая папка: /home/d/docdelux/tech-stc.ru/public_html/engineering-calculators/engineering-systems/lighting-calculator

Всего папок: 4

Всего файлов: 11

Общий размер: 101,477 байт

Создано: 2026-06-07 23:28:21

=== СТРУКТУРА ПАПОК И ФАЙЛОВ ===

content.php (12,958 байт)
[DIR] data/
[DIR] data/config/
data/config/content.json (7,351 байт)
data/config/modules.json (3,109 байт)
data/config/technical.json (7,925 байт)
data/config/validation.json (7,557 байт)
[DIR] data/js/
data/js/core.js (11,728 байт)
data/js/ui.js (15,488 байт)
[DIR] data/templates/
data/templates/form.php (16,509 байт)
data/templates/related-modules.php (2,121 байт)
index.php (4,679 байт)
test-runner.html (12,052 байт)

=== СОДЕРЖИМОЕ ФАЙЛОВ ===

========================================
ФАЙЛ #1: content.php
Расширение: php
Размер: 12,958 байт
========================================
<?php
/**
 * РАЗРАБОТЧИК: tech-stc.ru
 * ПРОЕКТ: Калькулятор расчёта освещения
 * ВЕРСИЯ: 1.0.0
 * ДАТА: 2026-06-05
 * @author tech-stc.ru
 * 
 * НОРМАТИВЫ РФ: СП 52.13330.2016 (с изм. №5 от 01.01.2026), СанПиН 1.2.3685-21
 */

ob_start();

$moduleBasePath = dirname($_SERVER['SCRIPT_NAME']);
$rootPath = '/';
?>

<div id="lighting-calculator" 
     data-module-base="<?= htmlspecialchars($moduleBasePath) ?>"
     data-root-base="<?= htmlspecialchars($rootPath) ?>">
    <?php include __DIR__ . '/data/templates/form.php'; ?>
</div>

<?php include __DIR__ . '/data/templates/related-modules.php'; ?>

<section id="technical-passport" class="technical-passport mt-6">
    <h2>Технический паспорт калькулятора расчёта освещения</h2>
    
    <details class="accordion__item">
        <summary class="accordion__summary">Нормативная база</summary>
        <div class="accordion__content">
            <p>Расчёт выполняется в соответствии со следующими нормативными документами Российской Федерации, актуальными на 2026 год:</p>
            <ul>
                <li><strong>СП 52.13330.2016 (с изм. №5 от 01.01.2026)</strong> — «Естественное и искусственное освещение». Основной документ по проектированию освещения.</li>
                <li><strong>СанПиН 1.2.3685-21</strong> — «Гигиенические требования к освещению помещений».</li>
                <li><strong>ГОСТ Р 55710-2013</strong> — «Освещение рабочих мест внутри зданий. Нормы и методы измерений».</li>
            </ul>
        </div>
    </details>
    
    <details class="accordion__item">
        <summary class="accordion__summary">Методика расчёта (СП 52.13330.2016)</summary>
        <div class="accordion__content">
            <p>Расчёт выполняется методом коэффициента использования светового потока:</p>
            <ol>
                <li><strong>Индекс помещения:</strong><br>
                i = (A × B) / (H_р × (A + B))<br>
                где A — длина помещения, B — ширина, H_р — расчётная высота (от светильника до рабочей поверхности).</li>
                
                <li><strong>Коэффициент использования светового потока:</strong><br>
                U = f(i, ρ_стен, ρ_потолка, ρ_пола, тип светильника)<br>
                Определяется по таблицам в зависимости от индекса помещения и коэффициентов отражения.</li>
                
                <li><strong>Требуемый световой поток:</strong><br>
                Ф_треб = (E_н × S × К_з) / (U × К_и)<br>
                где E_н — нормируемая освещённость, S — площадь помещения, К_з — коэффициент запаса (загрязнение), К_и — коэффициент неравномерности (1.1-1.2).</li>
                
                <li><strong>Количество светильников:</strong><br>
                N = Ф_треб / Ф_св<br>
                где Ф_св — световой поток одного светильника.</li>
                
                <li><strong>Удельная мощность:</strong><br>
                P_уд = P_сум / S (Вт/м²)<br>
                Нормативное значение для офисов: 8-12 Вт/м² (LED).</li>
            </ol>
            <p><strong>Важно:</strong> Расчёт выполняется для средней освещённости на рабочей поверхности.</p>
        </div>
    </details>
    
    <div id="tables-container">
        <details class="accordion__item">
            <summary class="accordion__summary">Таблицы нормативных данных (СП 52.13330.2016)</summary>
            <div class="accordion__content" id="tables-content">
                <p><strong>Таблица 1. Нормируемая освещённость для различных помещений:</strong></p>
                <table class="table table--striped">
                    <thead>
                        <tr><th>Тип помещения</th><th>Освещённость, лк</th><th>Примечание</th></tr>
                    </thead>
                    <tbody>
                        <tr><td>Офис, работа с ПК</th><td>500</td><td>на рабочей поверхности</td></tr>
                        <tr><td>Жилая комната</th><td>150</td><td>общее освещение</td></tr>
                        <tr><td>Кухня</th><td>300</td><td>на рабочей поверхности</td></tr>
                        <tr><td>Коридор</th><td>75</td><td>на полу</td></tr>
                        <tr><td>Склад</th><td>150</td><td>на полу</td></tr>
                        <tr><td>Производство</th><td>300</td><td>средняя точность</td></tr>
                        <tr><td>Школьный класс</th><td>400</td><td>на партах</td></tr>
                        <tr><td>Магазин</th><td>400</td><td>в торговом зале</td></tr>
                    </tbody>
                </table>
                
                <p><strong>Таблица 2. Коэффициенты отражения поверхностей:</strong></p>
                <table class="table table--striped">
                    <thead>
                        <tr><th>Поверхность</th><th>Светлая</th><th>Средняя</th><th>Тёмная</th></tr>
                    </thead>
                    <tbody>
                        <tr><td>Потолок</th><td>0.7</td><td>0.5</td><td>0.3</td></tr>
                        <tr><td>Стены</th><td>0.5</td><td>0.3</td><td>0.2</td></tr>
                        <tr><td>Пол</th><td>0.3</td><td>0.2</td><td>0.1</td></tr>
                    </tbody>
                </table>
                
                <p><strong>Таблица 3. Коэффициент запаса (загрязнение светильников):</strong></p>
                <table class="table table--striped">
                    <thead>
                        <tr><th>Тип помещения</th><th>Коэффициент запаса</th></tr>
                    </thead>
                    <tbody>
                        <tr><td>Чистые (офисы, жилые)</th><td>0.8</td></tr>
                        <tr><td>Средние (школы, магазины)</th><td>0.7</td></tr>
                        <tr><td>Пыльные (склады, цеха)</th><td>0.7</td></tr>
                    </tbody>
                </table>
            </div>
        </details>
    </div>
    
    <details class="accordion__item">
        <summary class="accordion__summary">Пример расчёта освещения для офиса</summary>
        <div class="accordion__content">
            <p><strong>Исходные данные:</strong></p>
            <ul>
                <li>Офис: длина 10 м, ширина 8 м, высота 2.7 м</li>
                <li>Норма освещённости: 500 лк</li>
                <li>Светильник LED: 3000 лм, 25 Вт</li>
                <li>Светильники накладные (U = 0.5)</li>
            </ul>
            <p><strong>Расчёт:</strong></p>
            <ul>
                <li>Площадь: 10 × 8 = 80 м²</li>
                <li>Индекс помещения: (10 × 8) / (2.7 × (10 + 8)) = 80 / 48.6 = 1.65</li>
                <li>Требуемый световой поток: (500 × 80 × 1.1) / (0.5 × 0.8) = 44000 / 0.4 = 110000 лм</li>
                <li>Количество светильников: 110000 / 3000 = 37 шт</li>
                <li>Суммарная мощность: 37 × 25 = 925 Вт</li>
                <li>Удельная мощность: 925 / 80 = 11.6 Вт/м² (норма для LED)</li>
            </ul>
            <p><strong>Результат:</strong> 37 светодиодных светильников по 25 Вт, общая мощность 925 Вт.</p>
        </div>
    </details>
    
    <details class="accordion__item">
        <summary class="accordion__summary">Ограничения применения</summary>
        <div class="accordion__content">
            <p>Калькулятор предназначен для <strong>предварительного расчёта</strong> освещения. Он не заменяет полноценный светотехнический расчёт.</p>
            <p><strong>Калькулятор не учитывает:</strong></p>
            <ul>
                <li>Затенение от мебели и оборудования</li>
                <li>Неравномерность освещения (пульсации, пятна)</li>
                <li>Цветовую температуру и индекс цветопередачи</li>
                <li>Специфические требования к освещению рабочих зон</li>
                <li>Наличие естественного освещения</li>
            </ul>
            <p><strong>Для ответственных объектов обязателен светотехнический расчёт в специализированном ПО (DIALux, Relux).</strong></p>
        </div>
    </details>
    
    <details class="accordion__item">
        <summary class="accordion__summary">Часто задаваемые вопросы</summary>
        <div class="accordion__content">
            <p><strong>Как выбрать световой поток светильника?</strong></p>
            <p>Для LED-светильников ориентировочный световой поток: 25 Вт — 2500-3000 лм, 40 Вт — 4000-4800 лм, 50 Вт — 5000-6000 лм. Точные значения смотрите в характеристиках производителя.</p>
            
            <p><strong>Какой коэффициент запаса выбрать?</strong></p>
            <p>Для чистых помещений (офисы, жилые) — 0.8. Для помещений с запылением (склады, цеха) — 0.7.</p>
            
            <p><strong>Какая удельная мощность считается нормальной?</strong></p>
            <p>Для LED-освещения офисов: 8-12 Вт/м². Если значение выше — светильники неэффективные.</p>
            
            <p><strong>Нужен ли запас по освещённости?</strong></p>
            <p>Рекомендуется запас 10-20% на старение ламп и загрязнение светильников.</p>
        </div>
    </details>
</section>

<script src="<?= $moduleBasePath ?>/data/js/core.js" defer></script>
<script src="<?= $moduleBasePath ?>/data/js/ui.js" defer></script>

<script>
document.addEventListener('DOMContentLoaded', async function() {
    const container = document.getElementById('lighting-calculator');
    if (!container) return;
    
    const moduleBase = container.dataset.moduleBase;
    if (!moduleBase) {
        console.error('moduleBase не указан');
        return;
    }
    
    try {
        const core = new LightingCalculatorCore();
        await core.load(moduleBase);
        
        const ui = new LightingCalculatorUI(core);
        await ui.init();
        
        console.log('Калькулятор расчёта освещения запущен');
    } catch (error) {
        console.error('Ошибка инициализации:', error);
        container.innerHTML = '<div style="color: #d32f2f; padding: 20px;">Ошибка загрузки калькулятора</div>';
    }
});
</script>

<?php
return ob_get_clean();
?>
========================================
ФАЙЛ #2: data/config/content.json
Расширение: json
Размер: 7,351 байт
========================================
{
    "meta": {
        "module_id": "lighting-calculator",
        "version": "1.0.0",
        "last_updated": "2026-06-05",
        "applicable_country": "RU",
        "normative_compliance": [
            "СП 52.13330.2016 (с изм. №5 от 01.01.2026) — Естественное и искусственное освещение",
            "СанПиН 1.2.3685-21 — Гигиенические требования к освещению",
            "ГОСТ Р 55710-2013 — Освещение рабочих мест внутри зданий"
        ],
        "authors": [
            {
                "name": "ТЕХСТАНДАРТ",
                "url": "https://tech-stc.ru"
            }
        ]
    },
    "seo": {
        "title": "Калькулятор расчёта освещения | Расчет освещённости помещения | ТЕХСТАНДАРТ",
        "description": "Расчёт освещённости помещения: количество светильников, мощность, световой поток. Соответствие СП 52.13330.2016, СанПиН 1.2.3685-21.",
        "keywords": "расчет освещения, калькулятор освещенности, количество светильников, расчет светильников, освещение помещения, сп 52.13330.2016",
        "h1": "Калькулятор расчёта освещения",
        "robots": "index, follow"
    },
    "open_graph": {
        "title": "Калькулятор расчёта освещения — онлайн расчет освещённости помещения",
        "description": "Рассчитайте освещение помещения: количество светильников, мощность, световой поток, годовые затраты на электроэнергию. По СП 52.13330.2016.",
        "image": "https://tech-stc.ru/assets/images/og/lighting-calculator-og.jpg",
        "type": "website",
        "locale": "ru_RU"
    },
    "json_ld": {
        "@context": "https://schema.org",
        "@type": "WebApplication",
        "name": "Калькулятор расчёта освещения",
        "applicationCategory": "EngineeringApplication",
        "applicationSubCategory": "LightingEngineering",
        "operatingSystem": "Web",
        "featureList": "Расчёт освещённости, подбор светильников, учёт коэффициентов отражения, расчёт энергопотребления",
        "audience": {
            "@type": "PeopleAudience",
            "audienceType": "Проектировщики, электрики, владельцы помещений"
        },
        "applicableCountry": "RU",
        "inLanguage": "ru"
    },
    "ui_texts": {
        "form_sections": {
            "room": "Параметры помещения",
            "lighting": "Требования к освещению",
            "fixture": "Параметры светильника",
            "cost": "Эксплуатационные расходы"
        },
        "buttons": {
            "calculate": "Рассчитать освещение",
            "reset": "Сбросить"
        },
        "results": {
            "requirements": "Нормативная освещённость",
            "fixtures": "Количество светильников",
            "power": "Энергопотребление",
            "cost": "Годовые затраты"
        },
        "hints": {
            "room_type": "Тип помещения определяет нормативную освещённость",
            "luminaire_type": "Тип светильника влияет на коэффициент использования",
            "light_source": "Светодиодные лампы самые экономичные"
        }
    },
    "technical_passport": {
        "normative_description": "Расчёт выполняется по СП 52.13330.2016 «Естественное и искусственное освещение» и СанПиН 1.2.3685-21.",
        "methodology_description": "Расчёт освещения выполняется методом коэффициента использования светового потока: N = (E × S × Kз) / (Ф × U × Kи), где E — нормируемая освещённость, S — площадь помещения, Kз — коэффициент запаса, Ф — световой поток светильника, U — коэффициент использования, Kи — коэффициент неравномерности.",
        "limitations": [
            "Расчёт приблизительный, точный расчёт требует учёта геометрии помещения",
            "Не учтены затенения от мебели и оборудования",
            "Для точного проектирования требуется программа DIALux или аналоги",
            "Не учтены требования к пульсации освещённости"
        ]
    },
    "unified": {
        "breadcrumb": [
            {
                "name": "Главная",
                "url": "/"
            },
            {
                "name": "Калькуляторы",
                "url": "/engineering-calculators/"
            },
            {
                "name": "Инженерные системы",
                "url": "/engineering-calculators/engineering-systems/"
            },
            {
                "name": "Расчёт освещения",
                "url": ""
            }
        ],
        "howto_steps": [
            {
                "name": "Выберите тип помещения",
                "text": "От типа зависит нормативная освещённость"
            },
            {
                "name": "Введите размеры помещения",
                "text": "Длина, ширина, высота"
            },
            {
                "name": "Выберите тип светильника и лампы",
                "text": "Влияет на эффективность и стоимость"
            },
            {
                "name": "Нажмите 'Рассчитать'",
                "text": "Калькулятор выполнит расчёт"
            },
            {
                "name": "Получите результат",
                "text": "Количество светильников, мощность, годовые затраты"
            }
        ],
        "product": {
            "name": "Светодиодные светильники",
            "low_price": 500,
            "high_price": 5000,
            "offer_count": 100,
            "brand": "Philips, Osram, Gauss, IEK"
        },
        "subcategory": "LightingEngineering"
    }
}
========================================
ФАЙЛ #3: data/config/modules.json
Расширение: json
Размер: 3,109 байт
========================================
{
    "section_title": "Связанные расчёты и материалы",
    "items": [
        {
            "id": "emergency-lighting-calculator",
            "type": "calculator",
            "title": "Расчёт аварийного освещения",
            "description": "Эвакуационное и резервное освещение по СП 52.13330",
            "url": "/engineering-calculators/engineering-systems/emergency-lighting-calculator/",
            "icon": "bi bi-1-circle",
            "position": "after_results",
            "priority": 1
        },
        {
            "id": "lighting-control-system",
            "type": "calculator",
            "title": "Автоматизация освещения",
            "description": "Расчёт экономии при установке датчиков движения",
            "url": "/engineering-calculators/engineering-systems/lighting-control-system/",
            "icon": "bi bi-2-circle",
            "position": "after_results",
            "priority": 2
        },
        {
            "id": "solar-panel-power-calculator",
            "type": "calculator",
            "title": "Расчёт солнечных панелей",
            "description": "Подбор солнечных батарей для автономного освещения",
            "url": "/engineering-calculators/smart-home-green/solar-panel-power-and-quantity-calculator/",
            "icon": "bi bi-3-circle",
            "position": "after_results",
            "priority": 3
        },
        {
            "id": "heating-calculator-heat-loss-home",
            "type": "calculator",
            "title": "Расчёт теплопотерь",
            "description": "Теплопотери через стены, окна, крышу",
            "url": "/engineering-calculators/engineering-systems/heating-calculator-heat-loss-home/",
            "icon": "bi bi-4-circle",
            "position": "after_results",
            "priority": 4
        },
        {
            "id": "acoustic-calculation-soue",
            "type": "calculator",
            "title": "Акустический расчёт СОУЭ",
            "description": "Расчёт оповещателей по СП 3.13130.2026",
            "url": "/engineering-calculators/engineering-systems/acoustic-calculation-soue/",
            "icon": "bi bi-5-circle",
            "position": "after_results",
            "priority": 5
        },
        {
            "id": "power-consumption-calculator",
            "type": "calculator",
            "title": "Расчёт энергопотребления",
            "description": "Расчёт потребления электроэнергии оборудованием",
            "url": "/engineering-calculators/engineering-systems/power-consumption-calculator/",
            "icon": "bi bi-6-circle",
            "position": "after_results",
            "priority": 6
        }
    ]
}
========================================
ФАЙЛ #4: data/config/technical.json
Расширение: json
Размер: 7,925 байт
========================================
{
    "meta": {
        "module_id": "lighting-calculator",
        "version": "1.0.0",
        "description": "Расчёт освещённости помещения",
        "normative_documents": [
            "СП 52.13330.2016 (с изм. №5 от 01.01.2026) — Естественное и искусственное освещение",
            "СанПиН 1.2.3685-21 — Гигиенические требования к освещению",
            "ГОСТ Р 55710-2013 — Освещение рабочих мест внутри зданий"
        ],
        "last_updated": "2026-06-05",
        "applicable_country": "RU"
    },
    "room_types": {
        "office": {
            "name": "Офис",
            "standard_illuminance_lux": 500,
            "height_default_m": 2.7,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        },
        "living": {
            "name": "Жилая комната",
            "standard_illuminance_lux": 150,
            "height_default_m": 2.5,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        },
        "kitchen": {
            "name": "Кухня",
            "standard_illuminance_lux": 300,
            "height_default_m": 2.5,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        },
        "bathroom": {
            "name": "Ванная",
            "standard_illuminance_lux": 150,
            "height_default_m": 2.5,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        },
        "corridor": {
            "name": "Коридор",
            "standard_illuminance_lux": 75,
            "height_default_m": 2.5,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        },
        "warehouse": {
            "name": "Склад",
            "standard_illuminance_lux": 150,
            "height_default_m": 4.0,
            "wall_reflectance": 0.4,
            "ceiling_reflectance": 0.5,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.7
        },
        "production": {
            "name": "Производственное помещение",
            "standard_illuminance_lux": 300,
            "height_default_m": 5.0,
            "wall_reflectance": 0.4,
            "ceiling_reflectance": 0.5,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.7
        },
        "school": {
            "name": "Школа/класс",
            "standard_illuminance_lux": 400,
            "height_default_m": 3.0,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.3,
            "maintenance_factor": 0.8
        },
        "hospital": {
            "name": "Больница/палата",
            "standard_illuminance_lux": 200,
            "height_default_m": 2.7,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        },
        "retail": {
            "name": "Магазин/торговый зал",
            "standard_illuminance_lux": 400,
            "height_default_m": 3.5,
            "wall_reflectance": 0.5,
            "ceiling_reflectance": 0.7,
            "floor_reflectance": 0.2,
            "maintenance_factor": 0.8
        }
    },
    "light_sources": {
        "led": {
            "name": "LED (светодиодный)",
            "luminous_efficacy_lm_per_w": 120,
            "service_life_hours": 50000,
            "color_temp_options": [
                2700,
                3000,
                4000,
                5000,
                6500
            ],
            "price_per_watt_rub": 10,
            "description": "Современные, экономичные, долговечные"
        },
        "fluorescent": {
            "name": "Люминесцентный",
            "luminous_efficacy_lm_per_w": 80,
            "service_life_hours": 15000,
            "color_temp_options": [
                3000,
                4000,
                6500
            ],
            "price_per_watt_rub": 5,
            "description": "Устаревают, требуют утилизации"
        },
        "halogen": {
            "name": "Галогенный",
            "luminous_efficacy_lm_per_w": 25,
            "service_life_hours": 2000,
            "color_temp_options": [
                3000,
                4000
            ],
            "price_per_watt_rub": 3,
            "description": "Высокий нагрев, низкая эффективность"
        },
        "incandescent": {
            "name": "Лампа накаливания",
            "luminous_efficacy_lm_per_w": 12,
            "service_life_hours": 1000,
            "color_temp_options": [
                2700
            ],
            "price_per_watt_rub": 2,
            "description": "Запрещены к продаже в РФ"
        }
    },
    "luminaire_types": {
        "downlight": {
            "name": "Встраиваемый светильник",
            "light_loss_factor": 0.9,
            "installation_height_min_m": 2.2,
            "description": "Для подвесных потолков"
        },
        "surface": {
            "name": "Накладной светильник",
            "light_loss_factor": 0.85,
            "installation_height_min_m": 2.2,
            "description": "Крепится на поверхность"
        },
        "suspended": {
            "name": "Подвесной светильник",
            "light_loss_factor": 0.95,
            "installation_height_min_m": 2.5,
            "description": "Для высоких потолков"
        },
        "highbay": {
            "name": "Высокопролётный светильник",
            "light_loss_factor": 0.85,
            "installation_height_min_m": 4.0,
            "description": "Для складов и цехов"
        }
    },
    "formulas": {
        "room_index": "length * width / (height * (length + width))",
        "utilization_factor": "по таблице от индекса помещения и коэффициентов отражения",
        "total_luminous_flux_lm": "illuminance * area / (utilization_factor * maintenance_factor)",
        "fixtures_count": "total_luminous_flux / (lumens_per_fixture)",
        "total_power_w": "fixtures_count * power_per_fixture",
        "annual_energy_kwh": "total_power_w * operating_hours_per_day * 365 / 1000",
        "annual_cost_rub": "annual_energy_kwh * electricity_tariff"
    },
    "default_params": {
        "room_type": "office",
        "room_length_m": 10,
        "room_width_m": 8,
        "room_height_m": 2.7,
        "working_plane_height_m": 0.8,
        "luminaire_type": "surface",
        "light_source": "led",
        "lumens_per_fixture_lm": 3000,
        "power_per_fixture_w": 25,
        "operating_hours_per_day": 10,
        "electricity_tariff_rub_per_kwh": 6.0,
        "wall_reflectance": null,
        "ceiling_reflectance": null,
        "floor_reflectance": null,
        "maintenance_factor": null
    }
}
========================================
ФАЙЛ #5: data/config/validation.json
Расширение: json
Размер: 7,557 байт
========================================
{
    "validation_rules": {
        "room_type": {
            "type": "select",
            "options": [
                "office",
                "living",
                "kitchen",
                "bathroom",
                "corridor",
                "warehouse",
                "production",
                "school",
                "hospital",
                "retail"
            ],
            "description": "Тип помещения"
        },
        "room_length_m": {
            "min": 1,
            "max": 100,
            "step": 0.5,
            "default": 10,
            "description": "Длина помещения (м)"
        },
        "room_width_m": {
            "min": 1,
            "max": 100,
            "step": 0.5,
            "default": 8,
            "description": "Ширина помещения (м)"
        },
        "room_height_m": {
            "min": 2.2,
            "max": 15,
            "step": 0.1,
            "default": 2.7,
            "description": "Высота помещения (м)"
        },
        "working_plane_height_m": {
            "min": 0,
            "max": 2,
            "step": 0.1,
            "default": 0.8,
            "description": "Высота рабочей поверхности (м)"
        },
        "luminaire_type": {
            "type": "select",
            "options": [
                "downlight",
                "surface",
                "suspended",
                "highbay"
            ],
            "description": "Тип светильника"
        },
        "light_source": {
            "type": "select",
            "options": [
                "led",
                "fluorescent",
                "halogen",
                "incandescent"
            ],
            "description": "Тип источника света"
        },
        "lumens_per_fixture_lm": {
            "min": 100,
            "max": 50000,
            "step": 100,
            "default": 3000,
            "description": "Световой поток одного светильника (лм)"
        },
        "power_per_fixture_w": {
            "min": 1,
            "max": 500,
            "step": 1,
            "default": 25,
            "description": "Мощность одного светильника (Вт)"
        },
        "operating_hours_per_day": {
            "min": 0,
            "max": 24,
            "step": 1,
            "default": 10,
            "description": "Время работы освещения в сутки (ч)"
        },
        "electricity_tariff_rub_per_kwh": {
            "min": 1,
            "max": 15,
            "step": 0.5,
            "default": 6,
            "description": "Тариф на электроэнергию (руб/кВт·ч)"
        },
        "maintenance_factor": {
            "min": 0.5,
            "max": 0.95,
            "step": 0.05,
            "description": "Коэффициент запаса (загрязнение светильников)",
            "reusable": true
        },
        "utilization_factor": {
            "min": 0.2,
            "max": 0.9,
            "step": 0.05,
            "description": "Коэффициент использования светового потока",
            "reusable": true
        }
    },
    "recommendations": {
        "low_illuminance": {
            "condition": "required_fixtures_count < 1 AND area > 20",
            "message": "Расчётное количество светильников менее 1. Возможно, световой поток одного светильника слишком высок для равномерного освещения. Рекомендуется использовать больше светильников меньшей мощности.",
            "action": "use_more_fixtures"
        },
        "high_power_density": {
            "condition": "total_power_w / area > 15",
            "message": "Высокая удельная мощность освещения (более 15 Вт/м²). Рекомендуется использовать более эффективные светодиодные светильники.",
            "action": "use_led"
        },
        "low_ceiling_for_highbay": {
            "condition": "room_height_m < 4 AND luminaire_type = 'highbay'",
            "message": "Высокопролётные светильники неэффективны при высоте потолка менее 4 м. Рекомендуются накладные или встраиваемые светильники.",
            "action": "change_luminaire_type"
        },
        "incandescent_warning": {
            "condition": "light_source = 'incandescent'",
            "message": "Лампы накаливания запрещены к обороту в РФ с 2026 года. Рекомендуются светодиодные светильники.",
            "action": "use_led_instead"
        },
        "high_energy_cost": {
            "condition": "annual_cost_rub > 100000",
            "message": "Годовые затраты на электроэнергию превышают 100 000 руб. Рекомендуется рассмотреть автоматизацию управления освещением.",
            "action": "add_lighting_control"
        },
        "led_recommended": {
            "condition": "light_source != 'led'",
            "message": "Светодиодные светильники имеют в 5-10 раз больший срок службы и меньшие эксплуатационные расходы.",
            "action": "consider_led"
        }
    },
    "output_format": {
        "required_illuminance_lux": {
            "unit": "лк",
            "decimals": 0
        },
        "total_luminous_flux_lm": {
            "unit": "лм",
            "decimals": 0
        },
        "fixtures_count": {
            "unit": "шт",
            "decimals": 0
        },
        "total_power_w": {
            "unit": "Вт",
            "decimals": 0
        },
        "total_power_kw": {
            "unit": "кВт",
            "decimals": 2
        },
        "annual_energy_kwh": {
            "unit": "кВт·ч",
            "decimals": 0
        },
        "annual_cost_rub": {
            "unit": "руб",
            "decimals": 0
        },
        "power_density_w_per_m2": {
            "unit": "Вт/м²",
            "decimals": 1
        },
        "room_index": {
            "unit": "",
            "decimals": 2
        }
    },
    "status_colors": {
        "ok": "#2e7d32",
        "warning": "#ed6c02",
        "error": "#d32f2f"
    },
    "status_messages": {
        "illuminance": {
            "ok": "Освещённость соответствует норме",
            "warning": "Освещённость близка к норме",
            "error": "Освещённость ниже нормы"
        },
        "efficiency": {
            "ok": "Энергоэффективность высокая",
            "warning": "Энергоэффективность средняя",
            "error": "Энергоэффективность низкая"
        }
    }
}
========================================
ФАЙЛ #6: data/js/core.js
Расширение: js
Размер: 11,728 байт
========================================
/**
 * LIGHTING CALCULATOR CORE
 * Версия: 1.0.0
 * Дата: 2026-06-05
 * Расчёт: освещение помещений
 * Нормативы: СП 52.13330.2016 (с изм. №5 от 01.01.2026), СанПиН 1.2.3685-21
 */
class LightingCalculatorCore {
    constructor() {
        this.config = null;
        this.isReady = false;
        this.basePath = null;
    }

    async load(basePath) {
        if (this.isReady && this.basePath === basePath) return this;
        this.basePath = basePath;
        try {
            const [technical, validation, content] = await Promise.all([
                fetch(`${basePath}/data/config/technical.json`),
                fetch(`${basePath}/data/config/validation.json`),
                fetch(`${basePath}/data/config/content.json`)
            ]);
            this.config = { ...await technical.json(), ...await validation.json(), ...await content.json() };
            this.isReady = true;
            return this;
        } catch (error) {
            throw new Error('Не удалось загрузить конфигурацию LightingCalculatorCore');
        }
    }

    get(path, defaultValue = null) {
        if (!this.isReady) throw new Error('LightingCalculatorCore: не инициализирован');
        const parts = path.split('.');
        let current = this.config;
        for (const part of parts) {
            if (current === undefined || current === null) return defaultValue;
            current = current[part];
        }
        return current !== undefined ? current : defaultValue;
    }

    getRoomType(roomTypeKey) {
        return this.get(`room_types.${roomTypeKey}`, null);
    }

    getLightSource(lightSourceKey) {
        return this.get(`light_sources.${lightSourceKey}`, null);
    }

    getLuminaireType(luminaireTypeKey) {
        return this.get(`luminaire_types.${luminaireTypeKey}`, null);
    }

    calculateRoomIndex(lengthM, widthM, heightM, workingPlaneHeightM) {
        const effectiveHeight = heightM - workingPlaneHeightM;
        if (effectiveHeight <= 0) return 1.0;
        const index = (lengthM * widthM) / (effectiveHeight * (lengthM + widthM));
        return parseFloat(index.toFixed(2));
    }

    calculateUtilizationFactor(roomIndex, wallReflectance, ceilingReflectance, floorReflectance, luminaireType) {
        let baseFactor = 0.5;
        
        if (luminaireType === 'downlight') baseFactor = 0.55;
        else if (luminaireType === 'surface') baseFactor = 0.5;
        else if (luminaireType === 'suspended') baseFactor = 0.6;
        else if (luminaireType === 'highbay') baseFactor = 0.7;
        
        let correction = 1.0;
        correction *= (0.8 + ceilingReflectance * 0.4);
        correction *= (0.7 + wallReflectance * 0.6);
        correction *= (0.9 + floorReflectance * 0.3);
        
        let indexFactor = 1.0;
        if (roomIndex < 0.5) indexFactor = 0.6;
        else if (roomIndex < 1) indexFactor = 0.8;
        else if (roomIndex < 2) indexFactor = 0.95;
        else if (roomIndex < 3) indexFactor = 1.0;
        else indexFactor = 1.05;
        
        let factor = baseFactor * correction * indexFactor;
        factor = Math.min(0.85, Math.max(0.3, factor));
        return parseFloat(factor.toFixed(2));
    }

    calculateRequiredLuminousFlux(illuminanceLux, areaM2, utilizationFactor, maintenanceFactor) {
        const safetyMargin = 1.1;
        const flux = (illuminanceLux * areaM2 * safetyMargin) / (utilizationFactor * maintenanceFactor);
        return Math.round(flux);
    }

    calculateFixturesCount(totalFluxLm, lumensPerFixtureLm) {
        let count = Math.ceil(totalFluxLm / lumensPerFixtureLm);
        count = Math.max(1, count);
        return count;
    }

    calculatePowerAndCost(fixturesCount, powerPerFixtureW, operatingHoursPerDay, electricityTariff) {
        const totalPowerW = fixturesCount * powerPerFixtureW;
        const totalPowerKw = parseFloat((totalPowerW / 1000).toFixed(2));
        const annualEnergyKwh = Math.round(totalPowerKw * operatingHoursPerDay * 365);
        const annualCostRub = Math.round(annualEnergyKwh * electricityTariff);
        return { totalPowerW, totalPowerKw, annualEnergyKwh, annualCostRub };
    }

    calculate(params) {
        if (!this.isReady) return { success: false, error: 'Модуль не инициализирован' };
        
        const roomType = this.getRoomType(params.room_type);
        const lightSource = this.getLightSource(params.light_source);
        const luminaireType = this.getLuminaireType(params.luminaire_type);
        
        if (!roomType || !lightSource || !luminaireType) {
            return { success: false, error: 'Ошибка загрузки справочных данных' };
        }
        
        const areaM2 = params.room_length_m * params.room_width_m;
        const illuminanceLux = roomType.standard_illuminance_lux;
        
        const wallReflectance = params.wall_reflectance || roomType.wall_reflectance;
        const ceilingReflectance = params.ceiling_reflectance || roomType.ceiling_reflectance;
        const floorReflectance = params.floor_reflectance || roomType.floor_reflectance;
        const maintenanceFactor = params.maintenance_factor || roomType.maintenance_factor;
        
        const roomIndex = this.calculateRoomIndex(
            params.room_length_m,
            params.room_width_m,
            params.room_height_m,
            params.working_plane_height_m
        );
        
        const utilizationFactor = this.calculateUtilizationFactor(
            roomIndex,
            wallReflectance,
            ceilingReflectance,
            floorReflectance,
            params.luminaire_type
        );
        
        const totalRequiredFluxLm = this.calculateRequiredLuminousFlux(
            illuminanceLux,
            areaM2,
            utilizationFactor,
            maintenanceFactor
        );
        
        const fixturesCount = this.calculateFixturesCount(
            totalRequiredFluxLm,
            params.lumens_per_fixture_lm
        );
        
        const actualTotalFluxLm = fixturesCount * params.lumens_per_fixture_lm;
        const actualIlluminanceLux = Math.round((actualTotalFluxLm * utilizationFactor * maintenanceFactor) / (areaM2 * 1.1));
        
        const powerData = this.calculatePowerAndCost(
            fixturesCount,
            params.power_per_fixture_w,
            params.operating_hours_per_day,
            params.electricity_tariff_rub_per_kwh
        );
        
        const powerDensityWPerM2 = parseFloat((powerData.totalPowerW / areaM2).toFixed(1));
        
        const recommendations = [];
        
        if (actualIlluminanceLux < illuminanceLux * 0.9) {
            recommendations.push({
                type: 'error',
                message: `Расчётная освещённость (${actualIlluminanceLux} лк) ниже нормы (${illuminanceLux} лк). Увеличьте количество светильников или используйте более мощные лампы.`
            });
        } else if (actualIlluminanceLux > illuminanceLux * 1.2) {
            recommendations.push({
                type: 'warning',
                message: `Расчётная освещённость (${actualIlluminanceLux} лк) выше нормы (${illuminanceLux} лк) более чем на 20%. Рекомендуется уменьшить количество светильников или использовать лампы меньшей мощности.`
            });
        } else {
            recommendations.push({
                type: 'success',
                message: `Расчётная освещённость (${actualIlluminanceLux} лк) соответствует норме (${illuminanceLux} лк).`
            });
        }
        
        if (lightSource.luminous_efficacy_lm_per_w < 80) {
            recommendations.push({
                type: 'warning',
                message: `Выбранный источник света имеет низкую светоотдачу (${lightSource.luminous_efficacy_lm_per_w} лм/Вт). Рекомендуется использовать светодиодные лампы (120+ лм/Вт).`
            });
        }
        
        if (params.luminaire_type === 'highbay' && params.room_height_m < 4) {
            recommendations.push({
                type: 'info',
                message: `Высокопролётные светильники неэффективны при высоте потолка менее 4 м. Рекомендуется использовать накладные или встраиваемые светильники.`
            });
        }
        
        if (params.light_source === 'incandescent') {
            recommendations.push({
                type: 'error',
                message: `Лампы накаливания запрещены к обороту в РФ с 2026 года. Используйте светодиодные светильники.`
            });
        }
        
        if (powerDensityWPerM2 > 10) {
            recommendations.push({
                type: 'info',
                message: `Удельная мощность освещения (${powerDensityWPerM2} Вт/м²) высокая. Рекомендуется автоматизация управления освещением.`
            });
        }
        
        if (recommendations.length === 0) {
            recommendations.push({
                type: 'success',
                message: 'Параметры освещения подобраны корректно. Требования СП 52.13330.2016 соблюдены.'
            });
        }
        
        return {
            success: true,
            results: {
                room: {
                    type: roomType.name,
                    area_m2: areaM2,
                    room_index: roomIndex
                },
                standards: {
                    required_illuminance_lux: illuminanceLux,
                    maintenance_factor: maintenanceFactor,
                    utilization_factor: utilizationFactor
                },
                luminous_flux: {
                    required_total_lm: totalRequiredFluxLm,
                    per_fixture_lm: params.lumens_per_fixture_lm,
                    actual_total_lm: actualTotalFluxLm,
                    actual_illuminance_lux: actualIlluminanceLux
                },
                fixtures: {
                    count: fixturesCount,
                    type: luminaireType.name,
                    power_per_fixture_w: params.power_per_fixture_w,
                    total_power_w: powerData.totalPowerW,
                    total_power_kw: powerData.totalPowerKw,
                    power_density_w_per_m2: powerDensityWPerM2
                },
                cost: {
                    operating_hours_per_day: params.operating_hours_per_day,
                    annual_energy_kwh: powerData.annualEnergyKwh,
                    electricity_tariff_rub_per_kwh: params.electricity_tariff_rub_per_kwh,
                    annual_cost_rub: powerData.annualCostRub
                }
            },
            recommendations: recommendations
        };
    }
}

window.LightingCalculatorCore = LightingCalculatorCore;
========================================
ФАЙЛ #7: data/js/ui.js
Расширение: js
Размер: 15,488 байт
========================================
/**
 * LIGHTING CALCULATOR UI
 * Версия: 1.0.0
 * Дата: 2026-06-05
 * Синхронизирован с lighting-calculator-core.js v1.0.0
 */
class LightingCalculatorUI {
    constructor(core) {
        this.core = core;
        this.form = document.getElementById('lighting-form');
    }

    async init() {
        if (!this.form) {
            console.warn('LightingCalculatorUI: форма не найдена в DOM');
            return;
        }
        this.attachFormHandlers();
        this.attachDynamicHandlers();
        this.updateRoomHint();
        this.updateEfficiencyHint();
    }

    attachFormHandlers() {
        const submitBtn = this.form.querySelector('button[type="submit"]');
        const resetBtn = this.form.querySelector('button[type="reset"]');

        if (submitBtn) {
            submitBtn.addEventListener('click', e => {
                e.preventDefault();
                this.calculateAndDisplay();
            });
        }
        if (resetBtn) {
            resetBtn.addEventListener('click', () => setTimeout(() => this.clearResults(), 50));
        }
    }

    attachDynamicHandlers() {
        const roomTypeSelect = document.getElementById('lighting-room-type');
        const lightSourceSelect = document.getElementById('lighting-light-source');
        const luminaireTypeSelect = document.getElementById('lighting-luminaire-type');
        const lumensInput = document.getElementById('lighting-lumens');
        const powerInput = document.getElementById('lighting-power');

        if (roomTypeSelect) {
            roomTypeSelect.addEventListener('change', () => this.updateRoomHint());
        }

        if (lightSourceSelect) {
            lightSourceSelect.addEventListener('change', () => this.updateEfficiencyHint());
        }

        if (luminaireTypeSelect) {
            luminaireTypeSelect.addEventListener('change', () => this.updateLuminaireHint());
        }

        if (lumensInput && powerInput) {
            lumensInput.addEventListener('input', () => this.updateEfficiencyHint());
            powerInput.addEventListener('input', () => this.updateEfficiencyHint());
        }

        this.updateRoomHint();
        this.updateEfficiencyHint();
        this.updateLuminaireHint();
    }

    updateRoomHint() {
        const roomTypeSelect = document.getElementById('lighting-room-type');
        const hintSpan = document.getElementById('room-hint');
        
        if (!roomTypeSelect || !hintSpan) return;
        
        const roomType = roomTypeSelect.value;
        const roomData = this.core.getRoomType(roomType);
        
        if (roomData) {
            hintSpan.innerHTML = '<i class="bi bi-info-circle-fill"></i> ' + roomData.name + ': нормативная освещённость ' + roomData.standard_illuminance_lux + ' лк';
        }
    }

    updateEfficiencyHint() {
        const lightSourceSelect = document.getElementById('lighting-light-source');
        const lumensInput = document.getElementById('lighting-lumens');
        const powerInput = document.getElementById('lighting-power');
        const hintSpan = document.getElementById('efficiency-hint');
        
        if (!hintSpan) return;
        
        if (lightSourceSelect && lightSourceSelect.value) {
            const lightSource = this.core.getLightSource(lightSourceSelect.value);
            if (lightSource) {
                hintSpan.innerHTML = '<i class="bi bi-lightbulb"></i> ' + lightSource.name + ': светоотдача ' + lightSource.luminous_efficacy_lm_per_w + ' лм/Вт, срок службы ' + (lightSource.service_life_hours / 1000) + ' тыс.ч';
                return;
            }
        }
        
        const lumens = parseFloat(lumensInput?.value || 0);
        const power = parseFloat(powerInput?.value || 0);
        if (lumens > 0 && power > 0) {
            const efficacy = Math.round(lumens / power);
            let efficiencyText = '';
            if (efficacy >= 100) efficiencyText = ' (высокая эффективность)';
            else if (efficacy >= 80) efficiencyText = ' (средняя эффективность)';
            else efficiencyText = ' (низкая эффективность, рекомендуется LED)';
            hintSpan.innerHTML = '<i class="bi bi-lightbulb"></i> Светоотдача: ' + efficacy + ' лм/Вт' + efficiencyText;
        } else {
            hintSpan.innerHTML = '<i class="bi bi-lightbulb"></i> Укажите световой поток и мощность для расчёта эффективности';
        }
    }

    updateLuminaireHint() {
        const luminaireTypeSelect = document.getElementById('lighting-luminaire-type');
        const hintSpan = document.getElementById('luminaire-hint');
        
        if (!luminaireTypeSelect || !hintSpan) return;
        
        const luminaireType = luminaireTypeSelect.value;
        const luminaireData = this.core.getLuminaireType(luminaireType);
        
        if (luminaireData) {
            hintSpan.innerHTML = '<i class="bi bi-info-circle-fill"></i> ' + luminaireData.name + ': ' + luminaireData.description + '. Рекомендуемая высота установки от ' + luminaireData.installation_height_min_m + ' м';
        }
    }

    getFormParams() {
        const getFloat = (id, def) => {
            const el = document.getElementById(id);
            const val = parseFloat(el?.value);
            return isNaN(val) ? def : val;
        };
        const getInt = (id, def) => {
            const el = document.getElementById(id);
            const val = parseInt(el?.value);
            return isNaN(val) ? def : val;
        };
        const getString = (id, def) => document.getElementById(id)?.value || def;

        return {
            room_type: getString('lighting-room-type', 'office'),
            room_length_m: getFloat('lighting-length', 10),
            room_width_m: getFloat('lighting-width', 8),
            room_height_m: getFloat('lighting-height', 2.7),
            working_plane_height_m: getFloat('lighting-working-height', 0.8),
            luminaire_type: getString('lighting-luminaire-type', 'surface'),
            light_source: getString('lighting-light-source', 'led'),
            lumens_per_fixture_lm: getInt('lighting-lumens', 3000),
            power_per_fixture_w: getInt('lighting-power', 25),
            operating_hours_per_day: getInt('lighting-hours', 10),
            electricity_tariff_rub_per_kwh: getFloat('lighting-tariff', 6),
            maintenance_factor: getFloat('lighting-maintenance', 0),
            wall_reflectance: getFloat('lighting-wall-reflectance', 0),
            ceiling_reflectance: getFloat('lighting-ceiling-reflectance', 0),
            floor_reflectance: getFloat('lighting-floor-reflectance', 0)
        };
    }

    async calculateAndDisplay() {
        const submitBtn = this.form.querySelector('button[type="submit"]');
        const originalText = submitBtn?.textContent;
        if (submitBtn) submitBtn.textContent = 'Расчёт...';

        try {
            const params = this.getFormParams();
            
            const roomData = this.core.getRoomType(params.room_type);
            if (roomData) {
                if (params.maintenance_factor === 0) params.maintenance_factor = roomData.maintenance_factor;
                if (params.wall_reflectance === 0) params.wall_reflectance = roomData.wall_reflectance;
                if (params.ceiling_reflectance === 0) params.ceiling_reflectance = roomData.ceiling_reflectance;
                if (params.floor_reflectance === 0) params.floor_reflectance = roomData.floor_reflectance;
            }
            
            const result = this.core.calculate(params);

            if (!result.success) {
                this.showErrors([result.error || 'Ошибка расчёта']);
                return;
            }

            this.displayResults(result.results);
            this.displayRecommendations(result.recommendations);
        } catch (error) {
            console.error('Ошибка расчёта:', error);
            this.showErrors(['Произошла ошибка при расчёте. Проверьте введённые данные.']);
        } finally {
            if (submitBtn) submitBtn.textContent = originalText;
        }
    }

    displayResults(results) {
        if (!results) return;
        
        this.setValue('lighting-result-area', results.room?.area_m2, 'м²');
        this.setValue('lighting-result-room-index', results.room?.room_index, '');
        
        this.setValue('lighting-result-required-lux', results.standards?.required_illuminance_lux, 'лк');
        this.setValue('lighting-result-utilization', results.standards?.utilization_factor, '');
        this.setValue('lighting-result-maintenance', results.standards?.maintenance_factor, '');
        
        this.setValue('lighting-result-required-flux', results.luminous_flux?.required_total_lm, 'лм');
        this.setValue('lighting-result-actual-flux', results.luminous_flux?.actual_total_lm, 'лм');
        this.setValue('lighting-result-actual-lux', results.luminous_flux?.actual_illuminance_lux, 'лк');
        
        this.setValue('lighting-result-fixtures-count', results.fixtures?.count, 'шт');
        this.setValue('lighting-result-fixtures-type', results.fixtures?.type, '');
        this.setValue('lighting-result-total-power', results.fixtures?.total_power_w, 'Вт');
        this.setValue('lighting-result-power-density', results.fixtures?.power_density_w_per_m2, 'Вт/м²');
        
        this.setValue('lighting-result-annual-energy', results.cost?.annual_energy_kwh, 'кВт·ч');
        this.setValue('lighting-result-annual-cost', results.cost?.annual_cost_rub, 'руб');
    }

    setValue(elementId, value, unit) {
        const el = document.getElementById(elementId);
        if (el && value !== undefined && value !== null && !isNaN(value)) {
            const formatted = typeof value === 'number' && Number.isInteger(value) ? value : 
                             typeof value === 'number' ? value.toFixed(2) : value;
            el.textContent = unit ? formatted + ' ' + unit : formatted;
        } else if (el && value !== undefined && value !== null) {
            el.textContent = value;
        } else if (el) {
            el.textContent = '—';
        }
    }

    displayRecommendations(recommendations) {
        const warningsContainer = document.getElementById('lighting-warnings');
        const recommendationsContainer = document.getElementById('lighting-recommendations');

        if (!warningsContainer || !recommendationsContainer) return;

        const errors = recommendations.filter(r => r.type === 'error');
        const warnings = recommendations.filter(r => r.type === 'warning');
        const info = recommendations.filter(r => r.type === 'info');
        const success = recommendations.filter(r => r.type === 'success');

        let warningsHtml = '';
        if (errors.length > 0) {
            warningsHtml += '<div style="background:#ffebee; border-left:4px solid #d32f2f; padding:12px; margin-bottom:16px; border-radius:4px;">';
            warningsHtml += '<strong style="color:#d32f2f;"><i class="bi bi-x-circle-fill"></i> Ошибки:</strong><ul style="margin:8px 0 0 20px;">';
            errors.forEach(e => warningsHtml += '<li style="color:#d32f2f;">' + e.message + '</li>');
            warningsHtml += '</ul></div>';
        }
        if (warnings.length > 0) {
            warningsHtml += '<div style="background:#fff4e5; border-left:4px solid #ed6c02; padding:12px; border-radius:4px;">';
            warningsHtml += '<strong style="color:#ed6c02;"><i class="bi bi-exclamation-triangle-fill"></i> Предупреждения:</strong><ul style="margin:8px 0 0 20px;">';
            warnings.forEach(w => warningsHtml += '<li style="color:#ed6c02;">' + w.message + '</li>');
            warningsHtml += '</ul></div>';
        }
        warningsContainer.innerHTML = warningsHtml;
        warningsContainer.style.display = (errors.length > 0 || warnings.length > 0) ? 'block' : 'none';

        let recHtml = '';
        if (info.length > 0) {
            recHtml += '<div style="background:#e3f2fd; border-left:4px solid #1976d2; padding:12px; margin-bottom:16px; border-radius:4px;">';
            recHtml += '<strong style="color:#1976d2;"><i class="bi bi-info-circle-fill"></i> Рекомендации:</strong><ul style="margin:8px 0 0 20px;">';
            info.forEach(i => recHtml += '<li>' + i.message + '</li>');
            recHtml += '</ul></div>';
        }
        if (success.length > 0) {
            recHtml += '<div style="background:#e8f5e9; border-left:4px solid #2e7d32; padding:12px; border-radius:4px;">';
            recHtml += '<strong style="color:#2e7d32;"><i class="bi bi-check-circle-fill"></i> Статус:</strong><ul style="margin:8px 0 0 20px;">';
            success.forEach(s => recHtml += '<li style="color:#2e7d32;">' + s.message + '</li>');
            recHtml += '</ul></div>';
        }
        recommendationsContainer.innerHTML = recHtml;
        recommendationsContainer.style.display = (info.length > 0 || success.length > 0) ? 'block' : 'none';
    }

    showErrors(errors) {
        const warningsContainer = document.getElementById('lighting-warnings');
        if (!warningsContainer || errors.length === 0) return;

        let html = '<div style="background:#ffebee; border-left:4px solid #d32f2f; padding:12px; border-radius:4px;">';
        html += '<strong style="color:#d32f2f;"><i class="bi bi-x-circle-fill"></i> Ошибки ввода:</strong><ul style="margin:8px 0 0 20px;">';
        errors.forEach(e => html += '<li style="color:#d32f2f;">' + e + '</li>');
        html += '</ul></div>';
        warningsContainer.innerHTML = html;
        warningsContainer.style.display = 'block';
    }

    clearResults() {
        const resultIds = [
            'lighting-result-area', 'lighting-result-room-index',
            'lighting-result-required-lux', 'lighting-result-utilization', 'lighting-result-maintenance',
            'lighting-result-required-flux', 'lighting-result-actual-flux', 'lighting-result-actual-lux',
            'lighting-result-fixtures-count', 'lighting-result-fixtures-type', 'lighting-result-total-power', 'lighting-result-power-density',
            'lighting-result-annual-energy', 'lighting-result-annual-cost'
        ];
        resultIds.forEach(id => {
            const el = document.getElementById(id);
            if (el) el.textContent = '—';
        });

        const warnings = document.getElementById('lighting-warnings');
        const recommendations = document.getElementById('lighting-recommendations');
        if (warnings) {
            warnings.innerHTML = '';
            warnings.style.display = 'none';
        }
        if (recommendations) {
            recommendations.innerHTML = '';
            recommendations.style.display = 'none';
        }
    }
}

window.LightingCalculatorUI = LightingCalculatorUI;
========================================
ФАЙЛ #8: data/templates/form.php
Расширение: php
Размер: 16,509 байт
========================================
<?php
/**
 * ФОРМА КАЛЬКУЛЯТОРА: Расчёт освещения
 * Версия: 1.0.0
 * Дата: 2026-06-05
 * Нормативы: СП 52.13330.2016 (с изм. №5 от 01.01.2026), СанПиН 1.2.3685-21
 */
?>
<div class="pro-split-calc">
    <div class="pro-grid">

        <div class="pro-form-col">
            <form id="lighting-form" class="pro-form" novalidate>

                <div class="pro-field">
                    <label class="pro-label">Тип помещения</label>
                    <select id="lighting-room-type" class="pro-select">
                        <option value="office" selected>Офис</option>
                        <option value="living">Жилая комната</option>
                        <option value="kitchen">Кухня</option>
                        <option value="bathroom">Ванная</option>
                        <option value="corridor">Коридор</option>
                        <option value="warehouse">Склад</option>
                        <option value="production">Производственное помещение</option>
                        <option value="school">Школа/класс</option>
                        <option value="hospital">Больница/палата</option>
                        <option value="retail">Магазин/торговый зал</option>
                    </select>
                    <span class="pro-hint" id="room-hint">Нормативная освещённость по СП 52.13330.2016</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Длина помещения (м)</label>
                    <input type="number" id="lighting-length" class="pro-input" value="10" step="0.5" min="1" max="100">
                    <span class="pro-hint">от 1 до 100 м</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Ширина помещения (м)</label>
                    <input type="number" id="lighting-width" class="pro-input" value="8" step="0.5" min="1" max="100">
                    <span class="pro-hint">от 1 до 100 м</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Высота помещения (м)</label>
                    <input type="number" id="lighting-height" class="pro-input" value="2.7" step="0.1" min="2.2" max="15">
                    <span class="pro-hint">от 2.2 до 15 м</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Высота рабочей поверхности (м)</label>
                    <input type="number" id="lighting-working-height" class="pro-input" value="0.8" step="0.1" min="0" max="2">
                    <span class="pro-hint">стандартно 0.8 м (стол), 0 м (пол)</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Тип светильника</label>
                    <select id="lighting-luminaire-type" class="pro-select">
                        <option value="surface" selected>Накладной светильник</option>
                        <option value="downlight">Встраиваемый светильник</option>
                        <option value="suspended">Подвесной светильник</option>
                        <option value="highbay">Высокопролётный светильник</option>
                    </select>
                    <span class="pro-hint" id="luminaire-hint">Влияет на коэффициент использования</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Тип источника света</label>
                    <select id="lighting-light-source" class="pro-select">
                        <option value="led" selected>LED (светодиодный)</option>
                        <option value="fluorescent">Люминесцентный</option>
                        <option value="halogen">Галогенный</option>
                        <option value="incandescent">Лампа накаливания</option>
                    </select>
                    <span class="pro-hint" id="efficiency-hint">Светодиодные лампы самые экономичные</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Световой поток одного светильника (лм)</label>
                    <input type="number" id="lighting-lumens" class="pro-input" value="3000" step="100" min="100" max="50000">
                    <span class="pro-hint">LED 25 Вт = ~3000 лм, люминесцентный 36 Вт = ~2800 лм</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Мощность одного светильника (Вт)</label>
                    <input type="number" id="lighting-power" class="pro-input" value="25" step="1" min="1" max="500">
                    <span class="pro-hint">Используется для расчёта энергопотребления</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Время работы освещения в сутки (ч)</label>
                    <input type="number" id="lighting-hours" class="pro-input" value="10" step="1" min="0" max="24">
                    <span class="pro-hint">для офисов 8-10 ч, для складов 4-6 ч</span>
                </div>

                <div class="pro-field">
                    <label class="pro-label">Тариф на электроэнергию (руб/кВт·ч)</label>
                    <input type="number" id="lighting-tariff" class="pro-input" value="6.0" step="0.5" min="1" max="15">
                    <span class="pro-hint">Для юрлиц может быть выше</span>
                </div>

                <details class="pro-details">
                    <summary class="pro-details-summary">Дополнительные параметры (коэффициенты)</summary>
                    <div class="pro-details-content">
                        <div class="pro-field">
                            <label class="pro-label">Коэффициент запаса (загрязнение)</label>
                            <input type="number" id="lighting-maintenance" class="pro-input" value="" step="0.05" min="0.5" max="0.95" placeholder="Автоматически">
                            <span class="pro-hint">Оставьте пустым — подставится по типу помещения</span>
                        </div>
                        <div class="pro-field">
                            <label class="pro-label">Коэффициент отражения стен</label>
                            <input type="number" id="lighting-wall-reflectance" class="pro-input" value="" step="0.1" min="0.2" max="0.8" placeholder="Автоматически">
                            <span class="pro-hint">0.5 для светлых стен, 0.3 для тёмных</span>
                        </div>
                        <div class="pro-field">
                            <label class="pro-label">Коэффициент отражения потолка</label>
                            <input type="number" id="lighting-ceiling-reflectance" class="pro-input" value="" step="0.1" min="0.3" max="0.9" placeholder="Автоматически">
                            <span class="pro-hint">0.7 для белого потолка, 0.5 для серого</span>
                        </div>
                        <div class="pro-field">
                            <label class="pro-label">Коэффициент отражения пола</label>
                            <input type="number" id="lighting-floor-reflectance" class="pro-input" value="" step="0.1" min="0.1" max="0.5" placeholder="Автоматически">
                            <span class="pro-hint">0.2 для тёмного пола, 0.3 для светлого</span>
                        </div>
                    </div>
                </details>

                <div class="pro-buttons">
                    <button type="submit" class="pro-btn pro-btn-primary">Рассчитать освещение</button>
                    <button type="reset" class="pro-btn pro-btn-secondary">Сбросить</button>
                </div>

            </form>
        </div>

        <div class="pro-results-col">
            <div class="pro-results-card">
                <h3 class="pro-results-title">Результаты расчёта освещения</h3>

                <div class="pro-result-group">
                    <h4 class="pro-result-group-title">Параметры помещения</h4>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Площадь помещения:</span>
                        <span class="pro-result-value" id="lighting-result-area">— м²</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Индекс помещения:</span>
                        <span class="pro-result-value" id="lighting-result-room-index">—</span>
                    </div>
                </div>

                <div class="pro-result-group">
                    <h4 class="pro-result-group-title">Нормы и коэффициенты</h4>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Нормативная освещённость:</span>
                        <span class="pro-result-value" id="lighting-result-required-lux">— лк</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Коэффициент использования:</span>
                        <span class="pro-result-value" id="lighting-result-utilization">—</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Коэффициент запаса:</span>
                        <span class="pro-result-value" id="lighting-result-maintenance">—</span>
                    </div>
                </div>

                <div class="pro-result-group">
                    <h4 class="pro-result-group-title">Световой поток</h4>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Требуемый суммарный поток:</span>
                        <span class="pro-result-value" id="lighting-result-required-flux">— лм</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Фактический суммарный поток:</span>
                        <span class="pro-result-value" id="lighting-result-actual-flux">— лм</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Фактическая освещённость:</span>
                        <span class="pro-result-value" id="lighting-result-actual-lux">— лк</span>
                    </div>
                </div>

                <div class="pro-result-group">
                    <h4 class="pro-result-group-title">Количество светильников</h4>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Количество:</span>
                        <span class="pro-result-value" id="lighting-result-fixtures-count">— шт</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Тип светильника:</span>
                        <span class="pro-result-value" id="lighting-result-fixtures-type">—</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Суммарная мощность:</span>
                        <span class="pro-result-value" id="lighting-result-total-power">— Вт</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Удельная мощность:</span>
                        <span class="pro-result-value" id="lighting-result-power-density">— Вт/м²</span>
                    </div>
                </div>

                <div class="pro-result-group">
                    <h4 class="pro-result-group-title">Эксплуатационные расходы</h4>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Годовое потребление:</span>
                        <span class="pro-result-value" id="lighting-result-annual-energy">— кВт·ч</span>
                    </div>
                    <div class="pro-result-item">
                        <span class="pro-result-label">Годовые затраты:</span>
                        <span class="pro-result-value" id="lighting-result-annual-cost">— руб</span>
                    </div>
                </div>

                <div id="lighting-warnings" class="pro-warning" style="display:none;"></div>
                <div id="lighting-recommendations" class="pro-recommendation" style="display:none;"></div>

                <div class="pro-note pro-normative-footer">
                    Расчёт по СП 52.13330.2016 (с изм. №5 от 01.01.2026) и СанПиН 1.2.3685-21.
                    Для точного проектирования рекомендуется использовать DIALux.
                </div>

            </div>
        </div>
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const roomTypeSelect = document.getElementById('lighting-room-type');
    const maintenanceInput = document.getElementById('lighting-maintenance');
    const wallReflectance = document.getElementById('lighting-wall-reflectance');
    const ceilingReflectance = document.getElementById('lighting-ceiling-reflectance');
    const floorReflectance = document.getElementById('lighting-floor-reflectance');
    
    function updateDefaultValues() {
        if (!roomTypeSelect) return;
        
        const roomType = roomTypeSelect.value;
        let maintenance = 0.8;
        let wall = 0.5;
        let ceiling = 0.7;
        let floor = 0.2;
        
        switch(roomType) {
            case 'warehouse': maintenance = 0.7; wall = 0.4; ceiling = 0.5; break;
            case 'production': maintenance = 0.7; wall = 0.4; ceiling = 0.5; break;
            case 'school': maintenance = 0.8; wall = 0.5; ceiling = 0.7; floor = 0.3; break;
            default: maintenance = 0.8; wall = 0.5; ceiling = 0.7; floor = 0.2;
        }
        
        if (maintenanceInput && !maintenanceInput.value) {
            maintenanceInput.placeholder = maintenance;
        }
        if (wallReflectance && !wallReflectance.value) {
            wallReflectance.placeholder = wall;
        }
        if (ceilingReflectance && !ceilingReflectance.value) {
            ceilingReflectance.placeholder = ceiling;
        }
        if (floorReflectance && !floorReflectance.value) {
            floorReflectance.placeholder = floor;
        }
    }
    
    if (roomTypeSelect) {
        roomTypeSelect.addEventListener('change', updateDefaultValues);
        updateDefaultValues();
    }
});
</script>
========================================
ФАЙЛ #9: data/templates/related-modules.php
Расширение: php
Размер: 2,121 байт
========================================
<?php
/**
 * РАЗРАБОТЧИК: tech-stc.ru
 * ПРОЕКТ: Калькулятор расчёта освещения
 * ВЕРСИЯ: 1.0.0
 * ДАТА: 2026-06-05
 * @author tech-stc.ru
 * 
 * Динамический блок связанных модулей
 * Читает modules.json, рендерит карточки/ссылки
 * Безопасен: молча завершается, если файл отсутствует или невалиден
 */

$configPath = __DIR__ . '/../config/modules.json';
if (!file_exists($configPath)) return;

$raw = file_get_contents($configPath);
$modules = json_decode($raw, true);
if (!$modules || empty($modules['items'])) return;
?>

<section class="related-modules" style="margin: 24px 0 16px;">
    <h3 class="pro-results-title"><?= htmlspecialchars($modules['section_title'] ?? 'Связанные расчёты и инструменты') ?></h3>
    <div class="modules-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin-top: 14px;">
        <?php foreach ($modules['items'] as $mod): 
            $url = htmlspecialchars($mod['url'] ?? '#');
            $icon = htmlspecialchars($mod['icon'] ?? 'bi-link-45deg');
            $title = htmlspecialchars($mod['title'] ?? '');
            $desc = htmlspecialchars($mod['description'] ?? '');
        ?>
        <a href="<?= $url ?>" class="categories-card" style="text-decoration: none; display: block; padding: 12px; background: #fff; border: 1px solid #e0e0e0; border-radius: 8px; transition: all 0.2s ease;">
            <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
                <i class="bi <?= $icon ?>" style="font-size: 1.4rem; color: #0d6efd; flex-shrink: 0;"></i>
                <h4 style="margin: 0; font-size: 0.95rem; font-weight: 600; color: #212529;"><?= $title ?></h4>
            </div>
            <p style="margin: 0; font-size: 0.85rem; color: #6c757d; line-height: 1.4;"><?= $desc ?></p>
        </a>
        <?php endforeach; ?>
    </div>
</section>
========================================
ФАЙЛ #10: index.php
Расширение: php
Размер: 4,679 байт
========================================
<?php
/**
 * РАЗРАБОТЧИК: tech-stc.ru
 * ПРОЕКТ: Калькулятор расчёта освещения
 * ВЕРСИЯ: 1.0.0
 * ДАТА: 2026-06-05
 * @author tech-stc.ru
 * 
 * НОРМАТИВЫ: СП 52.13330.2016 (с изм. №5 от 01.01.2026), СанПиН 1.2.3685-21
 */

$moduleBasePath = dirname($_SERVER['SCRIPT_NAME']);
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
$host = $_SERVER['HTTP_HOST'];
$uri = $_SERVER['REQUEST_URI'];
$canonicalUrl = $protocol . $host . $uri;
$currentDate = date('Y-m-d');

$contentJsonPath = __DIR__ . '/data/config/content.json';
$contentJson = [];
if (file_exists($contentJsonPath)) {
    $contentJson = json_decode(file_get_contents($contentJsonPath), true);
}

$technicalJsonPath = __DIR__ . '/data/config/technical.json';
$technicalJson = [];
if (file_exists($technicalJsonPath)) {
    $technicalJson = json_decode(file_get_contents($technicalJsonPath), true);
}

$moduleContent = require __DIR__ . '/content.php';

require_once $_SERVER['DOCUMENT_ROOT'] . '/shell/json-ld-generator.php';

$jsonLdParams = [
    'title' => $contentJson['seo']['title'] ?? 'Калькулятор расчёта освещения | Расчет освещённости помещения | ТЕХСТАНДАРТ',
    'description' => $contentJson['seo']['description'] ?? 'Расчёт освещённости помещения: количество светильников, мощность, световой поток. Соответствие СП 52.13330.2016, СанПиН 1.2.3685-21.',
    'url' => $canonicalUrl,
    'breadcrumb' => [
        ['name' => 'Главная', 'url' => $protocol . $host . '/'],
        ['name' => 'Калькуляторы', 'url' => $protocol . $host . '/engineering-calculators/'],
        ['name' => 'Инженерные системы', 'url' => $protocol . $host . '/engineering-calculators/engineering-systems/'],
        ['name' => $contentJson['seo']['h1'] ?? 'Расчёт освещения', 'url' => '']
    ],
    'subcategory' => 'LightingEngineering',
    'feature_list' => $contentJson['json_ld']['featureList'] ?? 'Расчёт освещённости, подбор светильников, учёт коэффициентов отражения, расчёт энергопотребления',
    'image' => $contentJson['open_graph']['image'] ?? '',
    'normative_compliance' => $contentJson['meta']['normative_compliance'] ?? [],
    'howto_steps' => [
        ['name' => 'Выберите тип помещения', 'text' => 'От типа зависит нормативная освещённость'],
        ['name' => 'Введите размеры помещения', 'text' => 'Длина, ширина, высота'],
        ['name' => 'Выберите тип светильника и лампы', 'text' => 'Влияет на эффективность и стоимость'],
        ['name' => 'Нажмите "Рассчитать"', 'text' => 'Калькулятор выполнит расчёт'],
        ['name' => 'Получите результат', 'text' => 'Количество светильников, мощность, годовые затраты']
    ],
    'product_name' => 'Светодиодные светильники',
    'product_low_price' => 500,
    'product_high_price' => 5000,
    'product_offer_count' => 100,
    'product_brand' => 'Philips, Osram, Gauss, IEK'
];

try {
    $jsonLd = generateJsonLd($jsonLdParams);
} catch (Exception $e) {
    error_log('JSON-LD generation error: ' . $e->getMessage());
    $jsonLd = [];
}

$pageData = [
    'title' => $jsonLdParams['title'],
    'description' => $jsonLdParams['description'],
    'h1' => $contentJson['seo']['h1'] ?? 'Калькулятор расчёта освещения',
    'content' => $moduleContent,

    'breadcrumb' => $jsonLdParams['breadcrumb'],

    'template' => 'calculator',
    'canonical_url' => $canonicalUrl,
    'robots' => $contentJson['seo']['robots'] ?? 'index, follow',
    'keywords' => $contentJson['seo']['keywords'] ?? '',

    'og_title' => $contentJson['open_graph']['title'] ?? '',
    'og_description' => $contentJson['open_graph']['description'] ?? '',
    'og_image' => $contentJson['open_graph']['image'] ?? '',
    'og_type' => $contentJson['open_graph']['type'] ?? 'website',
    'og_locale' => $contentJson['open_graph']['locale'] ?? 'ru_RU',

    'json_ld' => $jsonLd
];

require_once $_SERVER['DOCUMENT_ROOT'] . '/shell/render.php';
renderModule($pageData);
========================================
ФАЙЛ #11: test-runner.html
Расширение: html
Размер: 12,052 байт
========================================
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Тест калькулятора расчёта освещения</title>
    <style>
        body { font-family: 'Courier New', monospace; padding: 20px; background: #f5f5f5; margin: 0; }
        .pass { color: #2e7d32; font-weight: bold; }
        .fail { color: #d32f2f; font-weight: bold; }
        .test { margin-bottom: 12px; padding: 12px; background: white; border-left: 4px solid #ccc; border-radius: 4px; }
        .test-header { font-weight: bold; margin-bottom: 6px; }
        .test-details { font-size: 12px; color: #666; margin-top: 6px; }
        .summary { margin-top: 20px; padding: 15px; background: white; border-radius: 4px; font-weight: bold; }
        .note { background: #fff3e0; padding: 10px; margin-bottom: 20px; border-left: 4px solid #ff9800; font-size: 13px; }
    </style>
</head>
<body>
    <h3>Тест калькулятора расчёта освещения</h3>
    <div class="note">
        <strong>Эталонная методика:</strong> СП 52.13330.2016 (с изм. №5 от 01.01.2026)<br>
        Формулы:<br>
        - Индекс помещения: i = (L × W) / (Hр × (L + W))<br>
        - Световой поток: Ф = (E × S × Кз) / (U × Кп)<br>
        - Количество светильников: N = Ф / Ф_св<br>
        - Удельная мощность: P_уд = P_total / S (Вт/м²)
    </div>
    <div id="results"></div>

    <script src="data/js/core.js"></script>
    <script>
        function referenceRoomIndex(length, width, height, workingHeight) {
            const effectiveHeight = height - workingHeight;
            if (effectiveHeight <= 0) return 1.0;
            return parseFloat(((length * width) / (effectiveHeight * (length + width))).toFixed(2));
        }

        function referenceLuminousFlux(illuminance, area, utilization, maintenance) {
            const safety = 1.1;
            return Math.round((illuminance * area * safety) / (utilization * maintenance));
        }

        function referenceFixturesCount(totalFlux, lumensPerFixture) {
            return Math.max(1, Math.ceil(totalFlux / lumensPerFixture));
        }

        async function runTests() {
            const resultsDiv = document.getElementById('results');
            resultsDiv.innerHTML = 'Загрузка калькулятора...';
            
            const core = new LightingCalculatorCore();
            const basePath = '/engineering-calculators/engineering-systems/lighting-calculator';
            
            try {
                await core.load(basePath);
            } catch (e) {
                resultsDiv.innerHTML = '<span class="fail">Ошибка загрузки: ' + e.message + '</span>';
                return;
            }
            
            const tests = [
                {
                    name: "Индекс помещения: 10x8м, высота 2.7м, раб. поверхность 0.8м = 1.68",
                    fn: () => {
                        const result = core.calculateRoomIndex(10, 8, 2.7, 0.8);
                        const expected = referenceRoomIndex(10, 8, 2.7, 0.8);
                        return { ok: Math.abs(result - expected) < 0.05, actual: result, expected: expected };
                    }
                },
                {
                    name: "Индекс помещения: 5x4м, высота 3.5м, раб. поверхность 0.8м = 0.76",
                    fn: () => {
                        const result = core.calculateRoomIndex(5, 4, 3.5, 0.8);
                        const expected = referenceRoomIndex(5, 4, 3.5, 0.8);
                        return { ok: Math.abs(result - expected) < 0.05, actual: result, expected: expected };
                    }
                },
                {
                    name: "Световой поток: офис 80м², 500 лк, U=0.5, M=0.8 = 110000 лм",
                    fn: () => {
                        const result = core.calculateRequiredLuminousFlux(500, 80, 0.5, 0.8);
                        const expected = referenceLuminousFlux(500, 80, 0.5, 0.8);
                        return { ok: Math.abs(result - expected) < 500, actual: result, expected: expected };
                    }
                },
                {
                    name: "Количество светильников: 110000 лм / 3000 лм = 37 шт",
                    fn: () => {
                        const result = core.calculateFixturesCount(110000, 3000);
                        const expected = referenceFixturesCount(110000, 3000);
                        return { ok: result === expected, actual: result, expected: expected };
                    }
                },
                {
                    name: "Количество светильников: 5000 лм / 3000 лм = 2 шт",
                    fn: () => {
                        const result = core.calculateFixturesCount(5000, 3000);
                        const expected = referenceFixturesCount(5000, 3000);
                        return { ok: result === expected, actual: result, expected: expected };
                    }
                },
                {
                    name: "Мощность и затраты: 37 шт × 25 Вт × 10ч × 365дн × 6 руб = 20275 руб",
                    fn: () => {
                        const result = core.calculatePowerAndCost(37, 25, 10, 6);
                        const expectedAnnualCost = Math.round(37 * 25 / 1000 * 10 * 365 * 6);
                        return { ok: Math.abs(result.annualCostRub - expectedAnnualCost) < 100, actual: result.annualCostRub, expected: expectedAnnualCost };
                    }
                },
                {
                    name: "Офис 10x8м: расчёт количества светильников (должно быть >0)",
                    fn: () => {
                        const params = {
                            room_type: "office",
                            room_length_m: 10,
                            room_width_m: 8,
                            room_height_m: 2.7,
                            working_plane_height_m: 0.8,
                            luminaire_type: "surface",
                            light_source: "led",
                            lumens_per_fixture_lm: 3000,
                            power_per_fixture_w: 25,
                            operating_hours_per_day: 10,
                            electricity_tariff_rub_per_kwh: 6
                        };
                        const result = core.calculate(params);
                        return { ok: result.success === true && result.results?.fixtures?.count > 0, actual: result.results?.fixtures?.count };
                    }
                },
                {
                    name: "Проверка соответствия освещённости норме (должно быть OK)",
                    fn: () => {
                        const params = {
                            room_type: "office",
                            room_length_m: 10,
                            room_width_m: 8,
                            room_height_m: 2.7,
                            luminaire_type: "surface",
                            light_source: "led",
                            lumens_per_fixture_lm: 3000,
                            power_per_fixture_w: 25,
                            operating_hours_per_day: 10
                        };
                        const result = core.calculate(params);
                        const actualLux = result.results?.luminous_flux?.actual_illuminance_lux;
                        const requiredLux = result.results?.standards?.required_illuminance_lux;
                        const isOk = actualLux >= requiredLux * 0.9 && actualLux <= requiredLux * 1.2;
                        return { ok: isOk, actual: actualLux, expected: requiredLux };
                    }
                },
                {
                    name: "Запрет ламп накаливания: должно быть предупреждение",
                    fn: () => {
                        const params = {
                            room_type: "office",
                            room_length_m: 10,
                            room_width_m: 8,
                            light_source: "incandescent",
                            lumens_per_fixture_lm: 800,
                            power_per_fixture_w: 60
                        };
                        const result = core.calculate(params);
                        const hasWarning = result.recommendations.some(r => r.message.includes('запрещены'));
                        return { ok: hasWarning === true, actual: hasWarning };
                    }
                },
                {
                    name: "Удельная мощность: офис 80м², 925 Вт = 11.6 Вт/м²",
                    fn: () => {
                        const params = {
                            room_type: "office",
                            room_length_m: 10,
                            room_width_m: 8,
                            room_height_m: 2.7,
                            luminaire_type: "surface",
                            light_source: "led",
                            lumens_per_fixture_lm: 3000,
                            power_per_fixture_w: 25,
                            operating_hours_per_day: 10
                        };
                        const result = core.calculate(params);
                        const powerDensity = result.results?.fixtures?.power_density_w_per_m2;
                        return { ok: powerDensity > 0 && powerDensity < 20, actual: powerDensity };
                    }
                }
            ];
            
            let passed = 0;
            let failed = 0;
            let html = '';
            
            for (let test of tests) {
                let isOk = false;
                let actual = '—';
                let expected = '—';
                try {
                    const res = test.fn();
                    isOk = res.ok;
                    actual = res.actual;
                    expected = res.expected;
                } catch (e) {
                    isOk = false;
                    actual = e.message;
                }
                
                if (isOk) {
                    passed++;
                    html += '<div class="test" style="border-left-color: #2e7d32;">';
                    html += '<div class="test-header">[OK] ' + test.name + '</div>';
                    html += '<div class="test-details">Пройдено</div>';
                    html += '</div>';
                } else {
                    failed++;
                    html += '<div class="test" style="border-left-color: #d32f2f;">';
                    html += '<div class="test-header">[FAIL] ' + test.name + '</div>';
                    html += '<div class="test-details">Факт: ' + actual + ' | Ожидание: ' + expected + '</div>';
                    html += '</div>';
                }
            }
            
            html += '<div class="summary">';
            html += 'Итого: ' + passed + ' пройдено, ' + failed + ' не пройдено';
            html += (failed === 0) ? ' Все тесты успешны!' : ' Есть ошибки, требуется исправление';
            html += '</div>';
            resultsDiv.innerHTML = html;
        }
        
        runTests();
    </script>
</body>
</html>