/* ==========================================================================
   motion.css — yui540-inspired micro-motion system
   --------------------------------------------------------------------------
   Shared, page-agnostic animation layer. Linked from components/head.html so
   it reaches every built page, and hand-linked into the case-studies/ deep
   dives (which the build copies verbatim, no head injection).

   Design contract (see DESIGN.md → Motion):
     - GREMLIN gets the full squash / stretch / overshoot / bounce.
     - PRO gets the SAME choreography with ease-out curves and NO overshoot
       (DESIGN.md bans bouncy/elastic motion in pro).
     - Transform + opacity only. Never animate layout.
     - EVERYTHING that moves lives inside
       @media (prefers-reduced-motion: no-preference)
       so reduced-motion users get a completely static page. The JS side
       (js/motion.js) also bails early on reduce, so armed/hidden states are
       never even applied.
   ========================================================================== */

:root {
    /* The two named curves the whole system is tuned around. */
    --ease-dramatic: cubic-bezier(0.87, 0.05, 0.02, 0.97); /* slow-in, hard-out ramp */
    --ease-pop:      cubic-bezier(0, 0.31, 0.18, 0.99);    /* snappy settle */
    /* Gremlin-only bounce-back helper (control point y > 1 → real overshoot).
       Used where a transition needs to overshoot its target: keycap release,
       hat-capacity bar. Keyframe-based motion carries its own overshoot. */
    --ease-back:     cubic-bezier(0.34, 1.56, 0.64, 1);
    /* Pro fallback curve — monotonic, never passes its endpoint. */
    --ease-out-soft: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ==========================================================================
   Shared keyframes
   ========================================================================== */

/* Chip entrance — squash/stretch pop: 0 → overshoot(1.15/1.2) → undershoot
   (0.95/0.97) → settle(1). Gremlin only. */
@keyframes chip-pop {
    0%   { opacity: 0; transform: scale(0); }
    50%  { opacity: 1; transform: scale(1.15, 1.2); }
    72%  { transform: scale(0.95, 0.97); }
    100% { opacity: 1; transform: scale(1); }
}

/* Chip entrance — pro variant: gentle fade + rise-to-full, no overshoot. */
@keyframes chip-in-pro {
    0%   { opacity: 0; transform: scale(0.9); }
    100% { opacity: 1; transform: scale(1); }
}

/* Expanding radar ring for pseudo-element ripples (tdot, nav-dot, badge). */
@keyframes ripple-ring {
    0%   { transform: scale(0.6); opacity: 0.65; }
    70%  { opacity: 0; }
    100% { transform: scale(2.4); opacity: 0; }
}

/* Single-ring box-shadow ping — for the active-badge dot, whose ::before is
   already the dot, so it can't host its own pseudo ring. */
@keyframes ring-ping {
    0%       { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7); }
    70%, 100% { box-shadow: 0 0 0 7px rgba(255, 255, 255, 0); }
}

/* Gloss that sweeps across a button on hover. */
@keyframes chase-sweep {
    0%   { transform: translateX(-160%) skewX(-16deg); }
    100% { transform: translateX(260%) skewX(-16deg); }
}

/* Count-up landing pop — gremlin: overshoot + squash then settle. */
@keyframes settle-pop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.18); }
    60%  { transform: scale(0.94); }
    100% { transform: scale(1); }
}

/* Count-up landing pop — pro: a small confident tick, no overshoot below 1. */
@keyframes settle-pop-pro {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.07); }
    100% { transform: scale(1); }
}

/* Stat-item staggered entrance. */
@keyframes stat-enter {
    0%   { opacity: 0; transform: translateY(14px); }
    100% { opacity: 1; transform: translateY(0); }
}

/* Heading underline — left-to-right chase wipe (two offset layers). The
   translateX(-50%) keeps the bar centered while scaleX animates from its
   left edge. */
@keyframes underline-wipe {
    0%   { transform: translateX(-50%) scaleX(0); }
    100% { transform: translateX(-50%) scaleX(1); }
}

/* Left-anchored variant (no centering offset). */
@keyframes wipe-x {
    0%   { transform: scaleX(0); }
    100% { transform: scaleX(1); }
}

/* Hat-capacity bar — brief scaleX overshoot as it lands (gremlin). */
@keyframes bar-settle {
    0%   { transform: scaleX(1); }
    45%  { transform: scaleX(1.05); }
    100% { transform: scaleX(1); }
}

