"""
SURYA — A Mathematical Journey Through Feeling
Das's 14-track emotional odyssey rendered in mathematical beauty

Created with Manim Community Edition
"""

from manim import *
import numpy as np
from scipy.interpolate import interp1d
import random

# ============================================================================
# CONFIGURATION
# ============================================================================

config.background_color = "#030305"

# Color palette matching the SURYA website aesthetic
class SuryaColors:
    # Track colors
    INTRO = "#a78bfa"       # Purple
    SKIN = "#f472b6"        # Pink
    SAVED = "#22d3ee"       # Cyan
    NOTHING = "#666666"     # Grey
    RELIEF = "#a78bfa"      # Purple
    NATURE = "#5eead4"      # Teal
    DREAM = "#c4b5fd"       # Soft purple
    IDK = "#f472b6"         # Dark pink
    WITH_U = "#fbbf24"      # Golden (THE TURN)
    POOR = "#fb923c"        # Orange
    WAIT = "#a78bfa"        # Purple longing
    RUN = "#22d3ee"         # Cyan urgent
    MEDS = "#ef4444"        # Red
    HOLLOW = "#fbbf24"      # Golden resolution
    
    # Supporting colors
    DARK_BG = "#030305"
    SOUL_WHITE = "#ffffff"
    GLOW_SOFT = "#ffffff40"


# ============================================================================
# CUSTOM MATHEMATICAL OBJECTS
# ============================================================================

class SoulOrb(VGroup):
    """The persistent soul orb - heart of the journey"""
    def __init__(self, color=WHITE, radius=0.3, glow_radius=1.5, **kwargs):
        super().__init__(**kwargs)
        
        # Core orb
        self.core = Dot(radius=radius, color=color)
        self.core.set_fill(color, opacity=1)
        
        # Glow layers (gaussian-like falloff)
        self.glow_layers = VGroup()
        for i in range(8):
            factor = (i + 1) / 8
            glow = Circle(radius=radius + glow_radius * factor)
            opacity = 0.3 * (1 - factor) ** 2
            glow.set_stroke(color, width=2, opacity=opacity)
            glow.set_fill(color, opacity=opacity * 0.3)
            self.glow_layers.add(glow)
        
        # Pulsing ring
        self.ring = Circle(radius=radius * 2)
        self.ring.set_stroke(color, width=1, opacity=0.5)
        
        self.add(self.glow_layers, self.core, self.ring)
        self.color = color
    
    def pulse(self, scale_factor=1.3, run_time=1):
        return AnimationGroup(
            self.ring.animate.scale(scale_factor).set_opacity(0),
            rate_func=smooth
        )


class Spirograph(VMobject):
    """Parametric spirograph curves - mathematical beauty"""
    def __init__(self, R=3, r=1, d=1.5, color=WHITE, num_points=1000, **kwargs):
        super().__init__(**kwargs)
        self.R, self.r, self.d = R, r, d
        
        t = np.linspace(0, 2 * np.pi * self._lcm_period(), num_points)
        
        # Hypotrochoid equations
        x = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
        y = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)
        
        points = [np.array([x[i], y[i], 0]) for i in range(len(t))]
        self.set_points_smoothly(points)
        self.set_stroke(color, width=2, opacity=0.8)
    
    def _lcm_period(self):
        from math import gcd
        r_int, R_int = int(self.r * 100), int(self.R * 100)
        return r_int // gcd(r_int, R_int)


class LissajousCurve(VMobject):
    """Lissajous figures - chaos and confusion"""
    def __init__(self, a=3, b=4, delta=np.pi/2, color=WHITE, **kwargs):
        super().__init__(**kwargs)
        
        t = np.linspace(0, 2 * np.pi, 1000)
        x = 2 * np.sin(a * t + delta)
        y = 2 * np.sin(b * t)
        
        points = [np.array([x[i], y[i], 0]) for i in range(len(t))]
        self.set_points_smoothly(points)
        self.set_stroke(color, width=2)


class FlowerOfLife(VGroup):
    """Sacred geometry - spiritual moments"""
    def __init__(self, rings=2, radius=0.5, color=WHITE, **kwargs):
        super().__init__(**kwargs)
        
        centers = [ORIGIN]
        for ring in range(1, rings + 1):
            for i in range(6 * ring):
                angle = i * TAU / (6 * ring)
                dist = radius * ring * np.sqrt(3)
                centers.append(np.array([
                    dist * np.cos(angle),
                    dist * np.sin(angle),
                    0
                ]))
        
        for center in centers[:19]:  # Classic 19 circles
            circle = Circle(radius=radius, color=color)
            circle.move_to(center)
            circle.set_stroke(color, width=1.5, opacity=0.6)
            self.add(circle)


class GoldenSpiral(VMobject):
    """Golden ratio spiral - resolution and beauty"""
    def __init__(self, turns=4, color=GOLD, **kwargs):
        super().__init__(**kwargs)
        
        phi = (1 + np.sqrt(5)) / 2  # Golden ratio
        t = np.linspace(0, turns * 2 * np.pi, 500)
        
        # Logarithmic spiral with golden ratio
        r = 0.1 * phi ** (t / (2 * np.pi))
        x = r * np.cos(t)
        y = r * np.sin(t)
        
        points = [np.array([x[i], y[i], 0]) for i in range(len(t))]
        self.set_points_smoothly(points)
        self.set_stroke(color, width=3)


class FractalTree(VGroup):
    """Branching fractals - nature and growth"""
    def __init__(self, depth=6, length=2, angle=25, color=GREEN, **kwargs):
        super().__init__(**kwargs)
        self._build_tree(ORIGIN, UP * length, depth, length, angle, color)
    
    def _build_tree(self, start, direction, depth, length, angle, color):
        if depth == 0:
            return
        
        end = start + direction
        branch = Line(start, end, color=color)
        branch.set_stroke(width=depth * 0.8, opacity=0.3 + 0.1 * depth)
        self.add(branch)
        
        # Recursive branches
        new_length = length * 0.7
        left_dir = rotate_vector(direction, angle * DEGREES) * 0.7
        right_dir = rotate_vector(direction, -angle * DEGREES) * 0.7
        
        self._build_tree(end, left_dir, depth - 1, new_length, angle, color)
        self._build_tree(end, right_dir, depth - 1, new_length, angle, color)


class VoronoiFragments(VGroup):
    """Voronoi diagram - fragmentation and emptiness"""
    def __init__(self, num_points=30, color=WHITE, **kwargs):
        super().__init__(**kwargs)
        
        # Generate random points
        points = np.random.uniform(-4, 4, (num_points, 2))
        
        # Create approximate Voronoi cells using circles
        for point in points:
            cell = Dot(point=[point[0], point[1], 0], radius=0.05, color=color)
            cell.set_fill(color, opacity=0.3)
            self.add(cell)
            
            # Add connecting lines
            for other in points:
                if np.linalg.norm(point - other) < 2 and not np.array_equal(point, other):
                    mid = (point + other) / 2
                    angle = np.arctan2(other[1] - point[1], other[0] - point[0])
                    line = Line(
                        [mid[0] - np.sin(angle), mid[1] + np.cos(angle), 0],
                        [mid[0] + np.sin(angle), mid[1] - np.cos(angle), 0],
                        color=color
                    )
                    line.set_stroke(width=0.5, opacity=0.2)
                    self.add(line)


class ParticleField(VGroup):
    """Particles following mathematical fields"""
    def __init__(self, num_particles=100, color=WHITE, **kwargs):
        super().__init__(**kwargs)
        
        for _ in range(num_particles):
            x = random.uniform(-7, 7)
            y = random.uniform(-4, 4)
            size = random.uniform(0.02, 0.06)
            opacity = random.uniform(0.2, 0.6)
            
            particle = Dot(point=[x, y, 0], radius=size, color=color)
            particle.set_fill(color, opacity=opacity)
            self.add(particle)


class StrangeAttractor(VMobject):
    """Lorenz-like strange attractor for chaos moments"""
    def __init__(self, color=RED, **kwargs):
        super().__init__(**kwargs)
        
        # Simplified attractor visualization
        sigma, rho, beta = 10, 28, 8/3
        dt = 0.01
        x, y, z = 0.1, 0, 0
        
        points = []
        for _ in range(3000):
            dx = sigma * (y - x) * dt
            dy = (x * (rho - z) - y) * dt
            dz = (x * y - beta * z) * dt
            x, y, z = x + dx, y + dy, z + dz
            points.append(np.array([x * 0.1, (z - 25) * 0.1, 0]))
        
        self.set_points_smoothly(points)
        self.set_stroke(color, width=1, opacity=0.7)


