'Capricorn', 'symbol' => '♑', 'element' => 'Earth', 'emoji' => '🐐', 'from' => [12, 22], 'to' => [1, 19], 'traits' => ['Ambitious', 'Disciplined', 'Patient', 'Practical']], ['name' => 'Aquarius', 'symbol' => '♒', 'element' => 'Air', 'emoji' => '🏺', 'from' => [1, 20], 'to' => [2, 18], 'traits' => ['Independent', 'Progressive', 'Humanitarian', 'Inventive']], ['name' => 'Pisces', 'symbol' => '♓', 'element' => 'Water', 'emoji' => '🐟', 'from' => [2, 19], 'to' => [3, 20], 'traits' => ['Empathetic', 'Creative', 'Intuitive', 'Gentle']], ['name' => 'Aries', 'symbol' => '♈', 'element' => 'Fire', 'emoji' => '🐏', 'from' => [3, 21], 'to' => [4, 19], 'traits' => ['Courageous', 'Energetic', 'Enthusiastic', 'Bold']], ['name' => 'Taurus', 'symbol' => '♉', 'element' => 'Earth', 'emoji' => '🐂', 'from' => [4, 20], 'to' => [5, 20], 'traits' => ['Reliable', 'Patient', 'Devoted', 'Sensual']], ['name' => 'Gemini', 'symbol' => '♊', 'element' => 'Air', 'emoji' => '👥', 'from' => [5, 21], 'to' => [6, 20], 'traits' => ['Adaptable', 'Curious', 'Witty', 'Expressive']], ['name' => 'Cancer', 'symbol' => '♋', 'element' => 'Water', 'emoji' => '🦀', 'from' => [6, 21], 'to' => [7, 22], 'traits' => ['Caring', 'Protective', 'Intuitive', 'Emotional']], ['name' => 'Leo', 'symbol' => '♌', 'element' => 'Fire', 'emoji' => '🦁', 'from' => [7, 23], 'to' => [8, 22], 'traits' => ['Confident', 'Dramatic', 'Creative', 'Generous']], ['name' => 'Virgo', 'symbol' => '♍', 'element' => 'Earth', 'emoji' => '🌾', 'from' => [8, 23], 'to' => [9, 22], 'traits' => ['Analytical', 'Loyal', 'Practical', 'Diligent']], ['name' => 'Libra', 'symbol' => '♎', 'element' => 'Air', 'emoji' => '⚖️', 'from' => [9, 23], 'to' => [10, 22], 'traits' => ['Diplomatic', 'Gracious', 'Fair-minded', 'Social']], ['name' => 'Scorpio', 'symbol' => '♏', 'element' => 'Water', 'emoji' => '🦂', 'from' => [10, 23], 'to' => [11, 21], 'traits' => ['Passionate', 'Stubborn', 'Resourceful', 'Brave']], ['name' => 'Sagittarius','symbol' => '♐', 'element' => 'Fire', 'emoji' => '🏹', 'from' => [11, 22], 'to' => [12, 21], 'traits' => ['Optimistic', 'Adventurous', 'Honest', 'Philosophical']], ]; private static array $elementCompatibility = [ 'Fire' => ['Fire' => 85, 'Air' => 80, 'Earth' => 45, 'Water' => 40], 'Earth' => ['Earth' => 88, 'Water' => 82, 'Fire' => 45, 'Air' => 50], 'Air' => ['Air' => 84, 'Fire' => 80, 'Water' => 48, 'Earth' => 50], 'Water' => ['Water' => 86, 'Earth' => 82, 'Fire' => 40, 'Air' => 48], ]; public static function getSign(?string $birthday): ?array { if (! $birthday) return null; try { $date = new \DateTime($birthday); } catch (\Exception) { return null; } $month = (int) $date->format('n'); $day = (int) $date->format('j'); foreach (self::$signs as $sign) { [$fm, $fd] = $sign['from']; [$tm, $td] = $sign['to']; if ($fm > $tm) { // Capricorn wraps year if (($month === $fm && $day >= $fd) || ($month === $tm && $day <= $td)) { return $sign; } } else { if (($month === $fm && $day >= $fd) || ($month > $fm && $month < $tm) || ($month === $tm && $day <= $td)) { return $sign; } } } return null; } public static function compatibility(?array $sign1, ?array $sign2): ?int { if (! $sign1 || ! $sign2) return null; $base = self::$elementCompatibility[$sign1['element']][$sign2['element']] ?? 50; // Same sign bonus if ($sign1['name'] === $sign2['name']) { $base = min(98, $base + 8); } // Deterministic jitter so different sign pairs within same element feel unique $seed = crc32($sign1['name'] . ':' . $sign2['name']); $jitter = (abs($seed) % 11) - 5; return max(20, min(99, $base + $jitter)); } public static function elementColor(string $element): string { return match ($element) { 'Fire' => '#ff6b35', 'Earth' => '#6abf69', 'Air' => '#64b5f6', 'Water' => '#4fc3f7', default => '#aaa', }; } }