Shinfly

Check if your bird is having happy space

<!DOCTYPE html>

<html lang=”en”>

<head>

    <meta charset=”UTF-8″>

    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>

    <title>Shinfly Cage & Setup Evaluation</title>

    <script src=”https://cdn.tailwindcss.com”></script>

    <style>

        @import url(‘https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap’);

        body {

            font-family: ‘Inter’, sans-serif;

            background-color: #f7f9fb;

        }

        .container {

            padding-bottom: 8rem; /* Space for fixed results panel */

        }

        /* Custom track style for range input */

        input[type=range] {

            -webkit-appearance: none;

            width: 100%;

            height: 8px;

            background: #d1d5db;

            border-radius: 4px;

            outline: none;

            transition: opacity .15s ease-in-out;

        }

        input[type=range]::-webkit-slider-thumb {

            -webkit-appearance: none;

            appearance: none;

            width: 24px;

            height: 24px;

            border-radius: 50%;

            background: #2563eb;

            cursor: pointer;

            border: 3px solid white;

            box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);

        }

        input[type=range]::-moz-range-thumb {

            width: 24px;

            height: 24px;

            border-radius: 50%;

            background: #2563eb;

            cursor: pointer;

            border: 3px solid white;

            box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);

        }

        .score-display {

            transition: all 0.3s ease-in-out;

            will-change: transform, box-shadow;

        }

    </style>

</head>

<body>

<div id=”app” class=”min-h-screen”>

    <header class=”bg-white shadow-md sticky top-0 z-10″>

        <div class=”max-w-3xl mx-auto p-4 flex justify-between items-center”>

            <h1 class=”text-xl font-bold text-gray-800″>Shinfly Setup Evaluation</h1>

            <span id=”version” class=”text-xs text-gray-500 bg-gray-100 px-2 py-1 rounded-full”>Draft v4</span>

        </div>

    </header>

    <main class=”container max-w-3xl mx-auto px-4 py-8″>

        <p class=”text-gray-600 mb-6 text-center”>Rate each metric from 1 (Poor) to 5 (Excellent). Click “Submit Evaluation” to calculate the final score.</p>

        <!– Dynamic Form Sections will be rendered here –>

        <div id=”form-container”></div>

        <!– Add-on Sections –>

        <div class=”mt-12 bg-white p-6 rounded-xl shadow-lg border-t-4 border-yellow-500″>

            <h2 class=”text-2xl font-semibold text-gray-800 mb-4″>Add-on Section</h2>

            

            <label for=”observations” class=”block text-lg font-medium text-gray-700 mb-2″>Observations (Notes for Inspector)</label>

            <textarea id=”observations” rows=”4″ class=”w-full p-3 border border-gray-300 rounded-lg focus:ring-yellow-500 focus:border-yellow-500 transition”></textarea>

            <div class=”mt-6″>

                <label for=”photo-upload” class=”block text-lg font-medium text-gray-700 mb-2″>Photos / Video Evidence (Placeholder)</label>

                <div class=”p-4 border-2 border-dashed border-gray-300 rounded-lg text-center text-gray-500″>

                      Upload slots (Functionality to be implemented later)

                </div>

            </div>

        </div>

    </main>

    <!– Fixed Results Panel – Initial state is locked –>

    <div class=”fixed bottom-0 left-0 right-0 bg-gray-800 text-white shadow-2xl z-20″>

        <div class=”max-w-3xl mx-auto p-4 flex flex-col sm:flex-row justify-between items-center gap-4″>

            

            <div class=”text-center sm:text-left”>

                <div class=”text-sm font-medium uppercase text-gray-300″>Total Weighted Index</div>

                <div id=”final-score-display” class=”text-5xl font-extrabold score-display text-gray-400″>

                    —

                </div>

            </div>

            <div class=”flex flex-col items-center sm:items-end”>

                <div id=”score-interpretation” class=”text-xl font-semibold text-gray-400″>

                    Ready for Evaluation

                </div>

                <!– This button triggers the calculation and then becomes the “View Results” button –>

                <button id=”submit-button” onclick=”submitEvaluation()” class=”mt-2 px-6 py-3 bg-blue-600 hover:bg-blue-700 rounded-full font-medium transition duration-150 shadow-md transform active:scale-95″>

                    Submit Evaluation

                </button>

            </div>

        </div>

    </div>

    <!– Recommendations Modal –>

    <div id=”recommendations-modal” class=”hidden fixed inset-0 bg-gray-900 bg-opacity-75 z-50 flex items-center justify-center p-4″ onclick=”hideRecommendations(event)”>

        <div class=”bg-white rounded-xl shadow-2xl p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto” onclick=”event.stopPropagation()”>

            <div class=”flex justify-between items-center mb-4″>

                <h3 class=”text-2xl font-bold text-gray-800″>Evaluation Results & Insights</h3>

                <button onclick=”hideRecommendations()” class=”text-gray-400 hover:text-gray-600 text-2xl”>&times;</button>

            </div>

            

            <!– Dynamic Summary Section –>

            <div id=”modal-summary” class=”mb-6 p-4 rounded-xl border-b-4 border-gray-300″></div>

            <h4 class=”text-xl font-semibold text-blue-600 mb-3″>Overall Recommendation</h4>

            <p id=”overall-recommendation-text” class=”text-gray-700 mb-6″></p>

            <h4 class=”text-xl font-semibold text-red-600 mb-3 flex items-center”>

                <svg xmlns=”http://www.w3.org/2000/svg” class=”h-6 w-6 mr-2″ fill=”none” viewBox=”0 0 24 24″ stroke=”currentColor”>

                    <path stroke-linecap=”round” stroke-linejoin=”round” stroke-width=”2″ d=”M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.332 16c-.77 1.333.192 3 1.732 3z” />

                </svg>

                Automatic Flags (Immediate Attention: Score 1 or 2)

            </h4>

            <ul id=”flagged-items” class=”space-y-3 mb-8 text-gray-700″>

                <li class=”text-gray-500″>No low scores detected yet.</li>

            </ul>

            <h4 class=”text-xl font-semibold text-gray-800 border-b pb-2 mb-4″>Detailed Performance Breakdown</h4>

            <div id=”detailed-insights” class=”space-y-8 mb-8″>

                <!– Detailed Section Reports will go here –>

            </div>

            <button onclick=”hideRecommendations()” class=”mt-6 w-full py-3 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium rounded-lg transition duration-150″>Close</button>

        </div>

    </div>