# ============================================================================
# TRACK ANIMATIONS
# ============================================================================

class Track01_Skin(Scene):
    """Alienation - "don't fit in my own skin" """
    def construct(self):
        # Create distorted soul trying to find its shape
        soul = SoulOrb(color=SuryaColors.SKIN, radius=0.3)
        self.play(FadeIn(soul, scale=0.5), run_time=2)
        
        # Morphing spirograph representing identity struggle
        spiro1 = Spirograph(R=3, r=0.7, d=1.2, color=SuryaColors.SKIN)
        spiro2 = Spirograph(R=3, r=1.3, d=0.8, color=SuryaColors.SKIN)
        spiro1.scale(0.8)
        spiro2.scale(0.8)
        
        self.play(Create(spiro1), run_time=3)
        
        # Soul pulsates uncomfortably
        self.play(
            soul.animate.scale(0.7),
            spiro1.animate.set_opacity(0.5),
            run_time=1
        )
        self.play(
            soul.animate.scale(1.3),
            run_time=1
        )
        
        # Transform to different identity
        self.play(
            Transform(spiro1, spiro2),
            soul.animate.scale(0.8),
            run_time=4
        )
        
        # Particles of self floating away
        particles = ParticleField(50, color=SuryaColors.SKIN)
        self.play(FadeIn(particles, lag_ratio=0.1), run_time=2)
        
        # Everything fades with lingering unease
        self.play(
            FadeOut(spiro1),
            FadeOut(particles),
            soul.animate.scale(0.5).set_opacity(0.3),
            run_time=3
        )
        self.wait(1)


class Track02_USavedMe(Scene):
    """Hope emerges - "you saved me from my broken soul" """
    def construct(self):
        # Broken soul fragments
        soul = SoulOrb(color=SuryaColors.SAVED, radius=0.2)
        soul.set_opacity(0.5)
        
        # Fragments scattered
        fragments = VGroup(*[
            Dot(point=[random.uniform(-2, 2), random.uniform(-2, 2), 0], 
                radius=0.05, color=SuryaColors.SAVED)
            for _ in range(20)
        ])
        
        self.play(FadeIn(fragments, lag_ratio=0.1), run_time=2)
        
        # Fragments begin to coalesce
        self.play(
            *[f.animate.move_to(ORIGIN) for f in fragments],
            run_time=3,
            rate_func=smooth
        )
        
        # Soul emerges whole
        self.play(
            FadeOut(fragments),
            FadeIn(soul, scale=2),
            run_time=2
        )
        
        # Hopeful spirograph expands outward
        spiro = Spirograph(R=4, r=1.5, d=2, color=SuryaColors.SAVED)
        spiro.scale(0)
        self.add(spiro)
        
        self.play(
            spiro.animate.scale(0.7),
            soul.animate.scale(1.5).set_opacity(1),
            run_time=4
        )
        
        # Sacred geometry appears - spiritual awakening
        flower = FlowerOfLife(rings=2, radius=0.6, color=SuryaColors.SAVED)
        flower.set_opacity(0)
        self.add(flower)
        
        self.play(
            flower.animate.set_opacity(0.4),
            spiro.animate.set_opacity(0.3),
            run_time=3
        )
        
        # Gentle pulsing in harmony
        self.play(
            soul.animate.scale(1.2),
            flower.animate.scale(1.1),
            run_time=1.5
        )
        self.play(
            soul.animate.scale(1/1.2),
            flower.animate.scale(1/1.1),
            run_time=1.5
        )
        
        self.play(
            FadeOut(spiro, flower),
            soul.animate.scale(0.6),
            run_time=2
        )


class Track03_Nothing(Scene):
    """Emptiness - "I lost my heart, now I feel nothing" """
    def construct(self):
        # Soul starts visible but will fade
        soul = SoulOrb(color=SuryaColors.NOTHING, radius=0.3)
        self.add(soul)
        
        # Voronoi fragmentation - self breaking apart
        voronoi = VoronoiFragments(num_points=25, color=SuryaColors.NOTHING)
        
        # Soul slowly dims
        self.play(
            soul.animate.scale(0.7).set_opacity(0.5),
            run_time=3
        )
        
        # Fragments appear
        self.play(Create(voronoi), run_time=4)
        
        # Everything becomes grey and distant
        grey_circle = Circle(radius=3, color=SuryaColors.NOTHING)
        grey_circle.set_stroke(width=1, opacity=0.2)
        grey_circle.set_fill(SuryaColors.NOTHING, opacity=0.05)
        
        self.play(
            FadeIn(grey_circle),
            soul.animate.scale(0.3).set_opacity(0.2),
            voronoi.animate.set_opacity(0.1),
            run_time=4
        )
        
        # Hollow pulsing - mechanical, lifeless
        for _ in range(2):
            self.play(
                grey_circle.animate.scale(1.1),
                run_time=1,
                rate_func=there_and_back
            )
        
        # Static emptiness
        self.wait(2)
        
        # Fragments scatter into void
        self.play(
            FadeOut(voronoi, shift=UP * 2),
            FadeOut(grey_circle),
            soul.animate.set_opacity(0.1),
            run_time=3
        )


class Track04_SweetRelief(Scene):
    """Haunted - "seeing ghosts around my throat" """
    def construct(self):
        soul = SoulOrb(color=SuryaColors.RELIEF, radius=0.25)
        self.add(soul)
        
        # Ghost-like spiraling forms
        ghosts = VGroup()
        for i in range(5):
            ghost = Spirograph(R=2 + i * 0.3, r=0.5, d=1 + i * 0.2, 
                              color=SuryaColors.RELIEF)
            ghost.scale(0.5)
            ghost.set_opacity(0.1 + i * 0.05)
            ghost.rotate(i * PI / 5)
            ghosts.add(ghost)
        
        self.play(
            *[Create(g) for g in ghosts],
            run_time=4,
            lag_ratio=0.3
        )
        
        # Ghosts circle the soul menacingly
        self.play(
            Rotate(ghosts, PI, about_point=ORIGIN),
            soul.animate.scale(0.8),
            run_time=4
        )
        
        # Soul struggles
        self.play(
            soul.animate.shift(UP * 0.3),
            run_time=0.5
        )
        self.play(
            soul.animate.shift(DOWN * 0.3),
            run_time=0.5
        )
        
        # Ghosts close in
        self.play(
            ghosts.animate.scale(0.7),
            soul.animate.set_opacity(0.6),
            run_time=3
        )
        
        # Brief moment of relief (ghosts recede slightly)
        self.play(
            ghosts.animate.scale(1.3).set_opacity(0.1),
            soul.animate.scale(1.2).set_opacity(1),
            run_time=2
        )
        
        self.play(
            FadeOut(ghosts),
            run_time=2
        )


class Track05_Tiptoe(Scene):
    """Tense, cautious movement"""
    def construct(self):
        soul = SoulOrb(color=SuryaColors.RELIEF, radius=0.2)
        soul.shift(LEFT * 4)
        self.add(soul)
        
        # Careful path - Lissajous representing caution
        path = LissajousCurve(a=3, b=2, delta=PI/4, color=SuryaColors.RELIEF)
        path.set_opacity(0.3)
        
        self.play(Create(path), run_time=2)
        
        # Soul moves carefully along path
        # Small, hesitant steps
        positions = [LEFT * 3, LEFT * 2, LEFT * 1, ORIGIN, RIGHT * 1, RIGHT * 2, RIGHT * 3]
        
        for pos in positions:
            self.play(
                soul.animate.move_to(pos),
                run_time=0.8,
                rate_func=smooth
            )
            # Pause - checking surroundings
            self.wait(0.3)
        
        # Tension builds - path becomes more complex
        path2 = LissajousCurve(a=5, b=4, delta=PI/3, color=SuryaColors.RELIEF)
        path2.set_opacity(0.2)
        
        self.play(
            Transform(path, path2),
            soul.animate.move_to(ORIGIN),
            run_time=3
        )
        
        # Soul retreats to center, bracing
        self.play(
            soul.animate.scale(0.7),
            path.animate.set_opacity(0.1),
            run_time=2
        )
        
        self.play(FadeOut(path), run_time=1)


