/* ═══════════════════════════════════════════════════════════════════════════
   CINEMATIC CONTINUITY — design tokens + transition primitives

   This file defines the camera language of the product. Every visible state
   change in the app references these tokens so transitions speak the same
   visual grammar: zoom in to enter, zoom out to leave, pulse to announce.

   See `.cursor/rules/standing-orders.mdc` § "Cinematic Continuity" for the
   architectural rule. Never hardcode a duration or ease curve elsewhere —
   extend this file instead.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
    /* Ease curves — the verbs of the camera */
    --cine-ease-out:        cubic-bezier(0.16, 1, 0.3, 1);
    --cine-ease-in:         cubic-bezier(0.7, 0, 0.84, 0);
    --cine-ease-out-back:   cubic-bezier(0.34, 1.56, 0.64, 1);

    /* Durations — the tempo of the camera */
    --cine-dur-fast:        180ms;
    --cine-dur-base:        360ms;
    --cine-dur-slow:        600ms;

    /* The canonical "step back" zoom amount. The home cinematic uses this
       same value (already lives inline in menu.html); the primitives below
       inherit it so all surfaces zoom by the same amount. */
    --cine-scale-from:      1.06;

    /* Accent glow used for spawn pulse. Matches the demo-card launch pulse
       so the visual language is consistent across surfaces. */
    --cine-accent-glow:     0 0 0 2px var(--accent, #f28b3c),
                            0 0 24px rgba(242, 139, 60, 0.55);
}

/* ═══════════════════════════════════════════════════════════════════════════
   Primitive 1: cine-dolly-in
   A surface arrives. Eye reads it as the camera pushing forward into a new
   space. Use for: workflow open, node-to-tool expansion, modal arrival.
   ═══════════════════════════════════════════════════════════════════════════ */
@keyframes cine-dolly-in {
    from {
        opacity: 0;
        transform: scale(var(--cine-scale-from));
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}
.cine-dolly-in {
    /* !important — cinematic primitives are dynamically applied on top of
       elements that may already declare their own `animation` shorthand
       (e.g. .pe-graph-node, demo cards). The cascade tiebreak by source
       order would otherwise let the host stylesheet win. The primitives
       ARE the canonical state-change vocabulary — they must override.
       Same convention as .pe-graph-node.pe-selected in pipeline_editor.css. */
    animation: cine-dolly-in var(--cine-dur-base) var(--cine-ease-out) both !important;
    transform-origin: center center;
    will-change: transform, opacity;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Primitive 2: cine-dolly-out
   A surface recedes. Eye tracks where it went. Use for: workflow close,
   tool-to-node collapse, modal dismiss. ALWAYS the inverse partner of any
   cine-dolly-in usage on the same surface.
   ═══════════════════════════════════════════════════════════════════════════ */
@keyframes cine-dolly-out {
    from {
        opacity: 1;
        transform: scale(1);
    }
    to {
        opacity: 0;
        transform: scale(var(--cine-scale-from));
    }
}
.cine-dolly-out {
    animation: cine-dolly-out var(--cine-dur-base) var(--cine-ease-out) both !important;
    transform-origin: center center;
    will-change: transform, opacity;
    /* Once the animation lands at opacity:0, leave it there until the
       caller flips `hidden` — prevents a flicker frame back to opacity:1. */
}

/* ═══════════════════════════════════════════════════════════════════════════
   Primitive 3: cine-pulse-spawn
   A new element is born. Slight overshoot + accent glow ring fades through
   to nothing. Use for: pipeline node spawn, asset capture into rail, result
   tile landing.
   ═══════════════════════════════════════════════════════════════════════════ */
@keyframes cine-pulse-spawn {
    0% {
        opacity: 0;
        transform: scale(0.7);
        box-shadow: var(--cine-accent-glow);
    }
    60% {
        opacity: 1;
        transform: scale(1.04);
        box-shadow: var(--cine-accent-glow);
    }
    100% {
        opacity: 1;
        transform: scale(1);
        box-shadow: 0 0 0 0 rgba(242, 139, 60, 0);
    }
}
.cine-pulse-spawn {
    animation: cine-pulse-spawn var(--cine-dur-slow) var(--cine-ease-out-back) both !important;
    transform-origin: center center;
    will-change: transform, opacity, box-shadow;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Primitive 4: cine-cross-dolly
   Two siblings swap (tab → tab, node → node). The outgoing element gets
   .cine-cross-out, the incoming gets .cine-cross-in. The 50% overlap is
   produced by JS calling them within the same frame; CSS just holds the
   timing curves.
   ═══════════════════════════════════════════════════════════════════════════ */
.cine-cross-out {
    animation: cine-dolly-out var(--cine-dur-base) var(--cine-ease-in) both !important;
    transform-origin: center center;
}
.cine-cross-in {
    animation: cine-dolly-in var(--cine-dur-base) var(--cine-ease-out) both !important;
    animation-delay: calc(var(--cine-dur-base) / 2);
    transform-origin: center center;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Stage helper — the element behaves as a GPU layer for the duration of any
   cinematic transition, then releases. Apply to surfaces that participate
   in cinematic moves so the browser doesn't repaint the whole document.
   ═══════════════════════════════════════════════════════════════════════════ */
.cine-stage {
    transform: translateZ(0);
    backface-visibility: hidden;
}

/* ═══════════════════════════════════════════════════════════════════════════
   Reduced-motion fallback — required by Cinematic Continuity § hard rule 5.
   Users who request reduced motion get opacity-only transitions at the same
   tempo. The spatial information is gone but the temporal information (a
   transition is happening) remains.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
    .cine-dolly-in,
    .cine-cross-in {
        animation: cine-fade-in var(--cine-dur-fast) linear both !important;
    }
    .cine-dolly-out,
    .cine-cross-out {
        animation: cine-fade-out var(--cine-dur-fast) linear both !important;
    }
    .cine-pulse-spawn {
        animation: cine-fade-in var(--cine-dur-fast) linear both !important;
    }

    @keyframes cine-fade-in  { from { opacity: 0; } to { opacity: 1; } }
    @keyframes cine-fade-out { from { opacity: 1; } to { opacity: 0; } }
}