/* ==========================================================================
   Everything below only runs when the user hasn't asked to reduce motion.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {

    /* ----------------------------------------------------------------------
       1. Chips — scroll-triggered staggered pop
       .motion-armed  → hidden initial state (added by js/motion.js on init)
       .motion-in     → play (added when the chip's group scrolls into view)
       Stagger comes from --d, assigned per-sibling via :nth-child below.
       ---------------------------------------------------------------------- */
    :is(.skill-tag, .project-tag, .honor-pill, .tchip).motion-armed {
        opacity: 0;
    }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip).motion-in {
        animation: chip-in-pro 0.4s var(--ease-out-soft) both;
        animation-delay: calc(var(--d, 0) * 45ms);
    }
    [data-mode="gremlin"] :is(.skill-tag, .project-tag, .honor-pill, .tchip).motion-in {
        animation: chip-pop 0.5s var(--ease-pop) both;
        animation-delay: calc(var(--d, 0) * 45ms);
        transform-origin: center;
    }

    /* Per-sibling stagger index. Chips live in their own container rows, so
       :nth-child restarts the cascade for each group. */
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(1)  { --d: 0; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(2)  { --d: 1; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(3)  { --d: 2; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(4)  { --d: 3; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(5)  { --d: 4; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(6)  { --d: 5; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(7)  { --d: 6; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(8)  { --d: 7; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(9)  { --d: 8; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(10) { --d: 9; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(11) { --d: 10; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(12) { --d: 11; }
    :is(.skill-tag, .project-tag, .honor-pill, .tchip):nth-child(n+13) { --d: 12; }

    /* ----------------------------------------------------------------------
       2. Stats bar — staggered entrance + settle-pop on count completion
       The container reveal (.stats-bar.reveal-in) is driven by index.html's
       existing section-reveal observer, which only arms .reveal-init when
       motion is allowed. We piggyback the child stagger off it.
       ---------------------------------------------------------------------- */
    .stats-bar.reveal-init .stat-item { opacity: 0; }
    .stats-bar.reveal-in .stat-item {
        animation: stat-enter 0.5s var(--ease-out-soft) both;
        animation-delay: calc(var(--d, 0) * 60ms);
    }
    [data-mode="gremlin"] .stats-bar.reveal-in .stat-item {
        animation-timing-function: var(--ease-pop);
    }
    .stat-item:nth-child(1) { --d: 0; }
    .stat-item:nth-child(2) { --d: 1; }
    .stat-item:nth-child(3) { --d: 2; }
    .stat-item:nth-child(4) { --d: 3; }
    .stat-item:nth-child(5) { --d: 4; }
    .stat-item:nth-child(6) { --d: 5; }
    .stat-item:nth-child(7) { --d: 6; }
    .stat-item:nth-child(8) { --d: 7; }

    /* settle-pop fires when the count-up finishes (class added in index.html). */
    .stat-number.settle-pop {
        animation: settle-pop-pro 0.4s var(--ease-out-soft);
    }
    [data-mode="gremlin"] .stat-number.settle-pop {
        animation: settle-pop 0.45s var(--ease-pop);
    }

    /* ----------------------------------------------------------------------
       3. Availability ripples (two rings, 0.4s offset)
       Targets: next-upcoming talk .tdot-next, active scroll-nav dot, and the
       "Active" status-badge dot.
       ---------------------------------------------------------------------- */

    /* Next-upcoming talk dot — true two-ring ::before/::after ripple. */
    .tdot-next::before,
    .tdot-next::after {
        content: '';
        position: absolute;
        inset: -3px;
        border-radius: 50%;
        border: 2px solid var(--accent);
        pointer-events: none;
    }
    .tdot-next::before { animation: ripple-ring 1.9s var(--ease-out-soft) infinite; }
    .tdot-next::after  { animation: ripple-ring 1.9s var(--ease-out-soft) 0.4s infinite; }

    /* Active scroll-nav dot — radar ping on the current section. Overrides the
       old opacity pulse (higher specificity via the leading attribute sel). */
    [data-mode] .nav-dot.active { animation: none; }
    .nav-dot.active::before,
    .nav-dot.active::after {
        content: '';
        position: absolute;
        inset: 0;
        border-radius: 50%;
        border: 1.5px solid var(--accent);
        pointer-events: none;
    }
    .nav-dot.active::before { animation: ripple-ring 1.9s var(--ease-out-soft) infinite; }
    .nav-dot.active::after  { animation: ripple-ring 1.9s var(--ease-out-soft) 0.4s infinite; }

    /* "Active" status badge — the ::before dot pings via box-shadow (ring 1),
       and an ::after ring expands over it (ring 2), 0.4s apart. The leading
       `html` raises specificity over each page's inline .active-badge::before
       pulse rule (which is authored later in the document). */
    html .active-badge::before {
        animation: ring-ping 2.4s var(--ease-out-soft) infinite;
        position: relative;
    }
    .active-badge::after {
        content: '';
        position: absolute;
        left: 12px;                 /* over the ::before dot (badge padding-left) */
        top: 50%;
        width: 8px;
        height: 8px;
        margin-top: -4px;
        border-radius: 50%;
        border: 1.5px solid rgba(255, 255, 255, 0.85);
        pointer-events: none;
        animation: ripple-ring 2.4s var(--ease-out-soft) 0.4s infinite;
    }
    /* Projects/case-studies inline-flow badges sit static; keep the ring lined
       up with the dot regardless. */
    .pcards .active-badge::after { left: 12px; }

    /* ----------------------------------------------------------------------
       4. Buttons — hover chase-sweep gloss
       ---------------------------------------------------------------------- */
    .hero-download-btn,
    .filter-btn {
        position: relative;
        overflow: hidden;
    }
    .hero-download-btn::after,
    .filter-btn::after {
        content: '';
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        width: 45%;
        background: linear-gradient(100deg,
            transparent 0%,
            rgba(255, 255, 255, 0.35) 50%,
            transparent 100%);
        transform: translateX(-160%) skewX(-16deg);
        pointer-events: none;
    }
    .hero-download-btn:hover::after,
    .filter-btn:hover::after {
        animation: chase-sweep 0.6s var(--ease-dramatic);
    }
    /* Pro keeps the sweep but dials the shine down (stays crisp, not shiny). */
    [data-mode="pro"] .hero-download-btn::after,
    [data-mode="pro"] .filter-btn::after {
        background: linear-gradient(100deg,
            transparent 0%,
            rgba(255, 255, 255, 0.18) 50%,
            transparent 100%);
    }

    /* ----------------------------------------------------------------------
       5. Keycaps — press-squash
       Leading `html` beats each page's inline `.view-key:active` transform.
       Gremlin releases through --ease-back for a bounce-back pop; pro eases
       out flat.
       ---------------------------------------------------------------------- */
    html .view-key {
        transition: transform 0.14s var(--ease-out-soft), box-shadow 0.1s ease;
    }
    html[data-mode="gremlin"] .view-key {
        transition: transform 0.16s var(--ease-back), box-shadow 0.1s ease;
    }
    html .view-key:active {
        transform: translateY(3px) scale(1.06, 0.88);
    }
    html[data-mode="gremlin"] .view-key:active {
        transform: translateY(3px) scale(1.1, 0.82);
    }

    /* ----------------------------------------------------------------------
       6. Heading underline chase wipe
       Applied to .section-divider and case-study detail h2s. js/motion.js
       tags the targets with .motion-wipe on init and .motion-in on scroll.
       ---------------------------------------------------------------------- */
    .motion-wipe { position: relative; }
    .motion-wipe::before,
    .motion-wipe::after {
        content: '';
        position: absolute;
        height: 3px;
        border-radius: 3px;
        pointer-events: none;
    }
    .motion-wipe::before { background: var(--accent); }
    .motion-wipe::after  { background: var(--accent); opacity: 0.45; }

    /* Centered heading (about-page .section-divider) → centered underline. */
    .section-divider.motion-wipe::before,
    .section-divider.motion-wipe::after {
        left: 50%;
        bottom: -0.35em;
        width: 72px;
        transform: translateX(-50%) scaleX(0);
        transform-origin: left center;
    }
    .section-divider.motion-wipe.motion-in::before {
        animation: underline-wipe 0.5s var(--ease-dramatic) forwards;
    }
    .section-divider.motion-wipe.motion-in::after {
        animation: underline-wipe 0.5s var(--ease-dramatic) 0.18s forwards;
    }

    /* Left-aligned heading (case-study detail h2) → underline grows from the
       left edge of the heading. */
    [data-cs-detail] .section h2.motion-wipe::before,
    [data-cs-detail] .section h2.motion-wipe::after {
        left: 0;
        bottom: -0.28em;
        width: 3.2em;
        max-width: 220px;
        transform: scaleX(0);
        transform-origin: left center;
    }
    [data-cs-detail] .section h2.motion-wipe.motion-in::before {
        animation: wipe-x 0.5s var(--ease-dramatic) forwards;
    }
    [data-cs-detail] .section h2.motion-wipe.motion-in::after {
        animation: wipe-x 0.5s var(--ease-dramatic) 0.18s forwards;
    }

    [data-mode="gremlin"] .motion-wipe.motion-in::before,
    [data-mode="gremlin"] .motion-wipe.motion-in::after {
        animation-timing-function: var(--ease-pop);
    }

    /* ----------------------------------------------------------------------
       7. Hat-capacity bar — dramatic width ramp + gremlin overshoot
       Width transition is retimed to --ease-dramatic. The `.cap-bumped` class
       (toggled in the page JS when the fill updates) fires a brief scaleX
       overshoot in gremlin only.
       ---------------------------------------------------------------------- */
    /* Leading `html` beats each page's inline `.hat-capacity-fill` transition. */
    html .hat-capacity-fill {
        transition: width 0.55s var(--ease-dramatic), background 0.3s ease;
        transform-origin: left center;
    }
    [data-mode="gremlin"] .hat-capacity-fill.cap-bumped {
        animation: bar-settle 0.5s var(--ease-back);
    }
}