class Track06_NaturesCall(Scene):
    """Peaceful interlude - "thank you for joining us" """
    def construct(self):
        soul = SoulOrb(color=SuryaColors.NATURE, radius=0.3)
        self.play(FadeIn(soul), run_time=2)
        
        # Fractal tree grows - nature emerging
        tree = FractalTree(depth=6, length=1.5, color=SuryaColors.NATURE)
        tree.shift(DOWN * 2)
        tree.set_opacity(0)
        
        self.add(tree)
        self.play(
            tree.animate.set_opacity(0.6),
            run_time=5
        )
        
        # Particles like leaves/pollen floating up
        particles = VGroup()
        for _ in range(50):
            p = Dot(
                point=[random.uniform(-4, 4), random.uniform(-3, -2), 0],
                radius=random.uniform(0.02, 0.05),
                color=SuryaColors.NATURE
            )
            p.set_opacity(random.uniform(0.3, 0.7))
            particles.add(p)
        
        self.play(FadeIn(particles, lag_ratio=0.05), run_time=2)
        
        # Particles float upward peacefully
        self.play(
            *[p.animate.shift(UP * random.uniform(4, 6)) for p in particles],
            run_time=6,
            rate_func=smooth
        )
        
        # Golden ratio spiral emerges - nature's mathematics
        spiral = GoldenSpiral(turns=3, color=SuryaColors.NATURE)
        spiral.scale(0.3)
        spiral.set_opacity(0)
        
        self.play(
            spiral.animate.set_opacity(0.5).scale(2),
            soul.animate.scale(1.3),
            run_time=4
        )
        
        # Peaceful conclusion
        self.play(
            tree.animate.set_opacity(0.2),
            spiral.animate.set_opacity(0.2),
            run_time=3
        )
        
        self.play(
            FadeOut(tree, spiral, particles),
            run_time=2
        )


class Track07_Dreamcatcher(Scene):
    """Falling - "falling through the cracks" """
    def construct(self):
        soul = SoulOrb(color=SuryaColors.DREAM, radius=0.3)
        soul.shift(UP * 3)
        self.play(FadeIn(soul), run_time=1)
        
        # Dreamcatcher web - sacred geometry
        web = VGroup()
        for i in range(6):
            angle = i * PI / 3
            line = Line(ORIGIN, 3 * np.array([np.cos(angle), np.sin(angle), 0]))
            line.set_stroke(SuryaColors.DREAM, width=1, opacity=0.4)
            web.add(line)
        
        for r in [0.5, 1, 1.5, 2, 2.5]:
            circle = Circle(radius=r, color=SuryaColors.DREAM)
            circle.set_stroke(width=1, opacity=0.3)
            web.add(circle)
        
        self.play(Create(web), run_time=3)
        
        # Soul begins to fall through the web
        self.play(
            soul.animate.shift(DOWN * 2),
            run_time=2,
            rate_func=rate_functions.ease_in_quad
        )
        
        # Passing through layers - web pulses
        for _ in range(3):
            self.play(
                web.animate.scale(1.1).set_opacity(0.2),
                run_time=0.5
            )
            self.play(
                web.animate.scale(1/1.1).set_opacity(0.4),
                run_time=0.5
            )
            self.play(
                soul.animate.shift(DOWN * 1),
                run_time=1.5
            )
        
        # Soul falls through completely
        self.play(
            soul.animate.shift(DOWN * 2).scale(0.5).set_opacity(0.4),
            web.animate.set_opacity(0.1),
            run_time=3,
            rate_func=rate_functions.ease_in_quad
        )
        
        self.play(FadeOut(web), run_time=1)


class Track08_IDK(Scene):
    """Confusion - "drugs don't work on me no more" """
    def construct(self):
        soul = SoulOrb(color=SuryaColors.IDK, radius=0.25)
        soul.set_opacity(0.6)
        self.add(soul)
        
        # Strange attractor - chaos and confusion
        attractor = StrangeAttractor(color=SuryaColors.IDK)
        attractor.scale(0.5)
        
        self.play(Create(attractor), run_time=5)
        
        # Soul wanders erratically
        random_points = [
            np.array([random.uniform(-2, 2), random.uniform(-1.5, 1.5), 0])
            for _ in range(8)
        ]
        
        for point in random_points:
            self.play(
                soul.animate.move_to(point),
                run_time=0.6,
                rate_func=rate_functions.ease_in_out_sine
            )
        
        # Multiple Lissajous curves - mind racing
        curves = VGroup()
        for i in range(4):
            curve = LissajousCurve(
                a=random.randint(2, 5),
                b=random.randint(2, 5),
                delta=random.uniform(0, PI),
                color=SuryaColors.IDK
            )
            curve.scale(0.5 + i * 0.2)
            curve.set_opacity(0.2)
            curves.add(curve)
        
        self.play(
            *[Create(c) for c in curves],
            run_time=3,
            lag_ratio=0.2
        )
        
        # Everything spins in confusion
        self.play(
            Rotate(curves, PI, about_point=ORIGIN),
            soul.animate.move_to(ORIGIN).scale(0.8),
            run_time=4
        )
        
        # Collapse into uncertainty
        self.play(
            FadeOut(attractor, curves),
            soul.animate.scale(0.5).set_opacity(0.4),
            run_time=3
        )


class Track09_WithU(Scene):
    """THE TURN - "floating through the stars" - Two souls meet"""
    def construct(self):
        # Primary soul
        soul1 = SoulOrb(color=WHITE, radius=0.3)
        soul1.shift(LEFT * 3)
        
        # Second soul appears - golden warmth
        soul2 = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
        soul2.shift(RIGHT * 5)
        soul2.set_opacity(0)
        
        self.play(FadeIn(soul1), run_time=2)
        
        # Stars begin appearing
        stars = VGroup()
        for _ in range(80):
            star = Dot(
                point=[random.uniform(-7, 7), random.uniform(-4, 4), 0],
                radius=random.uniform(0.01, 0.04),
                color=WHITE
            )
            star.set_opacity(0)
            stars.add(star)
        
        self.add(stars)
        self.play(
            *[s.animate.set_opacity(random.uniform(0.3, 0.9)) for s in stars],
            run_time=3,
            lag_ratio=0.02
        )
        
        # Second soul emerges from the stars
        self.play(
            soul2.animate.set_opacity(1).shift(LEFT * 2),
            run_time=3
        )
        
        # Souls move toward each other
        self.play(
            soul1.animate.shift(RIGHT * 1.5),
            soul2.animate.shift(LEFT * 1.5),
            run_time=3,
            rate_func=smooth
        )
        
        # They begin orbiting each other - binary star system
        orbit_center = (soul1.get_center() + soul2.get_center()) / 2
        
        # Golden spiral emerges between them
        spiral = GoldenSpiral(turns=4, color=SuryaColors.WITH_U)
        spiral.scale(0.4)
        spiral.move_to(orbit_center)
        spiral.set_opacity(0)
        
        self.play(spiral.animate.set_opacity(0.5), run_time=2)
        
        # Orbiting animation
        for _ in range(2):
            self.play(
                Rotate(soul1, PI, about_point=orbit_center),
                Rotate(soul2, PI, about_point=orbit_center),
                Rotate(spiral, PI/2, about_point=orbit_center),
                run_time=4,
                rate_func=smooth
            )
        
        # Souls move closer, colors blend
        self.play(
            soul1.animate.scale(1.2).move_to(orbit_center + LEFT * 0.3),
            soul2.animate.scale(1.2).move_to(orbit_center + RIGHT * 0.3),
            spiral.animate.scale(1.5),
            run_time=3
        )
        
        # Radiant climax - sacred geometry
        flower = FlowerOfLife(rings=2, radius=0.5, color=SuryaColors.WITH_U)
        flower.move_to(orbit_center)
        flower.set_opacity(0)
        
        self.play(
            flower.animate.set_opacity(0.4).scale(1.2),
            stars.animate.set_opacity(0.8),
            run_time=3
        )
        
        # Final pulse of connection
        self.play(
            soul1.animate.scale(1.3),
            soul2.animate.scale(1.3),
            flower.animate.scale(1.3),
            run_time=1.5
        )
        self.play(
            soul1.animate.scale(1/1.3),
            soul2.animate.scale(1/1.3),
            flower.animate.scale(1/1.3),
            run_time=1.5
        )
        
        # Gentle fade maintaining connection
        self.play(
            flower.animate.set_opacity(0.1),
            spiral.animate.set_opacity(0.2),
            stars.animate.set_opacity(0.3),
            run_time=3
        )


