/* =====================================================
   BAOIAM - MOTION LAYER

   Shared animation vocabulary for the whole site, tuned to
   match the Figma prototype's smart-animate feel: things
   ease UP into place, hover states lift with an orange glow,
   and numbers count rather than pop.

   Usage:
     <div data-reveal>              fade + rise on scroll
     <div data-reveal="left">       slide in from the left
     <div data-reveal="right">      slide in from the right
     <div data-reveal="scale">      scale up from 96%
     <div data-reveal-group>        stagger direct children
     <span data-count="55" data-suffix="+">   count up
===================================================== */

/* ==========================
   SCROLL REVEAL
========================== */

[data-reveal]{
    opacity:0;
    transform:translate3d(0,28px,0);
    transition:
        opacity var(--dur-reveal) var(--ease-out),
        transform var(--dur-reveal) var(--ease-out);
    will-change:opacity,transform;
}

[data-reveal="left"]{
    transform:translate3d(-38px,0,0);
}

[data-reveal="right"]{
    transform:translate3d(38px,0,0);
}

[data-reveal="scale"]{
    transform:scale(.96);
}

[data-reveal="fade"]{
    transform:none;
}

[data-reveal].is-visible{
    opacity:1;
    transform:none;
}

/* Children of a group inherit the reveal and are offset by
   --delay, which the JS stamps on as it walks the group. */

[data-reveal-group] > *{
    opacity:0;
    transform:translate3d(0,24px,0);
    transition:
        opacity var(--dur-reveal) var(--ease-out),
        transform var(--dur-reveal) var(--ease-out);
    transition-delay:var(--delay,0ms);
    will-change:opacity,transform;
}

[data-reveal-group].is-visible > *{
    opacity:1;
    transform:none;
}

/* ==========================
   KEYFRAMES
========================== */

@keyframes baoFadeUp{
    from{opacity:0;transform:translate3d(0,24px,0);}
    to{opacity:1;transform:none;}
}

@keyframes baoFadeIn{
    from{opacity:0;}
    to{opacity:1;}
}

@keyframes baoFloat{
    0%,100%{transform:translateY(0);}
    50%{transform:translateY(-10px);}
}

@keyframes baoPulseGlow{
    0%,100%{box-shadow:0 0 0 0 var(--primary-25);}
    50%{box-shadow:0 0 0 14px rgba(255,102,0,0);}
}

@keyframes baoMarquee{
    from{transform:translate3d(0,0,0);}
    to{transform:translate3d(-50%,0,0);}
}

@keyframes baoShimmer{
    from{background-position:-140% 0;}
    to{background-position:240% 0;}
}

@keyframes baoUnderline{
    from{transform:scaleX(0);}
    to{transform:scaleX(1);}
}

/* ==========================
   ANIMATION UTILITIES
========================== */

.bao-float{
    animation:baoFloat 5.5s var(--ease-soft) infinite;
}

.bao-pulse{
    animation:baoPulseGlow 2.4s var(--ease-soft) infinite;
}

/* Auto-scrolling row. The JS duplicates the track's children
   so the -50% translate loops seamlessly. */

.bao-marquee{
    overflow:hidden;
    -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
    mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
    -webkit-backface-visibility:hidden;
    backface-visibility:hidden;
    -webkit-transform:translateZ(0);
    transform:translateZ(0);
}

/* iOS Safari WebKit fix: mask-image combined with 3D translation causes tile clipping / blank space */
@media (max-width:768px), (hover:none) and (pointer:coarse){
    .bao-marquee{
        -webkit-mask-image:none;
        mask-image:none;
    }
}

.bao-marquee-track{
    display:flex;
    width:max-content;
    animation:baoMarquee var(--marquee-duration,42s) linear infinite;
    -webkit-transform:translate3d(0,0,0);
    transform:translate3d(0,0,0);
    -webkit-backface-visibility:hidden;
    backface-visibility:hidden;
    -webkit-perspective:1000px;
    perspective:1000px;
    will-change:transform;
}

/* Left to right. The shared keyframe runs 0 -> -50%, so reversing it plays
   -50% -> 0: the same loop read the other way. It stays seamless on the
   same terms -- an even number of identical copies, one half at least as
   wide as the window -- which is what the clone pass in animations.js
   already guarantees for every row carrying .bao-marquee-track. */

.bao-marquee-track--ltr{
    animation-direction:reverse;
}

/* These rows do not pause under the cursor. Pausing reads as the animation
   glitching rather than as a deliberate stop - the row is moving, the
   pointer arrives, and it just stops dead. The internship mentors row had
   already been overridden back to running for exactly that reason; this
   makes running the default for all of them instead of the exception.

   To pause one deliberately, set animation-play-state:paused on that row's
   own :hover rather than bringing this back. */

/* ==========================
   HOVER PRIMITIVES

   The Figma file ships explicit "Hover" component variants
   for the trust cards and timeline cards — these are them.
========================== */

.bao-lift{
    transition:
        transform var(--dur-base) var(--ease-out),
        box-shadow var(--dur-base) var(--ease-out),
        border-color var(--dur-base) var(--ease-out);
}

.bao-lift:hover{
    transform:translateY(-6px);
    border-color:var(--primary-35);
    box-shadow:var(--glow);
}

.bao-press{
    transition:transform var(--dur-fast) var(--ease-out);
}

.bao-press:active{
    transform:scale(.97);
}

