ZIP/WAREHOUSE/biorhythms.php
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>바이오리듬 분석기</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            darkMode: 'media',
            theme: {
                extend: {
                    fontFamily: { sans: ['Noto Sans KR', 'sans-serif'] },
                    keyframes: {
                        fadeInUp: {
                            '0%': { opacity: '0', transform: 'translateY(20px)' },
                            '100%': { opacity: '1', transform: 'translateY(0)' },
                        }
                    },
                    animation: {
                        fadeInUp: 'fadeInUp 0.8s ease-out forwards',
                    }
                }
            }
        }
    </script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap');
        body { font-family: 'Noto Sans KR', sans-serif; transition: background-color 0.3s ease; }
        canvas { touch-action: none; transition: opacity 1s ease; }
        .stat-card { transition: all 0.3s ease; }
        .stat-card:hover { transform: translateY(-2px); }
    </style>
</head>
<body class="bg-slate-50 dark:bg-slate-950 min-h-screen flex flex-col items-center py-4 px-4 transition-colors duration-300">

    <!-- Header (Animated) -->
    <header class="mb-4 text-center opacity-0 animate-fadeInUp" style="animation-delay: 0.1s">
        <h1 class="text-2xl font-bold text-slate-800 dark:text-slate-100 mb-1">바이오리듬 분석기</h1>
        <p class="text-slate-500 dark:text-slate-400 text-xs">생년월일 기반 신체, 감성, 지성 주기 분석</p>
    </header>

    <!-- Main Card (Animated) -->
    <main class="bg-white dark:bg-slate-900 rounded-xl shadow-lg w-full max-w-2xl p-4 md:p-6 border border-slate-200 dark:border-slate-800 opacity-0 animate-fadeInUp" style="animation-delay: 0.3s">
        
        <!-- Controls -->
        <div class="grid grid-cols-2 gap-3 mb-6 p-3 bg-slate-50 dark:bg-slate-800/50 rounded-lg border border-slate-200 dark:border-slate-700">
            <div>
                <label class="block text-[10px] font-bold text-slate-500 dark:text-slate-400 uppercase mb-1">생년월일</label>
                <!-- 요청사항: 1972-03-02로 수정 -->
                <input type="date" id="birthDate" value="1972-03-02" 
                    class="w-full bg-white dark:bg-slate-800 border border-slate-300 dark:border-slate-600 text-slate-900 dark:text-slate-100 text-xs rounded-md p-1.5 focus:ring-2 focus:ring-blue-500 outline-none">
            </div>
            <div>
                <label class="block text-[10px] font-bold text-slate-500 dark:text-slate-400 uppercase mb-1">기준 날짜</label>
                <input type="date" id="targetDate" 
                    class="w-full bg-white dark:bg-slate-800 border border-slate-300 dark:border-slate-600 text-slate-900 dark:text-slate-100 text-xs rounded-md p-1.5 focus:ring-2 focus:ring-blue-500 outline-none">
            </div>
        </div>

        <!-- Legend & Stats (Dynamic Progress) -->
        <div class="grid grid-cols-3 gap-2 mb-4">
            <div class="stat-card bg-blue-50 dark:bg-blue-900/20 p-2 rounded-lg border-t-2 border-blue-500">
                <div class="flex justify-between items-baseline">
                    <span class="text-[10px] font-bold text-blue-700 dark:text-blue-400">신체</span>
                    <span id="val-p" class="text-sm font-bold text-blue-800 dark:text-blue-300">0%</span>
                </div>
                <div class="w-full bg-blue-200 dark:bg-blue-800 rounded-full h-1 mt-1 overflow-hidden">
                    <div id="bar-p" class="bg-blue-500 h-1 rounded-full transition-all duration-1000 ease-out" style="width: 50%"></div>
                </div>
            </div>

            <div class="stat-card bg-red-50 dark:bg-red-900/20 p-2 rounded-lg border-t-2 border-red-500">
                <div class="flex justify-between items-baseline">
                    <span class="text-[10px] font-bold text-red-700 dark:text-red-400">감성</span>
                    <span id="val-e" class="text-sm font-bold text-red-800 dark:text-red-300">0%</span>
                </div>
                <div class="w-full bg-red-200 dark:bg-red-800 rounded-full h-1 mt-1 overflow-hidden">
                    <div id="bar-e" class="bg-red-500 h-1 rounded-full transition-all duration-1000 ease-out" style="width: 50%"></div>
                </div>
            </div>

            <div class="stat-card bg-green-50 dark:bg-green-900/20 p-2 rounded-lg border-t-2 border-green-500">
                <div class="flex justify-between items-baseline">
                    <span class="text-[10px] font-bold text-green-700 dark:text-green-400">지성</span>
                    <span id="val-i" class="text-sm font-bold text-green-800 dark:text-green-300">0%</span>
                </div>
                <div class="w-full bg-green-200 dark:bg-green-800 rounded-full h-1 mt-1 overflow-hidden">
                    <div id="bar-i" class="bg-green-500 h-1 rounded-full transition-all duration-1000 ease-out" style="width: 50%"></div>
                </div>
            </div>
        </div>

        <!-- Canvas Container -->
        <div class="relative w-full overflow-hidden bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-lg mb-4">
            <canvas id="bioChart" class="w-full h-48 md:h-56 cursor-crosshair opacity-0"></canvas>
            <div id="chart-overlay" class="absolute top-1 right-2 text-[10px] text-slate-400 pointer-events-none">
                30일 간의 변화 추이
            </div>
        </div>

        <!-- Interpretation -->
        <div class="bg-slate-50 dark:bg-slate-800/50 rounded-lg p-3 border border-slate-200 dark:border-slate-700">
            <h3 class="font-bold text-slate-800 dark:text-slate-200 text-xs mb-1 flex items-center">
                <svg class="w-3.5 h-3.5 mr-1 text-yellow-500 animate-pulse" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
                오늘의 해석
            </h3>
            <p id="analysis-text" class="text-slate-600 dark:text-slate-400 text-[11px] leading-relaxed">
                데이터를 분석 중입니다...
            </p>
        </div>

    </main>

    <script>
        const PI = Math.PI;
        const CYCLE_P = 23;
        const CYCLE_E = 28;
        const CYCLE_I = 33;

        const birthInput = document.getElementById('birthDate');
        const targetInput = document.getElementById('targetDate');
        const canvas = document.getElementById('bioChart');
        const ctx = canvas.getContext('2d');
        
        const elValP = document.getElementById('val-p');
        const elValE = document.getElementById('val-e');
        const elValI = document.getElementById('val-i');
        const elBarP = document.getElementById('bar-p');
        const elBarE = document.getElementById('bar-e');
        const elBarI = document.getElementById('bar-i');
        const analysisText = document.getElementById('analysis-text');

        const today = new Date();
        targetInput.value = today.toISOString().split('T')[0];

        function resizeCanvas() {
            const container = canvas.parentElement;
            canvas.width = container.clientWidth;
            canvas.height = container.clientHeight;
            draw();
            canvas.style.opacity = '1'; // 로딩 시 서서히 나타남
        }

        window.addEventListener('resize', resizeCanvas);
        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', draw);

        function getBiorhythm(born, target) {
            const oneDay = 24 * 60 * 60 * 1000;
            const diffDays = (target.setHours(12,0,0,0) - born.setHours(12,0,0,0)) / oneDay;
            return {
                p: Math.sin((2 * PI * diffDays) / CYCLE_P) * 100,
                e: Math.sin((2 * PI * diffDays) / CYCLE_E) * 100,
                i: Math.sin((2 * PI * diffDays) / CYCLE_I) * 100
            };
        }

        function draw() {
            const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
            const birthDate = new Date(birthInput.value);
            const targetDateStr = targetInput.value;
            if (!targetDateStr || isNaN(birthDate.getTime())) return;
            const targetDate = new Date(targetDateStr);
            
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            const range = 15; 
            const width = canvas.width;
            const height = canvas.height;
            const midY = height / 2;

            const colorGrid = isDark ? '#334155' : '#e2e8f0';
            const colorZero = isDark ? '#64748b' : '#94a3b8';

            ctx.beginPath();
            ctx.strokeStyle = colorZero;
            ctx.lineWidth = 1;
            ctx.moveTo(0, midY);
            ctx.lineTo(width, midY);
            ctx.stroke();

            ctx.beginPath();
            ctx.strokeStyle = colorGrid;
            ctx.setLineDash([4, 4]);
            ctx.moveTo(width / 2, 0);
            ctx.lineTo(width / 2, height);
            ctx.stroke();
            ctx.setLineDash([]);

            function drawWave(cycle, color) {
                ctx.beginPath();
                ctx.strokeStyle = color;
                ctx.lineWidth = 2.5;
                ctx.lineCap = 'round';
                ctx.lineJoin = 'round';
                for (let xPixel = 0; xPixel <= width; xPixel += 2) {
                    const dayOffset = (xPixel / width) * (range * 2 + 1) - range - 0.5;
                    const oneDay = 24 * 60 * 60 * 1000;
                    const centerDiff = (targetDate - birthDate) / oneDay;
                    const currentDiff = centerDiff + dayOffset;
                    const val = Math.sin((2 * PI * currentDiff) / cycle);
                    const y = midY - (val * (height / 2 - 25));
                    if (xPixel === 0) ctx.moveTo(xPixel, y);
                    else ctx.lineTo(xPixel, y);
                }
                ctx.stroke();
            }

            drawWave(CYCLE_P, '#3b82f6'); 
            drawWave(CYCLE_E, '#ef4444'); 
            drawWave(CYCLE_I, '#22c55e'); 

            updateUI(getBiorhythm(birthDate, new Date(targetDate)));
        }

        // 동적 카운팅 애니메이션 함수
        function animateValue(obj, start, end, duration, isPercent = true) {
            let startTimestamp = null;
            const step = (timestamp) => {
                if (!startTimestamp) startTimestamp = timestamp;
                const progress = Math.min((timestamp - startTimestamp) / duration, 1);
                const currentVal = Math.floor(progress * (end - start) + start);
                obj.innerText = isPercent ? `${currentVal}%` : currentVal;
                if (progress < 1) {
                    window.requestAnimationFrame(step);
                }
            };
            window.requestAnimationFrame(step);
        }

        function updateUI(stats) {
            const getWidth = (val) => `${(val + 100) / 2}%`;

            // 값 애니메이션 (0에서 목표값까지)
            animateValue(elValP, 0, Math.round(stats.p), 1000);
            animateValue(elValE, 0, Math.round(stats.e), 1000);
            animateValue(elValI, 0, Math.round(stats.i), 1000);

            // 프로그레스 바 애니메이션
            elBarP.style.width = getWidth(stats.p);
            elBarE.style.width = getWidth(stats.e);
            elBarI.style.width = getWidth(stats.i);

            let comments = [];
            const isCritical = (val) => Math.abs(val) < 10;
            const isHigh = (val) => val > 75;

            if (isCritical(stats.p)) comments.push("⚠️ <b>신체 리듬</b>이 전환기에 있습니다. 충분한 휴식이 필요합니다.");
            else if (isHigh(stats.p)) comments.push("💪 <b>신체 리듬</b>이 매우 좋아 활동적인 일에 적합합니다.");
            
            if (isCritical(stats.e)) comments.push("⚠️ <b>감성 리듬</b>이 교차점에 있어 감정 변화에 유의하세요.");
            else if (isHigh(stats.e)) comments.push("🥰 <b>감성 리듬</b>이 높아 대인 관계가 원만할 것입니다.");

            if (isHigh(stats.i)) comments.push("🧠 <b>지성 리듬</b>이 정점입니다. 복잡한 업무나 공부에 최적입니다.");

            if (comments.length === 0) comments.push("전반적으로 안정적인 리듬입니다. 평소대로 활동하세요.");
            analysisText.innerHTML = comments.slice(0, 2).join("<br>");
        }

        birthInput.addEventListener('change', draw);
        targetInput.addEventListener('change', draw);
        
        // 초기 로딩 애니메이션 실행
        window.addEventListener('load', () => {
            setTimeout(resizeCanvas, 100);
        });
    </script>
</body>
</html>