class Track10_PoorYouPoorMe(Scene):
    """Bittersweet - "you left your cardigan on my bed" """
    def construct(self):
        # Two souls, but one is fading
        soul1 = SoulOrb(color=SuryaColors.POOR, radius=0.3)
        soul2 = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
        soul2.shift(RIGHT * 1.5)
        
        self.play(FadeIn(soul1, soul2), run_time=2)
        
        # Spirograph of intertwined paths
        spiro = Spirograph(R=3, r=1.8, d=1.5, color=SuryaColors.POOR)
        spiro.scale(0.6)
        spiro.set_opacity(0.4)
        
        self.play(Create(spiro), run_time=4)
        
        # One soul begins to drift away
        self.play(
            soul2.animate.shift(RIGHT * 2).set_opacity(0.6),
            run_time=3
        )
        
        # Lingering warmth - particles like memories
        memories = VGroup()
        for _ in range(30):
            m = Dot(
                point=soul2.get_center() + np.array([
                    random.uniform(-1, 1),
                    random.uniform(-1, 1),
                    0
                ]),
                radius=0.03,
                color=SuryaColors.WITH_U
            )
            m.set_opacity(0.5)
            memories.add(m)
        
        self.play(FadeIn(memories), run_time=2)
        
        # Memories drift toward remaining soul
        self.play(
            *[m.animate.move_to(
                soul1.get_center() + np.array([random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), 0])
            ) for m in memories],
            soul2.animate.shift(RIGHT * 2).set_opacity(0.2),
            run_time=4
        )
        
        # Bittersweet pulse
        self.play(
            soul1.animate.scale(1.2),
            spiro.animate.set_opacity(0.6),
            run_time=2
        )
        self.play(
            soul1.animate.scale(1/1.2),
            spiro.animate.set_opacity(0.3),
            run_time=2
        )
        
        self.play(
            FadeOut(soul2, memories, spiro),
            run_time=3
        )


class Track11_Wait4U(Scene):
    """Longing - waiting"""
    def construct(self):
        soul = SoulOrb(color=SuryaColors.WAIT, radius=0.3)
        self.play(FadeIn(soul), run_time=2)
        
        # Concentric circles - time passing, waiting
        circles = VGroup()
        for i in range(8):
            c = Circle(radius=0.5 + i * 0.4, color=SuryaColors.WAIT)
            c.set_stroke(width=1, opacity=0.3 - i * 0.03)
            circles.add(c)
        
        self.play(
            *[Create(c) for c in circles],
            run_time=3,
            lag_ratio=0.3
        )
        
        # Slow pulsing - patient waiting
        for _ in range(3):
            self.play(
                circles.animate.scale(1.1),
                soul.animate.scale(0.9),
                run_time=2
            )
            self.play(
                circles.animate.scale(1/1.1),
                soul.animate.scale(1/0.9),
                run_time=2
            )
        
        # Spiral of longing
        spiral = GoldenSpiral(turns=5, color=SuryaColors.WAIT)
        spiral.scale(0.3)
        spiral.set_opacity(0)
        
        self.play(
            spiral.animate.set_opacity(0.4).scale(2),
            circles.animate.set_opacity(0.1),
            run_time=4
        )
        
        self.play(
            FadeOut(circles, spiral),
            run_time=2
        )


class Track12_RunToU(Scene):
    """Urgency - "if the sky was falling I would run to you" """
    def construct(self):
        # Soul starts at edge, urgent energy
        soul = SoulOrb(color=SuryaColors.RUN, radius=0.3)
        soul.shift(LEFT * 5)
        
        # Target - the other soul (faint in distance)
        target = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
        target.shift(RIGHT * 5)
        target.set_opacity(0.3)
        
        self.play(FadeIn(soul, target), run_time=1)
        
        # Streaking particles - speed lines
        speed_lines = VGroup()
        for _ in range(20):
            y = random.uniform(-3, 3)
            line = Line(LEFT * 7, RIGHT * 7, color=SuryaColors.RUN)
            line.shift(UP * y)
            line.set_stroke(width=1, opacity=0.2)
            speed_lines.add(line)
        
        self.play(
            *[Create(l) for l in speed_lines],
            run_time=1
        )
        
        # Running motion - urgent spirograph
        spiro = Spirograph(R=2, r=0.3, d=2, color=SuryaColors.RUN)
        spiro.scale(0.3)
        spiro.move_to(soul.get_center())
        spiro.set_opacity(0)
        
        # Urgent movement toward target
        self.play(
            soul.animate.shift(RIGHT * 4),
            spiro.animate.shift(RIGHT * 4).set_opacity(0.5),
            target.animate.set_opacity(0.6),
            Rotate(spiro, TAU * 2, about_point=soul.get_center()),
            run_time=3,
            rate_func=rate_functions.ease_in_quad
        )
        
        # Final push
        self.play(
            soul.animate.shift(RIGHT * 4),
            target.animate.shift(LEFT * 1).set_opacity(1),
            speed_lines.animate.set_opacity(0.5),
            run_time=2,
            rate_func=rate_functions.ease_out_quad
        )
        
        # Collision/embrace - burst of energy
        burst = VGroup()
        for i in range(12):
            angle = i * PI / 6
            ray = Line(ORIGIN, 2 * np.array([np.cos(angle), np.sin(angle), 0]))
            ray.set_stroke(SuryaColors.RUN, width=3, opacity=0.6)
            burst.add(ray)
        burst.move_to(RIGHT * 2)
        
        self.play(
            FadeIn(burst, scale=0.5),
            soul.animate.move_to(RIGHT * 2).scale(1.3),
            target.animate.move_to(RIGHT * 2).scale(1.3),
            run_time=1
        )
        
        self.play(
            burst.animate.scale(2).set_opacity(0),
            FadeOut(speed_lines, spiro),
            run_time=2
        )


class Track13_Medications(Scene):
    """Struggle - "raging in my head" """
    def construct(self):
        soul = SoulOrb(color=SuryaColors.MEDS, radius=0.3)
        self.play(FadeIn(soul), run_time=1)
        
        # Strange attractor - chaos in the mind
        attractor = StrangeAttractor(color=SuryaColors.MEDS)
        attractor.scale(0.6)
        
        self.play(Create(attractor), run_time=3)
        
        # Aggressive spirograph - war in the head
        spiros = VGroup()
        for i in range(3):
            s = Spirograph(R=2 + i, r=0.5 + i * 0.3, d=1.5, color=SuryaColors.MEDS)
            s.scale(0.4)
            s.set_opacity(0.3)
            s.rotate(i * PI / 3)
            spiros.add(s)
        
        self.play(
            *[Create(s) for s in spiros],
            run_time=3
        )
        
        # Soul shakes violently
        original_pos = soul.get_center()
        for _ in range(6):
            offset = np.array([random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3), 0])
            self.play(
                soul.animate.move_to(original_pos + offset),
                run_time=0.15
            )
        self.play(soul.animate.move_to(original_pos), run_time=0.2)
        
        # Red pulses - pain
        for _ in range(3):
            pulse = Circle(radius=0.5, color=SuryaColors.MEDS)
            pulse.set_stroke(width=3, opacity=0.8)
            self.add(pulse)
            self.play(
                pulse.animate.scale(4).set_opacity(0),
                spiros.animate.rotate(PI/6),
                run_time=1
            )
            self.remove(pulse)
        
        # Struggle continues
        self.play(
            Rotate(attractor, PI, about_point=ORIGIN),
            spiros.animate.set_opacity(0.5),
            soul.animate.scale(0.8),
            run_time=3
        )
        
        # Eventual exhaustion
        self.play(
            attractor.animate.set_opacity(0.2),
            spiros.animate.set_opacity(0.1).scale(0.8),
            soul.animate.scale(0.7).set_opacity(0.6),
            run_time=3
        )
        
        self.play(FadeOut(attractor, spiros), run_time=2)