</div>

<script type=”text/javascript”>

    

    // — BIRD SPECIES DATA (NEW) —

    // Sizes are L, W, H in inches. Null for “Aviary preferred/required”.

    const BIRD_SPECIES_DATA = {

        “Budgerigar (Budgie / Parakeet)”: { single: [18, 18, 18], pair: [30, 18, 18], four: [36, 24, 24], six: [48, 30, 30], bar_spacing: ‘≤ 1/2 inch’ },

        “Lovebird”: { single: [24, 18, 24], pair: [30, 18, 24], four: [36, 24, 24], six: [48, 30, 30], bar_spacing: ‘1/2 inch’ },

        “Cockatiel”: { single: [24, 18, 24], pair: [36, 24, 24], four: [48, 30, 30], six: [60, 36, 36], bar_spacing: ‘1/2 inch’ },

        “Conure (Green-cheek, Sun)”: { single: [24, 24, 30], pair: [36, 24, 36], four: [48, 30, 36], six: [60, 36, 48], bar_spacing: ‘1/2–3/4 inch’ },

        “Ring-neck Parakeet”: { single: [30, 30, 36], pair: [48, 36, 48], four: [60, 36, 48], six: null, bar_spacing: ‘3/4–1 inch’ }, 

        “Cockatoo (Small–Medium)”: { single: [36, 30, 48], pair: [48, 36, 60], four: [72, 48, 60], six: null, bar_spacing: ‘3/4–1 inch’ },

        “African Grey”: { single: [36, 24, 48], pair: [48, 36, 60], four: null, six: null, bar_spacing: ‘3/4–1 inch’ },

        “Macaw (Large species)”: { single: [60, 48, 60], pair: [72, 60, 72], four: null, six: null, bar_spacing: ‘1–1.5 inch’ },

        “Finches / Canaries”: { single: [18, 18, 18], pair: [30, 18, 18], four: [36, 24, 24], six: [48, 30, 30], bar_spacing: ‘1/4 inch’ },

        “Parrotlet / Small Parrot”: { single: [18, 18, 24], pair: [24, 18, 24], four: [30, 24, 24], six: [36, 24, 24], bar_spacing: ‘1/2 inch’ }

    };

    // Framework Data (Criteria, Weights, Descriptions)

    const FRAMEWORK = [

        {

            title: “1. Cage Design & Dimensions”,

            weight: 0.20, // 20%

            color: ‘border-l-4 border-teal-500’,

            metrics: [

                { id: “size”, description: “Size & Space”, guidelines: “Score is calculated automatically based on species requirements and cage dimensions (L x W x H).” },

                { id: “mesh”, description: “Mesh Safety”, guidelines: “1 = Unsafe / 5 = Ideal build” },

                { id: “perch”, description: “Perch Layout”, guidelines: “1 = Single metal bar / 5 = Ergonomic layout” },

            ]

        },

        {

            title: “2. Ventilation & Lighting”,

            weight: 0.15, // 15%

            color: ‘border-l-4 border-emerald-500’,

            metrics: [

                { id: “airflow”, description: “Airflow”, guidelines: “1 = Stuffy / 5 = Fresh circulation” },

                { id: “light”, description: “Natural Light”, guidelines: “1 = Dark / 5 = Bright balanced light” },

            ]

        },

        {

            title: “3. Hygiene & Cleanliness”,

            weight: 0.15, // 15%

            color: ‘border-l-4 border-yellow-500’,

            metrics: [

                { id: “floor”, description: “Floor & Tray Cleanliness”, guidelines: “1 = Soiled / 5 = Clean daily” },

                { id: “feed_water_hygiene”, description: “Feed & Water Hygiene”, guidelines: “1 = Dirty / 5 = Sanitary routine” },

                { id: “waste”, description: “Waste Management”, guidelines: “1 = Neglected / 5 = Disciplined” },

            ]

        },

        {

            title: “4. Nest Boxes & Breeding Setup”,

            weight: 0.15, // 15%

            color: ‘border-l-4 border-orange-500’,

            metrics: [

                { id: “nest_placement”, description: “Number & Placement”, guidelines: “1 = Unsafe / 5 = Proper placement” },

                { id: “nest_hygiene”, description: “Hygiene & Rotation”, guidelines: “1 = Reused / 5 = Disinfected” },

                { id: “labeling”, description: “Labeling & Tracking”, guidelines: “1 = Unmarked / 5 = Systematic” },

            ]

        },

        {

            title: “5. Nutrition & Water Management”,

            weight: 0.15, // 15%

            color: ‘border-l-4 border-red-500’,

            metrics: [

                { id: “diet”, description: “Diet Diversity”, guidelines: “1 = Seed-only / 5 = Balanced diet” },

                { id: “supplements”, description: “Supplements & Calcium”, guidelines: “1 = Absent / 5 = Routine” },

                { id: “water_quality”, description: “Water Quality”, guidelines: “1 = Dirty / 5 = Fresh” },

            ]

        },

        {

            title: “6. Behaviour & Enrichment”,

            weight: 0.10, // 10%

            color: ‘border-l-4 border-pink-500’,

            metrics: [

                { id: “toys”, description: “Toys & Play”, guidelines: “1 = None / 5 = Varied” },

                { id: “stress”, description: “Stress Indicators”, guidelines: “1 = Visible stress / 5 = Calm” },

                { id: “social”, description: “Social Balance”, guidelines: “1 = Conflict / 5 = Stable pairs” },

            ]

        },

        {

            title: “7. Environment & Safety”,

            weight: 0.10, // 10%

            color: ‘border-l-4 border-indigo-500’,

            metrics: [

                { id: “location”, description: “Location & Air Quality”, guidelines: “1 = Hazardous / 5 = Safe” },

                { id: “temp”, description: “Temperature & Humidity”, guidelines: “1 = Extreme / 5 = Stable” },

                { id: “noise”, description: “Noise Control”, guidelines: “1 = Constant noise / 5 = Calm zone” },

            ]

        },

    ];

    let scores = {}; // Object to store metric scores, e.g., { “size_0”: 3, “mesh_0”: 5, … }

    let finalEvaluationScore = 0; // Store the calculated score

    // — Core Logic for Size & Space Calculation —

    function calculateSizeScoreAndUpdateUI() {

        const species = document.getElementById(‘bird-species’).value;

        const numBirds = parseInt(document.getElementById(‘num-birds’).value) || 0;

        const L_actual = parseInt(document.getElementById(‘cage-length’).value) || 0;

        const W_actual = parseInt(document.getElementById(‘cage-width’).value) || 0;

        const H_actual = parseInt(document.getElementById(‘cage-height’).value) || 0;

        const sizeScoreElement = document.getElementById(‘score-value-size_0’);

        const sizeInputHidden = document.getElementById(‘size_0’);

        const infoElement = document.getElementById(‘ideal-size-info’);

        infoElement.classList.add(‘hidden’); // Hide by default

        // 1. Initial Reset/Check for Missing Data

        if (!species || numBirds === 0 || L_actual === 0 || W_actual === 0 || H_actual === 0 || !BIRD_SPECIES_DATA[species]) {

            scores[‘size_0’] = 3; // Default score

            if (sizeScoreElement) sizeScoreElement.textContent = ‘3’;

            if (sizeInputHidden) sizeInputHidden.value = ‘3’;

            return;

        }

        const data = BIRD_SPECIES_DATA[species];

        let targetSize = null;

        let baseMultiplier = 1;

        // 2. Determine Target Base Size based on Bird Count

        if (numBirds === 1) {

            targetSize = data.single;

        } else if (numBirds === 2) {

            targetSize = data.pair;

        } else if (numBirds >= 3 && numBirds <= 4) {

            targetSize = data.four;

        } else if (numBirds >= 5 && numBirds <= 6) {

            targetSize = data.six;

        } else if (numBirds > 6) {

            // Scale volume for large flocks

            targetSize = data.six || data.four || data.pair || data.single;

            const sizeKey = data.six ? 6 : data.four ? 4 : data.pair ? 2 : 1;

            baseMultiplier = Math.ceil(numBirds / sizeKey);

        }

        

        // 3. Handle Aviary Preferred/Mandatory Scenarios

        if (targetSize === null) {

            scores[‘size_0’] = 3; // Max score for non-aviary setup when aviary is required

            if (sizeScoreElement) sizeScoreElement.textContent = ‘3’;

            if (sizeInputHidden) sizeInputHidden.value = ‘3’;

            

            infoElement.innerHTML = `For **${species}** with ${numBirds} birds, a standard cage is insufficient. **An aviary setup is strongly recommended or mandatory** for appropriate welfare. The score is capped at 3/5.`;

            infoElement.classList.remove(‘hidden’);

            return;

        }

        const [L_req, W_req, H_req] = targetSize;

        const vol_req = (L_req * W_req * H_req) * baseMultiplier;

        const vol_actual = L_actual * W_actual * H_actual;

        

        // 4. Update Info Box with Requirements

        let infoHtml = `<span class=”font-bold”>Required Bar Spacing:</span> ${data.bar_spacing}.`;

        if (baseMultiplier > 1) {

            infoHtml += `<br><span class=”font-bold”>Base Requirement (for ${data.six ? ‘6’ : data.four ? ‘4’ : ‘2’} birds):</span> ${L_req}” x ${W_req}” x ${H_req}” (L x W x H). Actual size is checked against a ${baseMultiplier}x volume multiplier.`;

        } else {

            infoHtml += `<br><span class=”font-bold”>Required Cage Size:</span> ${L_req}” x ${W_req}” x ${H_req}” (L x W x H).`;

        }

        infoElement.innerHTML = infoHtml;

        infoElement.classList.remove(‘hidden’);

        

        // 5. Scoring Logic based on Volume Ratio

        let score = 0;

        const volumeRatio = vol_actual / vol_req;

        

        if (volumeRatio >= 1.5) {

            score = 5; // 50%+ larger

        } else if (volumeRatio >= 1.25) {

            score = 4; // 25%+ larger

        } else if (volumeRatio >= 1.0) {

            score = 3; // Meets minimum volume

        } else if (volumeRatio >= 0.8) {

            score = 2; // Up to 20% too small

        } else {

            score = 1; // More than 20% too small (critical)

        }

        

        // 6. Horizontal Flight Penalty (if applicable)

        // Check for common design flaw: tall but narrow/short cages, penalize high scores

        if (H_actual > (L_actual * 1.5) && H_actual > (W_actual * 1.5) && score > 3) {

             // Penalize strong scores if height is disproportionately large, as horizontal flight is preferred.

             score = Math.max(3, score – 1); // Max score can drop to 4 (from 5) or 3 (from 4)

        }

        

        // Ensure score is clamped between 1 and 5

        score = Math.max(1, Math.min(5, score));

        

        // 7. Final UI Update

        scores[‘size_0’] = score;

        if (sizeScoreElement) sizeScoreElement.textContent = score.toString();

        if (sizeInputHidden) sizeInputHidden.value = score.toString();

    }

    // — DOM Generation —

    function generateForm() {

        const container = document.getElementById(‘form-container’);

        let html = ”;

        // — NEW SETUP DATA INPUT SECTION —

        const speciesOptions = Object.keys(BIRD_SPECIES_DATA).map(key => 

            `<option value=”${key}”>${key}</option>`

        ).join(”);

        

        html += `

            <section class=”bg-white p-6 rounded-xl shadow-lg mb-8 border-t-4 border-blue-500″>

                <h2 class=”text-2xl font-bold text-gray-800 mb-4″>0. Setup Data (MANDATORY)</h2>

                <p class=”text-sm text-gray-500 mb-6″>Input the bird species, the number of birds, and the actual cage dimensions (in inches) to automatically calculate the **Size & Space** score.</p>

                <div class=”grid grid-cols-1 md:grid-cols-2 gap-6″>

                    <div>

                        <label for=”bird-species” class=”block text-sm font-medium text-gray-700 mb-1″>Bird Species</label>

                        <select id=”bird-species” onchange=”calculateSizeScoreAndUpdateUI()” class=”w-full p-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition”>

                            <option value=””>– Select Species –</option>

                            ${speciesOptions}

                        </select>

                    </div>

                    <div>

                        <label for=”num-birds” class=”block text-sm font-medium text-gray-700 mb-1″>Number of Birds</label>

                        <input type=”number” id=”num-birds” placeholder=”e.g. 4″ min=”1″ value=”” oninput=”calculateSizeScoreAndUpdateUI()” class=”w-full p-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition”>

                    </div>

                </div>

                <div class=”mt-6″>

                    <label class=”block text-sm font-medium text-gray-700 mb-2″>Actual Cage Dimensions (L x W x H in inches)</label>

                    <div class=”grid grid-cols-3 gap-3″>

                        <input type=”number” id=”cage-length” placeholder=”Length” min=”1″ value=”” oninput=”calculateSizeScoreAndUpdateUI()” class=”p-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition”>

                        <input type=”number” id=”cage-width” placeholder=”Width” min=”1″ value=”” oninput=”calculateSizeScoreAndUpdateUI()” class=”p-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition”>

                        <input type=”number” id=”cage-height” placeholder=”Height” min=”1″ value=”” oninput=”calculateSizeScoreAndUpdateUI()” class=”p-2 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition”>

                    </div>

                </div>

                <div id=”ideal-size-info” class=”mt-4 p-3 bg-blue-50 rounded-lg text-sm text-blue-700 hidden”>

                    <!– Dynamic Ideal Size Info Here –>

                </div>

            </section>

        `;

        // — END NEW SETUP DATA INPUT SECTION —

        FRAMEWORK.forEach((section, sectionIndex) => {

            html += `

                <section class=”bg-white p-6 rounded-xl shadow-lg mb-8 ${section.color}”>

                    <h2 class=”text-2xl font-bold text-gray-800 mb-4″>${section.title} <span class=”text-base font-normal text-gray-500″>(${section.weight * 100}%)</span></h2>

                    <div class=”space-y-6″>

            `;

            

            section.metrics.forEach(metric => {

                const metricId = `${metric.id}_${sectionIndex}`;

                scores[metricId] = 3; // Initialize default score

                if (metric.id === ‘size’) {

                    // RENDER SPECIAL AUTO-CALCULATED SIZE METRIC

                    html += `

                        <div class=”border-b pb-4 last:border-b-0 last:pb-0 bg-blue-50 p-3 rounded-lg”>

                            <label class=”block text-lg font-bold text-gray-700 mb-1 flex justify-between items-center”>

                                ${metric.description}

                                <span id=”score-value-${metricId}” class=”text-2xl font-extrabold text-blue-600 w-8 text-center”>3</span>

                            </label>

                            <p class=”text-sm text-blue-700 mb-1″>Score is automatically calculated based on Setup Data.</p>

                            <p class=”text-xs text-gray-500″>${metric.guidelines}</p>

                            <!– Hidden input maintains the score data –>

                            <input type=”hidden” id=”${metricId}” value=”3″> 

                        </div>

                    `;

                } else {

                    // Render standard range slider for all other metrics

                    html += `

                        <div class=”border-b pb-4 last:border-b-0 last:pb-0″>

                            <label for=”${metricId}” class=”block text-lg font-medium text-gray-700 mb-1″>${metric.description}</label>

                            <p class=”text-sm text-gray-500 mb-3″>${metric.guidelines}</p>

                            

                            <div class=”flex items-center gap-4″>

                                <input type=”range” 

                                    id=”${metricId}” 

                                    min=”1″ max=”5″ value=”3″ 

                                    class=”flex-grow” 

                                    oninput=”updateScore(this, ‘${metricId}’)”>

                                <span id=”score-value-${metricId}” class=”text-2xl font-bold text-blue-600 w-8 text-center”>3</span>

                            </div>

                        </div>

                    `;

                }

            });

            html += `

                    </div>

                </section>

            `;

        });

        container.innerHTML = html;

        // Call initial update to ensure ‘size’ is calculated if default values are present

        calculateSizeScoreAndUpdateUI(); 

    }

    function updateScore(inputElement, metricId) {

        const scoreValueSpan = document.getElementById(`score-value-${metricId}`);

        scoreValueSpan.textContent = inputElement.value;

        scores[metricId] = parseInt(inputElement.value);

        

        // Update range color based on value (visual feedback)

        const value = parseInt(inputElement.value);

        let color = ‘#2563eb’; // Blue for average

        if (value <= 2) color = ‘#dc2626’; // Red for poor

        if (value >= 4) color = ‘#10b981’; // Green for good

        

        // Use an approximate fill for the range track

        const percentage = (value – 1) * 25;

        inputElement.style.background = `linear-gradient(to right, ${color} 0%, ${color} ${percentage}%, #d1d5db ${percentage}%, #d1d5db 100%)`;

        inputElement.style.setProperty(‘–tw-shadow-color’, color);

    }

    // — Submission Logic —

    function submitEvaluation() {

        // Ensure the Size score is calculated one last time before submission

        calculateSizeScoreAndUpdateUI(); 

        

        const score = calculateFinalScore();

        finalEvaluationScore = score;

        

        const interpretation = getInterpretation(score);

        

        // 1. Update Fixed Panel with results

        document.getElementById(‘final-score-display’).textContent = score;

        // Apply the dynamic color and keep the base classes

        document.getElementById(‘final-score-display’).className = `text-5xl font-extrabold score-display ${interpretation.color}`;

        

        document.getElementById(‘score-interpretation’).textContent = interpretation.title;

        document.getElementById(‘score-interpretation’).className = `text-xl font-semibold ${interpretation.color}`;

        // 2. Change Button to “View Results” and bind new function

        const submitButton = document.getElementById(‘submit-button’);

        submitButton.textContent = ‘View Recommendations & Flags’;

        submitButton.onclick = showRecommendations;

        submitButton.classList.remove(‘bg-blue-600’, ‘hover:bg-blue-700’);

        submitButton.classList.add(‘bg-green-600’, ‘hover:bg-green-700’);

        // 3. Populate Modal content and show it immediately

        updateModalContent(score);

        showRecommendations();

    }

    

    // Core calculation logic (now separate from UI updates)

    function calculateFinalScore() {

        let totalWeightedScore = 0;

        FRAMEWORK.forEach((section, sectionIndex) => {

            let sectionScoreSum = 0;

            const metricCount = section.metrics.length;

            section.metrics.forEach(metric => {

                const metricId = `${metric.id}_${sectionIndex}`;

                // Fallback to default 3 if a score is somehow missing, though it should be initialized.

                const score = scores[metricId] || 3; 

                sectionScoreSum += score;

            });

            // Normalized Section Score (0 to 1)

            const maxSectionScore = metricCount * 5;

            const normalizedSectionScore = sectionScoreSum / maxSectionScore;

            // Weighted contribution

            totalWeightedScore += normalizedSectionScore * section.weight;

        });

        return Math.round(totalWeightedScore * 100);

    }

    // — Results & Recommendations Logic —

    function getInterpretation(score) {

        if (score >= 90) return { title: “Excellent Setup”, level: “Gold Certified”, color: “text-yellow-300”, bg: “bg-yellow-100”, recommendation: “The setup is exemplary! You have achieved **Shinfly Gold Certification**. Maintain these exceptional high standards across all areas, and consider documenting best practices to share with other breeders.”, badgeColor: “bg-yellow-500”, badgeIcon: “🌟” };

        if (score >= 75) return { title: “Good Compliance”, level: “Silver Compliant”, color: “text-green-400”, bg: “bg-green-100”, recommendation: “The setup is compliant and high-quality, meeting the requirements for **Shinfly Silver Compliance**. Review any flagged items for minor adjustments that could lead to Gold Certification.”, badgeColor: “bg-gray-400”, badgeIcon: “✨” };

        if (score >= 60) return { title: “Moderate Standard”, level: “Needs Correction”, color: “text-orange-400”, bg: “bg-orange-100”, recommendation: “The setup meets basic requirements but has several areas needing improvement to ensure optimal bird health. Focus immediate attention on any flagged items, particularly those related to Hygiene and Safety.”, badgeColor: “bg-orange-500”, badgeIcon: “⚠️” };

        return { title: “Poor Standard”, level: “Immediate Action”, color: “text-red-500”, bg: “bg-red-100”, recommendation: “The current setup presents significant welfare risks. **Immediate, comprehensive review and correction** of all low-scoring areas is mandatory to protect the birds’ well-being.”, badgeColor: “bg-red-500”, badgeIcon: “🚨” };

    }

    function getFlaggedMetrics() {

        const flaggedMetrics = [];

        FRAMEWORK.forEach((section, sectionIndex) => {

            section.metrics.forEach(metric => {

                const metricId = `${metric.id}_${sectionIndex}`;

                const score = scores[metricId] || 3; 

                if (score <= 2) {

                    flaggedMetrics.push({

                        section: section.title,

                        metric: metric.description,

                        score: score

                    });

                }

            });

        });

        return flaggedMetrics;

    }

    function getDetailedMetricReport(metric, sectionIndex) {

        const metricId = `${metric.id}_${sectionIndex}`;

        const score = scores[metricId] || 3;

        const result = { html: ”, statusColor: ” };

        // Determine status and set color

        if (score >= 4) {

            result.statusColor = score === 5 ? ‘text-green-700’ : ‘text-lime-600’;

        } else if (score <= 2) {

            result.statusColor = score === 1 ? ‘text-red-700’ : ‘text-orange-600’;

        } else {

            result.statusColor = ‘text-blue-600’;

        }

        // Generate detailed report based on score

        const description = metric.description;

        const guidelines = metric.guidelines;

        const descriptionLower = description.toLowerCase();

        const sectionName = FRAMEWORK[sectionIndex].title.split(‘.’)[1].trim(); // e.g., “Cage Design & Dimensions”

        

        // — CUSTOM LOGIC FOR SIZE & SPACE —

        if (metric.id === ‘size’) {

            const species = document.getElementById(‘bird-species’).value;

            const numBirds = parseInt(document.getElementById(‘num-birds’).value) || 0;

            const L_actual = parseInt(document.getElementById(‘cage-length’).value) || 0;

            const W_actual = parseInt(document.getElementById(‘cage-width’).value) || 0;

            const H_actual = parseInt(document.getElementById(‘cage-height’).value) || 0;

            

            // Re-run key calculation data for the report text

            const data = BIRD_SPECIES_DATA[species];

            let required_text = ‘N/A’;

            let actual_volume = L_actual * W_actual * H_actual;

            let required_volume = 0;

            let targetSize = null;

            let baseMultiplier = 1;

            

            if (numBirds === 1) { targetSize = data?.single; } 

            else if (numBirds === 2) { targetSize = data?.pair; } 

            else if (numBirds >= 3 && numBirds <= 4) { targetSize = data?.four; } 

            else if (numBirds >= 5 && numBirds <= 6) { targetSize = data?.six; } 

            else if (numBirds > 6) { 

                targetSize = data?.six || data?.four || data?.pair || data?.single;

                const sizeKey = data?.six ? 6 : data?.four ? 4 : data?.pair ? 2 : 1;

                baseMultiplier = Math.ceil(numBirds / sizeKey);

            }

            if (targetSize) {

                const [L_req, W_req, H_req] = targetSize;

                required_volume = (L_req * W_req * H_req) * baseMultiplier;

                required_text = `${L_req}” L x ${W_req}” W x ${H_req}” H (Base)`;

            } else if (species && numBirds > 2) {

                 required_text = ‘Aviary or very large custom cage’;

                 required_volume = 0; // Prevent division by zero

            } else {

                required_text = ‘Missing input data.’;

            }

            const required_volume_ft3 = Math.round(required_volume / 1728); // 1728 in^3 in 1 ft^3

            const actual_volume_ft3 = Math.round(actual_volume / 1728);

            if (score === 5) {

                result.html = `

                    <p class=”font-bold”>Status: Excellent Oversize</p>

                    <p class=”text-gray-600 mt-1″>The cage size provides **ample space** (${actual_volume_ft3} ft³) for the **${numBirds} ${species}** and exceeds the minimum required volume (${required_volume_ft3} ft³ required base). This minimizes stress and encourages flight.</p>

                    <p class=”text-sm mt-1 text-gray-500″>**Recommendation:** Maintain this high standard. The extra space is a major strength.</p>

                `;

            } else if (score === 4) {

                result.html = `

                    <p class=”font-bold”>Status: Strong Adequacy</p>

                    <p class=”text-gray-600 mt-1″>The size (${actual_volume_ft3} ft³) is **highly adequate** for the ${numBirds} ${species} (${required_volume_ft3} ft³ required base). It is slightly above minimum requirements and provides good flight area.</p>

                    <p class=”text-sm mt-1 text-gray-500″>**How to Improve:** Consider increasing the length/width (horizontal space) if possible, as flight space is more critical than height for most species. Maximize usable horizontal perching space.</p>

                `;

            } else if (score === 3) {

                result.html = `

                    <p class=”font-bold”>Status: Meets Minimum Standard</p>

                    <p class=”text-gray-600 mt-1″>The cage dimensions (${actual_volume_ft3} ft³) **just meet the bare minimum** for the ${numBirds} ${species} (${required_volume_ft3} ft³ required base). While compliant, it limits the bird’s natural behaviors (e.g., full wing stretches). ${required_text === ‘Missing input data.’ ? ” : `Required base size: ${required_text}.`}</p>

                    <p class=”text-sm mt-1 text-gray-500″>**How to Improve:** This score (3/5) indicates potential **overcrowding during breeding or movement restriction**. Upgrade to a larger enclosure as soon as feasible. If upgrading is not possible, drastically increase out-of-cage supervised free-flight time.</p>

                `;

            } else if (score === 2) {

                result.html = `

                    <p class=”font-bold”>Status: Area for Concern (Minor Undersize)</p>

                    <p class=”text-red-700 font-medium mt-1″>**Why it’s low:** The cage (${actual_volume_ft3} ft³) is **under-sized** for the ${numBirds} ${species} (${required_volume_ft3} ft³ required base), being up to 20% smaller than the requirement. This will lead to **increased stress, feather plucking, or aggression**.</p>

                    <p class=”text-sm mt-1 text-gray-600″>**Immediate Action:** You must **reduce the number of birds immediately** or acquire a cage that meets at least the minimum volume requirement. Focus on the required horizontal dimensions.</p>

                `;

            } else if (score === 1) {

                result.html = `

                    <p class=”font-bold”>Status: CRITICAL FAILURE POINT (Severe Undersize)</p>

                    <p class=”text-red-700 font-extrabold mt-1″>**Critical Risk:** The cage (${actual_volume_ft3} ft³) is severely inadequate (more than 20% smaller) for the ${numBirds} ${species} (${required_volume_ft3} ft³ required base). This is a **major welfare violation** and indicates **severe overcrowding**.</p>

                    <p class=”text-sm mt-1 text-gray-600″>**Mandatory Correction:** **IMMEDIATELY** move the birds to a compliant cage or separate the flock into multiple cages. Failure to correct this poses an immediate health and safety risk. **The facility cannot pass inspection with this score.**</p>

                `;

            }

            return result;

        } 

        // — END CUSTOM LOGIC FOR SIZE & SPACE —

        // DEFAULT LOGIC for all other metrics (remains the same)

        if (score === 5) {

            result.html = `

                <p class=”font-bold”>Status: Best Practice</p>

                <p class=”text-gray-600 mt-1″>This attribute is **exemplary** within the ${sectionName} section. It indicates a consistent, high-quality approach to ${descriptionLower}.</p>

                <p class=”text-sm mt-1 text-gray-500″>**Recommendation:** Maintain this high benchmark. This is a point of strength for your setup!</p>

            `;

        } else if (score === 4) {

            result.html = `

                <p class=”font-bold”>Status: Strong Quality</p>

                <p class=”text-gray-600 mt-1″>You have a **strong foundation** in ${descriptionLower}. The setup is safe and effective, but minor proactive measures could achieve perfection (5/5).</p>

                <p class=”text-sm mt-1 text-gray-500″>**How to Improve:** Review the detailed guidelines (${guidelines}). Look for small optimizations, such as daily rotations of toys or double-checking sealing to prevent drafts, to reach the top tier.</p>

            `;

        } else if (score === 3) {

            result.html = `

                <p class=”font-bold”>Status: Meets Minimum Standard</p>

                <p class=”text-gray-600 mt-1″>The performance of **${description}** meets general minimum requirements. While not a risk, this average score limits potential welfare or operational efficiency.</p>

                <p class=”text-sm mt-1 text-gray-500″>**How to Improve:** Focus on consistency and quality control. Aim for a score of 4 by implementing established routines, such as scheduled deep cleaning or purchasing higher quality feed/equipment.</p>

            `;

        } else if (score === 2) {

            result.html = `

                <p class=”font-bold”>Status: Area for Concern</p>

                <p class=”text-red-700 font-medium mt-1″>**Why it’s low:** A score of 2 in **${description}** indicates a significant deficiency (e.g., poor mesh/unsafe perches, irregular cleaning, or limited diet) that could lead to **welfare issues or increased stress**.</p>

                <p class=”text-sm mt-1 text-gray-600″>**Immediate Action:** Address the cause of the deficiency immediately. For ‘Diet Diversity’, introduce fresh vegetables and supplements. For ‘Airflow’, identify and fix stale air pockets. **Aim to reach a score of 4.**</p>

            `;

        } else if (score === 1) {

            result.html = `

                <p class=”font-bold”>Status: CRITICAL FAILURE POINT</p>

                <p class=”text-red-700 font-extrabold mt-1″>**Critical Risk:** A score of 1 in **${description}** is a severe failure. This setup aspect is actively posing an immediate threat (e.g., severe overcrowding, toxic environment, or extreme temps). **Correction is mandatory.**</p>

                <p class=”text-sm mt-1 text-gray-600″>**Mandatory Correction:** You must take **immediate and decisive action** to resolve the issue. If it is ‘Size & Space’, immediately reduce bird density or acquire a larger enclosure. **Resolve this to a score of at least 3 within 24 hours.**</p>

            `;

        }

        return result;

    }

    

    function generateDetailedInsights() {

        let html = ”;

        FRAMEWORK.forEach((section, sectionIndex) => {

            let sectionHtml = ”;

            

            // Generate detailed report for each metric in the section

            section.metrics.forEach(metric => {

                const score = scores[`${metric.id}_${sectionIndex}`] || 3; 

                const report = getDetailedMetricReport(metric, sectionIndex);

                sectionHtml += `

                    <div class=”border-b last:border-b-0 pb-4 mb-4″>

                        <div class=”flex items-center justify-between”>

                            <h5 class=”text-lg font-semibold text-gray-800″>${metric.description}</h5>

                            <span class=”text-2xl font-extrabold ${report.statusColor}”>${score}/5</span>

                        </div>

                        <div class=”mt-2 text-sm”>

                            ${report.html}

                        </div>

                    </div>

                `;

            });

            // Wrap the section report

            html += `

                <div class=”p-4 bg-gray-50 rounded-xl shadow-inner ${section.color.replace(‘border-l-4’, ‘border-l-8’)}”>

                    <h3 class=”text-xl font-bold text-gray-800 mb-4″>${section.title}</h3>

                    <div class=”space-y-4″>

                        ${sectionHtml}

                    </div>

                </div>

            `;

        });

        return html;

    }

    

    function updateModalContent(score) {

        const interpretation = getInterpretation(score);

        const flaggedMetrics = getFlaggedMetrics();

        // Summary Section with Badge and Score

        document.getElementById(‘modal-summary’).innerHTML = `

            <div class=”flex items-center justify-between”>

                <h5 class=”text-xl font-bold text-gray-800″>Total Index Score: ${score}/100</h5>

                <span class=”text-sm font-bold text-white px-3 py-1 rounded-full ${interpretation.badgeColor} shadow-md”>

                    ${interpretation.badgeIcon} ${interpretation.level.toUpperCase()}

                </span>

            </div>

            <p class=”text-3xl font-extrabold mt-2 text-gray-800″>${interpretation.title}</p>

        `;

        document.getElementById(‘modal-summary’).className = `mb-6 p-4 rounded-xl ${interpretation.bg} border-b-4 border-gray-300`;

        // Flagged Items (High-priority summary)

        const flaggedList = document.getElementById(‘flagged-items’);

        flaggedList.innerHTML = ”;

        if (flaggedMetrics.length > 0) {

            flaggedMetrics.forEach(item => {

                flaggedList.innerHTML += `<li class=”p-3 bg-red-50 border border-red-200 rounded-lg”>

                    <span class=”font-bold text-red-700″>${item.section}: ${item.metric}</span>

                    <span class=”ml-2 text-sm text-red-600 font-medium”>(Score ${item.score})</span>

                </li>`;

            });

        } else {

            flaggedList.innerHTML = ‘<li class=”text-gray-700 bg-green-50 p-3 rounded-lg border border-green-200 font-medium”>No metrics scored 1 or 2. The foundation is excellent.</li>’;

        }

        

        document.getElementById(‘overall-recommendation-text’).innerHTML = interpretation.recommendation;

        

        // Detailed Insights Breakdown

        document.getElementById(‘detailed-insights’).innerHTML = generateDetailedInsights();

    }

    function showRecommendations() {

        // Use the stored finalEvaluationScore to populate the modal

        updateModalContent(finalEvaluationScore); 

        document.getElementById(‘recommendations-modal’).classList.remove(‘hidden’);

        document.body.style.overflow = ‘hidden’; // Prevent scrolling background

    }

    function hideRecommendations(event) {

        if (!event || event.target.id === ‘recommendations-modal’) {

            document.getElementById(‘recommendations-modal’).classList.add(‘hidden’);

            document.body.style.overflow = ”; // Restore scrolling

        }

    }

    // Initialize the application

    window.onload = generateForm;

</script>