/* =============================================================================
   SitePilot demo template — animation catalogue

   Every effect is written ONCE but addressed two ways:

     [data-anim="fade-up"] .anim   → the site-wide choice, switched by setting a
                                     single attribute on <html>. No JS has to
                                     touch individual elements, so changing the
                                     animation is instant even on a long page.
     .a-fade-up                    → the same effect on one isolated element,
                                     which is how design.html shows a specimen
                                     of each without adopting it site-wide.

   Elements start in the "out" state and are released by the `in` class, which
   script.js adds on scroll or on load. That is a class rather than an
   animation-delay on purpose: if the script never runs, the .no-js rule at the
   bottom leaves everything visible instead of leaving a blank page.
   ========================================================================== */

.anim, .animx {
    transition:
        opacity   var(--anim-duration) var(--anim-ease),
        transform var(--anim-duration) var(--anim-ease),
        filter    var(--anim-duration) var(--anim-ease),
        clip-path var(--anim-duration) var(--anim-ease);
    will-change: opacity, transform;
}

/* --- 1. fade — opacity only, the quietest option ------------------------- */
[data-anim="fade"] .anim, .a-fade { opacity: 0; }
[data-anim="fade"] .anim.in, .a-fade.in { opacity: 1; }

/* --- 2. fade-up — the default; content arrives from below ---------------- */
[data-anim="fade-up"] .anim, .a-fade-up { opacity: 0; transform: translateY(26px); }
[data-anim="fade-up"] .anim.in, .a-fade-up.in { opacity: 1; transform: none; }

/* --- 3. fade-down — from above; reads as a menu dropping in -------------- */
[data-anim="fade-down"] .anim, .a-fade-down { opacity: 0; transform: translateY(-26px); }
[data-anim="fade-down"] .anim.in, .a-fade-down.in { opacity: 1; transform: none; }

/* --- 4/5. slide — strong horizontal entrance ----------------------------- */
[data-anim="slide-left"] .anim, .a-slide-left { opacity: 0; transform: translateX(-48px); }
[data-anim="slide-left"] .anim.in, .a-slide-left.in { opacity: 1; transform: none; }

[data-anim="slide-right"] .anim, .a-slide-right { opacity: 0; transform: translateX(48px); }
[data-anim="slide-right"] .anim.in, .a-slide-right.in { opacity: 1; transform: none; }

/* --- 6/7. zoom — grows in, or settles back from slightly oversized ------- */
[data-anim="zoom-in"] .anim, .a-zoom-in { opacity: 0; transform: scale(.92); }
[data-anim="zoom-in"] .anim.in, .a-zoom-in.in { opacity: 1; transform: none; }

[data-anim="zoom-out"] .anim, .a-zoom-out { opacity: 0; transform: scale(1.07); }
[data-anim="zoom-out"] .anim.in, .a-zoom-out.in { opacity: 1; transform: none; }

/* --- 8. blur-in — soft focus pull. Costs more to paint than the others,
       so it is worth previewing on a phone before choosing it. ------------- */
[data-anim="blur-in"] .anim, .a-blur-in { opacity: 0; filter: blur(12px); }
[data-anim="blur-in"] .anim.in, .a-blur-in.in { opacity: 1; filter: blur(0); }

/* --- 9. flip-up — hinges up from its own bottom edge --------------------- */
[data-anim="flip-up"] .anim, .a-flip-up {
    opacity: 0;
    transform: perspective(900px) rotateX(-16deg) translateY(20px);
    transform-origin: 50% 100%;
}
[data-anim="flip-up"] .anim.in, .a-flip-up.in { opacity: 1; transform: none; }

/* --- 10. tilt-in — swings in around the vertical axis -------------------- */
[data-anim="tilt-in"] .anim, .a-tilt-in {
    opacity: 0;
    transform: perspective(900px) rotateY(14deg) translateX(24px);
    transform-origin: 0% 50%;
}
[data-anim="tilt-in"] .anim.in, .a-tilt-in.in { opacity: 1; transform: none; }

/* --- 11. rise-rotate — a slight rotation off true, then straightens ------ */
[data-anim="rise-rotate"] .anim, .a-rise-rotate { opacity: 0; transform: translateY(30px) rotate(-2.2deg); }
[data-anim="rise-rotate"] .anim.in, .a-rise-rotate.in { opacity: 1; transform: none; }

/* --- 12. mask-up — wipes upward behind its own edge. The most "designed"
       of the set; best on headings, heavy on a page of twenty cards. ------- */
[data-anim="mask-up"] .anim, .a-mask-up {
    opacity: 0;
    clip-path: inset(100% 0 0 0);
    transform: translateY(12px);
}
[data-anim="mask-up"] .anim.in, .a-mask-up.in { opacity: 1; clip-path: inset(0 0 0 0); transform: none; }

/* --- 13. none — content is simply present. Not a cop-out: on a site people
       visit to get something done, this is often the right answer. -------- */
[data-anim="none"] .anim, .a-none { opacity: 1; transform: none; filter: none; }

/* =============================================================================
   Card hover
   ========================================================================== */