class Track14_Hollow(Scene):
    """Resolution - "you took my sorrow and flew it to the moon" """
    def construct(self):
        # Soul begins small and humble
        soul = SoulOrb(color=SuryaColors.HOLLOW, radius=0.2)
        soul.set_opacity(0.5)
        self.play(FadeIn(soul), run_time=2)
        
        # Stars return - hope
        stars = VGroup()
        for _ in range(100):
            star = Dot(
                point=[random.uniform(-7, 7), random.uniform(-4, 4), 0],
                radius=random.uniform(0.01, 0.05),
                color=WHITE
            )
            star.set_opacity(0)
            stars.add(star)
        
        self.add(stars)
        self.play(
            *[s.animate.set_opacity(random.uniform(0.2, 0.8)) for s in stars],
            soul.animate.set_opacity(0.8).scale(1.2),
            run_time=4,
            lag_ratio=0.01
        )
        
        # Golden spiral - resolution through golden ratio
        spiral = GoldenSpiral(turns=5, color=SuryaColors.HOLLOW)
        spiral.scale(0.2)
        spiral.set_opacity(0)
        
        self.play(
            spiral.animate.set_opacity(0.6).scale(3),
            run_time=5
        )
        
        # Moon appears (large circle in upper area)
        moon = Circle(radius=1.5, color=SuryaColors.HOLLOW)
        moon.set_fill(SuryaColors.HOLLOW, opacity=0.1)
        moon.set_stroke(SuryaColors.HOLLOW, width=2, opacity=0.5)
        moon.shift(UP * 2 + RIGHT * 3)
        
        self.play(FadeIn(moon, scale=0.5), run_time=3)
        
        # Sorrow (dark particles) flying to the moon
        sorrow = VGroup()
        for _ in range(20):
            s = Dot(
                point=soul.get_center() + np.array([
                    random.uniform(-0.5, 0.5),
                    random.uniform(-0.5, 0.5),
                    0
                ]),
                radius=0.04,
                color=GREY
            )
            sorrow.add(s)
        
        self.play(FadeIn(sorrow), run_time=1)
        
        # Sorrow flies to moon
        self.play(
            *[s.animate.move_to(moon.get_center() + np.array([
                random.uniform(-0.5, 0.5),
                random.uniform(-0.5, 0.5),
                0
            ])).set_opacity(0) for s in sorrow],
            soul.animate.scale(1.3).set_opacity(1),
            run_time=4
        )
        
        # Sacred geometry finale
        flower = FlowerOfLife(rings=2, radius=0.6, color=SuryaColors.HOLLOW)
        flower.set_opacity(0)
        
        self.play(
            flower.animate.set_opacity(0.4),
            spiral.animate.set_opacity(0.8),
            run_time=3
        )
        
        # Final radiant pulse
        self.play(
            soul.animate.scale(1.5),
            flower.animate.scale(1.3),
            spiral.animate.scale(1.2),
            stars.animate.set_opacity(1),
            run_time=2
        )
        
        # Peaceful hold
        self.wait(3)
        
        # Gentle fade to completion
        self.play(
            soul.animate.scale(2).set_opacity(0.5),
            flower.animate.set_opacity(0.2),
            spiral.animate.set_opacity(0.3),
            stars.animate.set_opacity(0.5),
            moon.animate.set_opacity(0.3),
            run_time=4
        )


# ============================================================================
# MAIN JOURNEY - FULL COMPOSITION
# ============================================================================