/* Orange underline that wipes in from the left */

.bao-underline{
    position:relative;
}

.bao-underline::after{
    content:"";
    position:absolute;
    left:0;
    bottom:-4px;
    width:100%;
    height:2px;
    background:var(--primary);
    transform:scaleX(0);
    transform-origin:left;
    transition:transform var(--dur-base) var(--ease-out);
}

.bao-underline:hover::after{
    transform:scaleX(1);
}

/* ==========================
   REDUCED MOTION

   Everything above collapses to "already there".
========================== */

@media (prefers-reduced-motion:reduce){

    [data-reveal],
    [data-reveal-group] > *{
        opacity:1 !important;
        transform:none !important;
        transition:none !important;
    }

    .bao-float,
    .bao-pulse,
    .bao-marquee-track{
        animation:none !important;
    }

    .bao-lift:hover{
        transform:none;
    }

    *,
    *::before,
    *::after{
        animation-duration:.01ms !important;
        animation-iteration-count:1 !important;
        transition-duration:.01ms !important;
        scroll-behavior:auto !important;
    }

}

/* ==========================
   CONFETTI BURST

   Driven by js/confetti.js, which fires from the hero's two CTAs.
   The layer covers the viewport but never takes a click; pieces are
   positioned by transform from the button's centre.
========================== */

.confetti-layer{
    position:fixed;
    inset:0;
    z-index:9999;
    overflow:hidden;
    pointer-events:none;
}

.confetti-piece{
    position:absolute;
    top:0;
    left:0;
    width:9px;
    height:14px;
    /* Pieces are placed with translate3d, so their own box starts at the
       layer's origin and the script moves it from there. */
    will-change:transform,opacity;
}


/* ==========================================
   MOTION ON A PHONE

   The reveal system itself is sound — one observer, unobserve after firing,
   a stagger per group, and a reduced-motion escape. What follows tunes it
   for a small screen and a thumb that scrolls faster than a mouse wheel.
========================================== */

/* Release the compositor layer once the element has arrived.

   will-change promotes an element to its own layer and holds it there for
   as long as the property is set. It is never cleared here, so a phone
   carries one layer per reveal for the life of the page — 51 of them on the
   internship page, 41 on a course page, 36 on About. That is memory the
   device is not getting back and a compositing cost on every frame of every
   scroll, for elements whose animation finished seconds ago.

   The property is only needed for the transition itself, so it goes the
   moment the element is visible. */

[data-reveal].is-visible,
[data-reveal-group].is-visible > *{
    will-change:auto;
}

@media (max-width:768px){

    /* 760ms is a considered pace on a desktop, where the eye arrives at a
       section and waits. A thumb moves faster than that: content is still
       fading up after it has been scrolled past, which reads as the page
       lagging behind the finger rather than as motion. */
    :root{
        --dur-reveal:520ms;
    }

    /* 28px of travel is a fifth of a phone's visible height in a short
       section. Less distance, same easing — it arrives rather than flies. */
    [data-reveal]{
        transform:translate3d(0,18px,0);
    }

    /* Sideways reveals belong to a wide screen, where there is room either
       side for something to come in from. On a 390px column they read as the
       layout shifting, and they fight the vertical direction of the scroll.
       Everything rises on a phone. */
    [data-reveal="left"],
    [data-reveal="right"]{
        transform:translate3d(0,18px,0);
    }

    [data-reveal="scale"]{
        transform:translate3d(0,18px,0) scale(.985);
    }

    [data-reveal-group] > *{
        transform:translate3d(0,16px,0);
    }

}

/* ==========================================
   TOUCH FEEDBACK

   Every hover state on this site is invisible on a phone. The cards lift,
   the buttons deepen, the underlines wipe in — and a finger triggers none
   of it, because there is no pointer to hover. So on desktop the whole
   interface answers back, and on a phone nothing does. That gap is most of
   the difference between the site feeling built and feeling flat.

   Two rules close it.
========================================== */

@media (hover:none){

    /* 1. The tap flash.

       Android paints a translucent grey box over any element it considers
       tappable, sized to the element's bounding box. On a rounded button it
       is a hard rectangle with square corners; on a card it covers the whole
       card. Four stylesheets already turn it off for their own components —
       this makes it the default rather than something each one remembers. */
    a,
    button,
    summary,
    label,
    input,
    select,
    textarea,
    [role="button"],
    [tabindex]{
        -webkit-tap-highlight-color:transparent;
    }

    /* 2. Press response.

       scale, not transform. They are separate properties: `scale` composes
       with whatever transform an element already carries instead of
       replacing it, so a button mid-reveal — internship.html has one that
       is a reveal target itself — keeps its translate and gets the press on
       top. A transform:scale() here would overwrite that translate and snap
       the button to its final position the instant it was touched.

       The transition sits inside :where(), which contributes no specificity
       at all. Any button that already declares its own transition therefore
       keeps it untouched and presses instantly; only buttons with nothing to
       lose pick up the eased version. It cannot override anything, which is
       the point — this is a site-wide selector reaching into twenty-odd
       stylesheets it does not own. */
    :where(button,[class*="btn"],[role="button"],.bao-lift){
        transition:scale 140ms var(--ease-out);
    }

    button:active,
    [class*="btn"]:active,
    [role="button"]:active,
    .bao-lift:active{
        scale:.97;
    }

}