[data-card-hover="lift"] .card:hover, .demo-card.ch-lift:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lift);
}
[data-card-hover="glow"] .card:hover, .demo-card.ch-glow:hover {
    box-shadow: 0 0 0 1px var(--brand), 0 12px 32px var(--brand-ring);
    border-color: var(--brand);
}
[data-card-hover="border"] .card:hover, .demo-card.ch-border:hover {
    border-color: var(--brand);
    box-shadow: inset 0 -3px 0 var(--brand), var(--shadow);
}
[data-card-hover="tilt"] .card:hover, .demo-card.ch-tilt:hover {
    transform: perspective(800px) rotateX(3deg) rotateY(-3deg) translateY(-2px);
    box-shadow: var(--shadow-lift);
}
[data-card-hover="scale"] .card:hover, .demo-card.ch-scale:hover {
    transform: scale(1.025);
    box-shadow: var(--shadow-lift);
}
/* data-card-hover="none" intentionally has no rule. */

/* =============================================================================
   Button hover
   ========================================================================== */

[data-btn-hover="deepen"] .btn:not(.btn-ghost):hover, .demo-btn.bh-deepen:hover {
    background: var(--brand-deep);
}
[data-btn-hover="lift"] .btn:not(.btn-ghost):hover, .demo-btn.bh-lift:hover {
    background: var(--brand-deep);
    transform: translateY(-2px);
    box-shadow: 0 8px 18px var(--brand-ring);
}
[data-btn-hover="grow"] .btn:not(.btn-ghost):hover, .demo-btn.bh-grow:hover {
    background: var(--brand-deep);
    transform: scale(1.04);
}
/* A light sweep across the face. The pseudo-element is always present so there
   is nothing to lay out on hover — only its position changes. */
[data-btn-hover="shine"] .btn:not(.btn-ghost)::after,
.demo-btn.bh-shine::after {
    content: "";
    position: absolute;
    top: 0; bottom: 0; left: -60%;
    width: 40%;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .45), transparent);
    transform: skewX(-18deg);
    transition: left .55s ease;
}
[data-btn-hover="shine"] .btn:not(.btn-ghost):hover::after,
.demo-btn.bh-shine:hover::after { left: 120%; }

[data-btn-hover="outline"] .btn:not(.btn-ghost):hover, .demo-btn.bh-outline:hover {
    background: transparent;
    color: var(--brand);
    border-color: var(--brand);
    box-shadow: inset 0 0 0 1px var(--brand);
}
/* data-btn-hover="none" intentionally has no rule. */

/* =============================================================================
   Hero rotator
   Each phrase fades or moves fully out before the next comes in — a crossfade
   overlaps two sentences mid-sentence and reads as a glitch. `.showing` is the
   visible state; script.js waits out --rotator-fade before setting it.
   ========================================================================== */

.rotator-item {
    opacity: 0;
    transition:
        opacity   var(--rotator-fade, 1.6s) ease-in-out,
        transform var(--rotator-fade, 1.6s) ease-in-out,
        filter    var(--rotator-fade, 1.6s) ease-in-out;
}
.rotator-item.showing { opacity: 1; transform: none; filter: none; }

[data-rotator="rise"]  .rotator-item { transform: translateY(24px); }
[data-rotator="drop"]  .rotator-item { transform: translateY(-24px); }
[data-rotator="slide"] .rotator-item { transform: translateX(36px); }
[data-rotator="zoom"]  .rotator-item { transform: scale(.9); }
[data-rotator="blur"]  .rotator-item { filter: blur(10px); }
[data-rotator="flip"]  .rotator-item { transform: perspective(900px) rotateX(-40deg); transform-origin: 50% 100%; }
/* data-rotator="fade" is the bare .rotator-item state above. */

/* =============================================================================
   Marquee
   ========================================================================== */

.marquee-track { animation: marquee var(--marquee-speed) linear infinite; }
@keyframes marquee { to { transform: translateX(-100%); } }

/* Off by default — see DEFAULTS in designer.js. The specimen on design.html is
   exempt: it is what the client is deciding about, so hiding it when the answer
   is currently "off" would leave an empty section explaining a thing you cannot
   see. */
[data-marquee="off"] .marquee:not(.marquee-demo) { display: none; }

/* =============================================================================
   Fallbacks
   ========================================================================== */

/* Belt and braces: if JavaScript never runs, nothing stays hidden. */
.no-js .anim { opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important; }
.no-js .rotator-item { opacity: 0; }
.no-js .rotator-item.showing { opacity: 1; }

/* A client who has asked their OS for less motion gets a static page whatever
   is selected here. The panel's choices are a presentation decision; this is an
   accessibility one, and it wins. */
@media (prefers-reduced-motion: reduce) {
    .anim, .animx {
        opacity: 1 !important;
        transform: none !important;
        filter: none !important;
        clip-path: none !important;
        transition: none !important;
    }
    .marquee-track { animation: none; padding-left: 0; }
    .card:hover, .btn:hover { transform: none !important; }
    .rotator-item { transition: none; }
}