class SuryaJourney(Scene):
    """
    SURYA — The Complete Journey Through Feeling
    14 tracks of mathematical beauty
    """
    
    def construct(self):
        # Persistent elements
        self.soul = SoulOrb(color=WHITE, radius=0.3)
        self.stars = self.create_stars()
        self.particles = self.create_particles()
        
        # Journey begins
        self.intro()
        self.track_01_skin()
        self.track_02_u_saved_me()
        self.track_03_nothing()
        self.track_04_sweet_relief()
        self.track_05_tiptoe()
        self.track_06_natures_call()
        self.track_07_dreamcatcher()
        self.track_08_idk()
        self.track_09_with_u()
        self.track_10_poor_you_poor_me()
        self.track_11_wait_4_u()
        self.track_12_run_to_u()
        self.track_13_medications()
        self.track_14_hollow()
        self.finale()
    
    def create_stars(self):
        stars = VGroup()
        for _ in range(100):
            star = Dot(
                point=[random.uniform(-7, 7), random.uniform(-4, 4), 0],
                radius=random.uniform(0.01, 0.05),
                color=WHITE
            )
            star.set_opacity(0)
            stars.add(star)
        return stars
    
    def create_particles(self):
        particles = VGroup()
        for _ in range(60):
            p = Dot(
                point=[random.uniform(-7, 7), random.uniform(-5, -4), 0],
                radius=random.uniform(0.02, 0.05),
                color=WHITE
            )
            p.set_opacity(0)
            particles.add(p)
        return particles
    
    def transition_color(self, new_color, run_time=2):
        """Smoothly transition soul and ambient elements to new color"""
        new_soul = SoulOrb(color=new_color, radius=self.soul.core.radius * 5)
        new_soul.move_to(self.soul.get_center())
        
        self.play(
            Transform(self.soul, new_soul),
            run_time=run_time
        )
    
    def show_title(self, number, title, lyric="", color=WHITE):
        """Display track title"""
        num_text = Text(f"{number:02d}", font_size=24, color=color)
        num_text.set_opacity(0.4)
        num_text.to_edge(UP, buff=1)
        
        title_text = Text(title, font_size=72, color=color, slant=ITALIC)
        title_text.next_to(num_text, DOWN, buff=0.5)
        
        lyric_text = Text(lyric, font_size=28, color=color, slant=ITALIC)
        lyric_text.set_opacity(0.6)
        lyric_text.next_to(title_text, DOWN, buff=0.5)
        
        group = VGroup(num_text, title_text, lyric_text)
        
        self.play(
            FadeIn(group, shift=UP * 0.5),
            run_time=1.5
        )
        self.wait(1)
        self.play(
            FadeOut(group, shift=UP * 0.5),
            run_time=1
        )
    
    # =========== INTRO ===========
    def intro(self):
        # Title sequence
        surya_text = Text("SURYA", font_size=120, color=WHITE)
        sub_text = Text("a journey through feeling", font_size=32, color=WHITE)
        sub_text.set_opacity(0.5)
        sub_text.next_to(surya_text, DOWN, buff=0.5)
        artist_text = Text("DAS", font_size=20, color=WHITE)
        artist_text.set_opacity(0.3)
        artist_text.next_to(sub_text, DOWN, buff=1)
        
        self.play(
            FadeIn(surya_text, scale=0.9),
            run_time=3
        )
        self.play(FadeIn(sub_text, artist_text), run_time=2)
        self.wait(2)
        
        # Soul emerges
        self.add(self.stars, self.particles)
        self.play(
            FadeIn(self.soul, scale=0.5),
            FadeOut(surya_text, sub_text, artist_text),
            run_time=3
        )
        self.wait(1)
    
    # =========== TRACK 01: SKIN ===========
    def track_01_skin(self):
        color = SuryaColors.SKIN
        self.show_title(1, "skin", '"don\'t fit in my own skin"', color)
        
        # Transform soul color
        self.transition_color(color)
        
        # Morphing spirograph - identity struggle
        spiro1 = Spirograph(R=3, r=0.7, d=1.2, color=color)
        spiro2 = Spirograph(R=3, r=1.3, d=0.8, color=color)
        spiro1.scale(0.8)
        spiro2.scale(0.8)
        
        self.play(Create(spiro1), run_time=3)
        
        # Soul pulsates uncomfortably
        self.play(
            self.soul.animate.scale(0.7),
            run_time=1
        )
        self.play(
            self.soul.animate.scale(1.4),
            run_time=1
        )
        
        # Transform identity
        self.play(
            Transform(spiro1, spiro2),
            self.soul.animate.scale(0.8),
            run_time=4
        )
        
        # Particles of self floating
        temp_particles = ParticleField(40, color=color)
        self.play(FadeIn(temp_particles, lag_ratio=0.05), run_time=2)
        
        self.play(
            temp_particles.animate.shift(UP * 2).set_opacity(0),
            run_time=3
        )
        
        # Fade out
        self.play(
            FadeOut(spiro1, temp_particles),
            self.soul.animate.scale(0.8),
            run_time=2
        )
    
    # =========== TRACK 02: U SAVED ME ===========
    def track_02_u_saved_me(self):
        color = SuryaColors.SAVED
        self.show_title(2, "u saved me", '"you saved me from my broken soul"', color)
        
        # Soul fragments scattered
        fragments = VGroup(*[
            Dot(point=[random.uniform(-2, 2), random.uniform(-2, 2), 0], 
                radius=0.05, color=color)
            for _ in range(25)
        ])
        
        self.play(
            FadeIn(fragments, lag_ratio=0.05),
            self.soul.animate.set_opacity(0.3),
            run_time=2
        )
        
        # Fragments coalesce
        self.play(
            *[f.animate.move_to(self.soul.get_center()) for f in fragments],
            run_time=3
        )
        self.transition_color(color)
        
        self.play(
            FadeOut(fragments),
            self.soul.animate.set_opacity(1).scale(1.3),
            run_time=2
        )
        
        # Sacred geometry - spiritual awakening
        flower = FlowerOfLife(rings=2, radius=0.6, color=color)
        flower.set_opacity(0)
        self.add(flower)
        
        # Hopeful spirograph
        spiro = Spirograph(R=4, r=1.5, d=2, color=color)
        spiro.scale(0.7)
        spiro.set_opacity(0)
        self.add(spiro)
        
        self.play(
            flower.animate.set_opacity(0.4),
            spiro.animate.set_opacity(0.5),
            run_time=4
        )
        
        # Harmony pulse
        self.play(
            self.soul.animate.scale(1.2),
            flower.animate.scale(1.1),
            run_time=1.5
        )
        self.play(
            self.soul.animate.scale(1/1.2),
            flower.animate.scale(1/1.1),
            run_time=1.5
        )
        
        self.play(
            FadeOut(flower, spiro),
            run_time=2
        )
    
    # =========== TRACK 03: NOTHING ===========
    def track_03_nothing(self):
        color = SuryaColors.NOTHING
        self.show_title(3, "nothing", '"I lost my heart, now I feel nothing"', color)
        
        self.transition_color(color)
        
        # Voronoi fragmentation
        voronoi = VoronoiFragments(num_points=25, color=color)
        
        self.play(
            self.soul.animate.scale(0.6),
            run_time=2
        )
        
        self.play(Create(voronoi), run_time=4)
        
        # Grey void
        grey_circle = Circle(radius=3, color=color)
        grey_circle.set_stroke(width=1, opacity=0.2)
        grey_circle.set_fill(color, opacity=0.03)
        
        self.play(
            FadeIn(grey_circle),
            self.soul.animate.scale(0.5).set_opacity(0.3),
            voronoi.animate.set_opacity(0.1),
            run_time=4
        )
        
        # Hollow pulsing
        for _ in range(2):
            self.play(
                grey_circle.animate.scale(1.1),
                run_time=1.5,
                rate_func=there_and_back
            )
        
        self.wait(1)
        
        # Scatter into void
        self.play(
            FadeOut(voronoi, shift=UP),
            FadeOut(grey_circle),
            self.soul.animate.set_opacity(0.2),
            run_time=3
        )
    
    # =========== TRACK 04: SWEET RELIEF ===========
    def track_04_sweet_relief(self):
        color = SuryaColors.RELIEF
        self.show_title(4, "sweet relief", '"seeing ghosts around my throat"', color)
        
        self.transition_color(color)
        self.play(self.soul.animate.set_opacity(0.8).scale(1.5), run_time=1)
        
        # Ghost spirals
        ghosts = VGroup()
        for i in range(5):
            ghost = Spirograph(R=2 + i * 0.3, r=0.5, d=1 + i * 0.2, color=color)
            ghost.scale(0.5)
            ghost.set_opacity(0.15 + i * 0.05)
            ghost.rotate(i * PI / 5)
            ghosts.add(ghost)
        
        self.play(
            *[Create(g) for g in ghosts],
            run_time=4,
            lag_ratio=0.3
        )
        
        # Ghosts circle menacingly
        self.play(
            Rotate(ghosts, PI, about_point=ORIGIN),
            self.soul.animate.scale(0.8),
            run_time=4
        )
        
        # Soul struggles
        self.play(self.soul.animate.shift(UP * 0.3), run_time=0.4)
        self.play(self.soul.animate.shift(DOWN * 0.3), run_time=0.4)
        
        # Ghosts close in
        self.play(
            ghosts.animate.scale(0.7),
            run_time=3
        )
        
        # Brief relief
        self.play(
            ghosts.animate.scale(1.5).set_opacity(0.05),
            self.soul.animate.scale(1.2).set_opacity(1),
            run_time=2
        )
        
        self.play(FadeOut(ghosts), run_time=2)
    
    # =========== TRACK 05: TIPTOE ===========
    def track_05_tiptoe(self):
        color = SuryaColors.RELIEF
        self.show_title(5, "tiptoe", "", color)
        
        # Lissajous path
        path = LissajousCurve(a=3, b=2, delta=PI/4, color=color)
        path.set_opacity(0.3)
        
        self.play(Create(path), run_time=2)
        
        # Careful movement
        self.soul.move_to(LEFT * 3)
        positions = [LEFT * 2, LEFT * 1, ORIGIN, RIGHT * 1, RIGHT * 2]
        
        for pos in positions:
            self.play(
                self.soul.animate.move_to(pos),
                run_time=0.8
            )
            self.wait(0.2)
        
        # Tension path
        path2 = LissajousCurve(a=5, b=4, delta=PI/3, color=color)
        path2.set_opacity(0.2)
        
        self.play(
            Transform(path, path2),
            self.soul.animate.move_to(ORIGIN).scale(0.8),
            run_time=3
        )
        
        self.play(FadeOut(path), run_time=1)
    
    # =========== TRACK 06: NATURE'S CALL ===========
    def track_06_natures_call(self):
        color = SuryaColors.NATURE
        self.show_title(6, "nature's call", '"thank you for joining us"', color)
        
        self.transition_color(color)
        self.play(self.soul.animate.scale(1.3), run_time=1)
        
        # Fractal tree grows
        tree = FractalTree(depth=6, length=1.5, color=color)
        tree.shift(DOWN * 2)
        tree.set_opacity(0)
        self.add(tree)
        
        self.play(
            tree.animate.set_opacity(0.5),
            run_time=5
        )
        
        # Nature particles float up
        nature_particles = VGroup()
        for _ in range(40):
            p = Dot(
                point=[random.uniform(-4, 4), random.uniform(-3, -2), 0],
                radius=random.uniform(0.02, 0.05),
                color=color
            )
            p.set_opacity(random.uniform(0.3, 0.6))
            nature_particles.add(p)
        
        self.play(FadeIn(nature_particles, lag_ratio=0.03), run_time=2)
        
        self.play(
            *[p.animate.shift(UP * random.uniform(4, 6)) for p in nature_particles],
            run_time=5
        )
        
        # Golden spiral - nature's math
        spiral = GoldenSpiral(turns=3, color=color)
        spiral.scale(0.3)
        spiral.set_opacity(0)
        
        self.play(
            spiral.animate.set_opacity(0.5).scale(2),
            run_time=4
        )
        
        self.play(
            FadeOut(tree, spiral, nature_particles),
            run_time=2
        )
    
    # =========== TRACK 07: DREAMCATCHER ===========
    def track_07_dreamcatcher(self):
        color = SuryaColors.DREAM
        self.show_title(7, "dreamcatcher", '"falling through the cracks"', color)
        
        self.transition_color(color)
        self.soul.move_to(UP * 2.5)
        
        # Dreamcatcher web
        web = VGroup()
        for i in range(6):
            angle = i * PI / 3
            line = Line(ORIGIN, 3 * np.array([np.cos(angle), np.sin(angle), 0]))
            line.set_stroke(color, width=1, opacity=0.4)
            web.add(line)
        
        for r in [0.5, 1, 1.5, 2, 2.5]:
            circle = Circle(radius=r, color=color)
            circle.set_stroke(width=1, opacity=0.3)
            web.add(circle)
        
        self.play(Create(web), run_time=3)
        
        # Soul falls through
        self.play(
            self.soul.animate.shift(DOWN * 2),
            run_time=2,
            rate_func=rate_functions.ease_in_quad
        )
        
        # Passing through layers
        for _ in range(2):
            self.play(
                web.animate.scale(1.1).set_opacity(0.2),
                run_time=0.5
            )
            self.play(
                web.animate.scale(1/1.1).set_opacity(0.4),
                run_time=0.5
            )
            self.play(
                self.soul.animate.shift(DOWN * 1.2),
                run_time=1.5
            )
        
        # Falls through completely
        self.play(
            self.soul.animate.move_to(ORIGIN).scale(0.7).set_opacity(0.5),
            web.animate.set_opacity(0.1),
            run_time=3
        )
        
        self.play(FadeOut(web), run_time=1)
    
    # =========== TRACK 08: IDK ===========
    def track_08_idk(self):
        color = SuryaColors.IDK
        self.show_title(8, "idk", '"drugs don\'t work on me no more"', color)
        
        self.transition_color(color)
        self.play(self.soul.animate.set_opacity(0.7).scale(1.2), run_time=1)
        
        # Strange attractor - chaos
        attractor = StrangeAttractor(color=color)
        attractor.scale(0.5)
        
        self.play(Create(attractor), run_time=4)
        
        # Erratic wandering
        for _ in range(6):
            point = np.array([random.uniform(-1.5, 1.5), random.uniform(-1, 1), 0])
            self.play(
                self.soul.animate.move_to(point),
                run_time=0.5
            )
        
        # Multiple confused curves
        curves = VGroup()
        for i in range(3):
            curve = LissajousCurve(
                a=random.randint(2, 5),
                b=random.randint(2, 5),
                delta=random.uniform(0, PI),
                color=color
            )
            curve.scale(0.5 + i * 0.2)
            curve.set_opacity(0.2)
            curves.add(curve)
        
        self.play(
            *[Create(c) for c in curves],
            run_time=3,
            lag_ratio=0.2
        )
        
        # Spin in confusion
        self.play(
            Rotate(curves, PI, about_point=ORIGIN),
            self.soul.animate.move_to(ORIGIN).scale(0.8),
            run_time=4
        )
        
        # Collapse
        self.play(
            FadeOut(attractor, curves),
            self.soul.animate.scale(0.7).set_opacity(0.5),
            run_time=3
        )
    
    # =========== TRACK 09: WITH U (THE TURN) ===========
    def track_09_with_u(self):
        color = SuryaColors.WITH_U
        self.show_title(9, "with u", '"floating through the stars"', color)
        
        # This is THE TURN - second soul appears
        self.play(self.soul.animate.shift(LEFT * 2).set_opacity(1).scale(1.3), run_time=2)
        self.transition_color(WHITE)
        
        # Stars light up
        self.play(
            *[s.animate.set_opacity(random.uniform(0.3, 0.9)) for s in self.stars],
            run_time=3,
            lag_ratio=0.01
        )
        
        # Second soul emerges - golden
        soul2 = SoulOrb(color=color, radius=0.25)
        soul2.shift(RIGHT * 5)
        soul2.set_opacity(0)
        self.add(soul2)
        
        self.play(
            soul2.animate.set_opacity(1).shift(LEFT * 3),
            run_time=3
        )
        
        # Souls approach each other
        self.play(
            self.soul.animate.shift(RIGHT * 0.5),
            soul2.animate.shift(LEFT * 0.5),
            run_time=2
        )
        
        # Golden spiral between them
        orbit_center = (self.soul.get_center() + soul2.get_center()) / 2
        spiral = GoldenSpiral(turns=4, color=color)
        spiral.scale(0.4)
        spiral.move_to(orbit_center)
        spiral.set_opacity(0)
        
        self.play(spiral.animate.set_opacity(0.6), run_time=2)
        
        # Binary orbit
        for _ in range(2):
            self.play(
                Rotate(self.soul, PI, about_point=orbit_center),
                Rotate(soul2, PI, about_point=orbit_center),
                Rotate(spiral, PI/2, about_point=orbit_center),
                run_time=4
            )
        
        # Closer, colors blend
        self.play(
            self.soul.animate.scale(1.2).move_to(orbit_center + LEFT * 0.3),
            soul2.animate.scale(1.2).move_to(orbit_center + RIGHT * 0.3),
            spiral.animate.scale(1.5),
            run_time=3
        )
        
        # Sacred geometry climax
        flower = FlowerOfLife(rings=2, radius=0.5, color=color)
        flower.move_to(orbit_center)
        flower.set_opacity(0)
        
        self.play(
            flower.animate.set_opacity(0.4).scale(1.2),
            self.stars.animate.set_opacity(0.9),
            run_time=3
        )
        
        # Peak pulse
        self.play(
            self.soul.animate.scale(1.3),
            soul2.animate.scale(1.3),
            flower.animate.scale(1.3),
            run_time=1.5
        )
        self.play(
            self.soul.animate.scale(1/1.3),
            soul2.animate.scale(1/1.3),
            flower.animate.scale(1/1.3),
            run_time=1.5
        )
        
        # Store soul2 for later
        self.soul2 = soul2
        
        self.play(
            flower.animate.set_opacity(0.1),
            spiral.animate.set_opacity(0.2),
            run_time=2
        )
        
        self.play(FadeOut(flower, spiral), run_time=1)
    
    # =========== TRACK 10: POOR YOU POOR ME ===========
    def track_10_poor_you_poor_me(self):
        color = SuryaColors.POOR
        self.show_title(10, "poor you poor me", '"you left your cardigan on my bed"', color)
        
        self.transition_color(color)
        
        # Bittersweet spirograph
        spiro = Spirograph(R=3, r=1.8, d=1.5, color=color)
        spiro.scale(0.6)
        spiro.set_opacity(0.4)
        
        self.play(Create(spiro), run_time=3)
        
        # One soul drifts away
        self.play(
            self.soul2.animate.shift(RIGHT * 3).set_opacity(0.4),
            run_time=4
        )
        
        # Memories drift
        memories = VGroup()
        for _ in range(20):
            m = Dot(
                point=self.soul2.get_center() + np.array([
                    random.uniform(-0.5, 0.5),
                    random.uniform(-0.5, 0.5),
                    0
                ]),
                radius=0.03,
                color=SuryaColors.WITH_U
            )
            m.set_opacity(0.5)
            memories.add(m)
        
        self.play(FadeIn(memories), run_time=1)
        
        self.play(
            *[m.animate.move_to(
                self.soul.get_center() + np.array([random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3), 0])
            ) for m in memories],
            self.soul2.animate.shift(RIGHT * 2).set_opacity(0.1),
            run_time=4
        )
        
        # Bittersweet pulse
        self.play(
            self.soul.animate.scale(1.2),
            spiro.animate.set_opacity(0.6),
            run_time=2
        )
        self.play(
            self.soul.animate.scale(1/1.2),
            spiro.animate.set_opacity(0.3),
            run_time=2
        )
        
        self.play(
            FadeOut(self.soul2, memories, spiro),
            run_time=2
        )
    
    # =========== TRACK 11: WAIT 4 U ===========
    def track_11_wait_4_u(self):
        color = SuryaColors.WAIT
        self.show_title(11, "wait 4 u", "", color)
        
        self.transition_color(color)
        self.soul.move_to(ORIGIN)
        
        # Concentric circles - time passing
        circles = VGroup()
        for i in range(8):
            c = Circle(radius=0.5 + i * 0.4, color=color)
            c.set_stroke(width=1, opacity=0.3 - i * 0.03)
            circles.add(c)
        
        self.play(
            *[Create(c) for c in circles],
            run_time=3,
            lag_ratio=0.3
        )
        
        # Patient pulsing
        for _ in range(2):
            self.play(
                circles.animate.scale(1.1),
                self.soul.animate.scale(0.9),
                run_time=2
            )
            self.play(
                circles.animate.scale(1/1.1),
                self.soul.animate.scale(1/0.9),
                run_time=2
            )
        
        # Longing spiral
        spiral = GoldenSpiral(turns=5, color=color)
        spiral.scale(0.3)
        spiral.set_opacity(0)
        
        self.play(
            spiral.animate.set_opacity(0.4).scale(2),
            circles.animate.set_opacity(0.1),
            run_time=4
        )
        
        self.play(FadeOut(circles, spiral), run_time=2)
    
    # =========== TRACK 12: RUN TO U ===========
    def track_12_run_to_u(self):
        color = SuryaColors.RUN
        self.show_title(12, "run to u", '"if the sky was falling I would run to you"', color)
        
        self.transition_color(color)
        self.soul.move_to(LEFT * 4)
        
        # Target in distance
        target = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
        target.shift(RIGHT * 4)
        target.set_opacity(0.3)
        self.add(target)
        
        # Speed lines
        speed_lines = VGroup()
        for _ in range(15):
            y = random.uniform(-3, 3)
            line = Line(LEFT * 7, RIGHT * 7, color=color)
            line.shift(UP * y)
            line.set_stroke(width=1, opacity=0.2)
            speed_lines.add(line)
        
        self.play(FadeIn(speed_lines), run_time=0.5)
        
        # Urgent run
        self.play(
            self.soul.animate.shift(RIGHT * 4),
            target.animate.set_opacity(0.7),
            run_time=2,
            rate_func=rate_functions.ease_in_quad
        )
        
        # Final push
        self.play(
            self.soul.animate.shift(RIGHT * 3),
            target.animate.shift(LEFT * 1).set_opacity(1),
            run_time=1.5,
            rate_func=rate_functions.ease_out_quad
        )
        
        # Collision burst
        burst = VGroup()
        for i in range(12):
            angle = i * PI / 6
            ray = Line(ORIGIN, 2 * np.array([np.cos(angle), np.sin(angle), 0]))
            ray.set_stroke(color, width=3, opacity=0.6)
            burst.add(ray)
        burst.move_to(RIGHT * 1.5)
        
        self.play(
            FadeIn(burst, scale=0.5),
            self.soul.animate.move_to(RIGHT * 1.5).scale(1.2),
            target.animate.move_to(RIGHT * 1.5).scale(1.2),
            run_time=1
        )
        
        self.play(
            burst.animate.scale(2).set_opacity(0),
            FadeOut(speed_lines, target),
            run_time=2
        )
        self.remove(burst)
    
    # =========== TRACK 13: MEDICATIONS ===========
    def track_13_medications(self):
        color = SuryaColors.MEDS
        self.show_title(13, "medications", '"raging in my head"', color)
        
        self.transition_color(color)
        self.soul.move_to(ORIGIN)
        
        # Chaos attractor
        attractor = StrangeAttractor(color=color)
        attractor.scale(0.5)
        
        self.play(Create(attractor), run_time=3)
        
        # Aggressive spirographs
        spiros = VGroup()
        for i in range(3):
            s = Spirograph(R=2 + i, r=0.5 + i * 0.3, d=1.5, color=color)
            s.scale(0.4)
            s.set_opacity(0.3)
            s.rotate(i * PI / 3)
            spiros.add(s)
        
        self.play(
            *[Create(s) for s in spiros],
            run_time=2
        )
        
        # Soul shakes
        original_pos = self.soul.get_center()
        for _ in range(5):
            offset = np.array([random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3), 0])
            self.play(
                self.soul.animate.move_to(original_pos + offset),
                run_time=0.1
            )
        self.play(self.soul.animate.move_to(original_pos), run_time=0.2)
        
        # Red pulses
        for _ in range(2):
            pulse = Circle(radius=0.5, color=color)
            pulse.set_stroke(width=3, opacity=0.8)
            self.add(pulse)
            self.play(
                pulse.animate.scale(4).set_opacity(0),
                spiros.animate.rotate(PI/6),
                run_time=1
            )
            self.remove(pulse)
        
        # Struggle
        self.play(
            Rotate(attractor, PI/2, about_point=ORIGIN),
            spiros.animate.set_opacity(0.5),
            self.soul.animate.scale(0.8),
            run_time=3
        )
        
        # Exhaustion
        self.play(
            attractor.animate.set_opacity(0.2),
            spiros.animate.set_opacity(0.1),
            self.soul.animate.scale(0.8).set_opacity(0.6),
            run_time=3
        )
        
        self.play(FadeOut(attractor, spiros), run_time=2)
    
    # =========== TRACK 14: HOLLOW ===========
    def track_14_hollow(self):
        color = SuryaColors.HOLLOW
        self.show_title(14, "hollow", '"you took my sorrow and flew it to the moon"', color)
        
        self.transition_color(color)
        self.play(self.soul.animate.move_to(ORIGIN).scale(1.3).set_opacity(1), run_time=2)
        
        # Stars return bright
        self.play(
            *[s.animate.set_opacity(random.uniform(0.4, 0.9)) for s in self.stars],
            run_time=3,
            lag_ratio=0.01
        )
        
        # Golden spiral - resolution
        spiral = GoldenSpiral(turns=5, color=color)
        spiral.scale(0.2)
        spiral.set_opacity(0)
        
        self.play(
            spiral.animate.set_opacity(0.6).scale(3),
            run_time=5
        )
        
        # Moon appears
        moon = Circle(radius=1.5, color=color)
        moon.set_fill(color, opacity=0.1)
        moon.set_stroke(color, width=2, opacity=0.5)
        moon.shift(UP * 2 + RIGHT * 3)
        
        self.play(FadeIn(moon, scale=0.5), run_time=3)
        
        # Sorrow flies to moon
        sorrow = VGroup()
        for _ in range(15):
            s = Dot(
                point=self.soul.get_center() + np.array([
                    random.uniform(-0.4, 0.4),
                    random.uniform(-0.4, 0.4),
                    0
                ]),
                radius=0.04,
                color=GREY
            )
            sorrow.add(s)
        
        self.play(FadeIn(sorrow), run_time=1)
        
        self.play(
            *[s.animate.move_to(moon.get_center() + np.array([
                random.uniform(-0.4, 0.4),
                random.uniform(-0.4, 0.4),
                0
            ])).set_opacity(0) for s in sorrow],
            self.soul.animate.scale(1.3).set_opacity(1),
            run_time=4
        )
        
        # Sacred geometry finale
        flower = FlowerOfLife(rings=2, radius=0.6, color=color)
        flower.set_opacity(0)
        
        self.play(
            flower.animate.set_opacity(0.4),
            spiral.animate.set_opacity(0.8),
            run_time=3
        )
        
        # Final radiance
        self.play(
            self.soul.animate.scale(1.5),
            flower.animate.scale(1.3),
            spiral.animate.scale(1.2),
            self.stars.animate.set_opacity(1),
            run_time=2
        )
        
        self.wait(2)
        
        # Store for finale
        self.flower = flower
        self.spiral = spiral
        self.moon = moon
    
    # =========== FINALE ===========
    def finale(self):
        # Final title
        finale_text = Text("ready to feel something?", font_size=48, 
                          color=SuryaColors.HOLLOW, slant=ITALIC)
        finale_text.shift(DOWN * 1)
        finale_text.set_opacity(0)
        
        self.play(
            finale_text.animate.set_opacity(0.8),
            self.soul.animate.scale(1.2),
            run_time=3
        )
        
        self.wait(2)
        
        # Gentle fade
        self.play(
            self.soul.animate.scale(2).set_opacity(0.3),
            self.flower.animate.set_opacity(0.1),
            self.spiral.animate.set_opacity(0.2),
            self.stars.animate.set_opacity(0.4),
            self.moon.animate.set_opacity(0.2),
            finale_text.animate.set_opacity(0.4),
            run_time=4
        )
        
        # SURYA text returns
        surya_final = Text("SURYA", font_size=80, color=SuryaColors.HOLLOW)
        surya_final.set_opacity(0)
        surya_final.shift(UP * 1)
        
        das_final = Text("DAS", font_size=24, color=WHITE)
        das_final.set_opacity(0)
        das_final.next_to(surya_final, DOWN, buff=0.5)
        
        self.play(
            surya_final.animate.set_opacity(0.6),
            das_final.animate.set_opacity(0.3),
            finale_text.animate.shift(DOWN * 0.5).set_opacity(0.2),
            run_time=3
        )
        
        self.wait(3)
        
        # Final fade to black
        self.play(
            *[mob.animate.set_opacity(0) for mob in self.mobjects],
            run_time=4
        )
        
        self.wait(1)


# ============================================================================
# INDIVIDUAL TRACK SCENES (for testing)
# ============================================================================

class TestSoul(Scene):
    """Quick test of soul orb"""
    def construct(self):
        soul = SoulOrb(color=SuryaColors.WITH_U, radius=0.3)
        self.play(FadeIn(soul, scale=0.5), run_time=2)
        
        for _ in range(3):
            self.play(soul.animate.scale(1.3), run_time=1)
            self.play(soul.animate.scale(1/1.3), run_time=1)
        
        self.wait(1)


class TestMathObjects(Scene):
    """Test mathematical objects"""
    def construct(self):
        # Spirograph
        spiro = Spirograph(R=3, r=1, d=1.5, color=PINK)
        spiro.scale(0.5)
        self.play(Create(spiro), run_time=3)
        self.wait(1)
        
        # Golden spiral
        spiral = GoldenSpiral(turns=4, color=GOLD)
        spiral.scale(0.5)
        self.play(Transform(spiro, spiral), run_time=2)
        self.wait(1)
        
        # Flower of life
        flower = FlowerOfLife(rings=2, radius=0.5, color=TEAL)
        self.play(FadeIn(flower), run_time=2)
        self.wait(1)


if __name__ == "__main__":
    print("SURYA — A Mathematical Journey Through Feeling")
    print("Render with: manim -pqh --fps 60 surya_journey.py SuryaJourney")
    print("Preview with: manim -pql surya_journey.py SuryaJourney")
