/* ==========================================================================
   CSS Animation Snippets  v1.0.0
   Free, copy-paste animations for business websites.
   License: MIT (free for personal and commercial use)
   --------------------------------------------------------------------------
   HOW TO USE
   1. Link this file in your <head>:
        <link rel="stylesheet" href="css/animations.css">
   2. Add a class to any element, e.g. <h1 class="anim-slide-up">Hello</h1>
   3. (Optional) For scroll animations, also load js/scroll-reveal.js
   --------------------------------------------------------------------------
   CONTENTS
   0. Settings (custom properties you can change)
   1. Entrance animations   (play once when the page loads)
   2. Scroll reveal         (play when the element scrolls into view)
   3. Hover effects         (buttons, cards, links)
   4. Loaders               (spinners, dots, progress bar, skeleton)
   5. Attention seekers     (pulse, ping, shake, bounce, wiggle)
   6. Text effects          (typewriter, animated gradient)
   7. Utilities             (delays, speeds, repeat)
   8. Accessibility         (respects "reduce motion" settings)
   ========================================================================== */


/* ==========================================================================
   0. SETTINGS
   Change these once here to restyle every animation on your site,
   or override them on a single element:
     <div class="anim-slide-up" style="--anim-duration: 1s">...</div>
   ========================================================================== */
:root {
  --anim-duration: 600ms;                         /* how long one animation lasts   */
  --anim-delay: 0ms;                              /* wait before it starts          */
  --anim-ease: cubic-bezier(0.22, 1, 0.36, 1);    /* "ease-out-quint": fast, then soft landing */
  --anim-distance: 24px;                          /* how far slide animations travel */
  --anim-accent: #6366f1;                         /* brand color used by effects    */
  --anim-accent-contrast: #ffffff;                /* text color on top of accent    */
}


/* ==========================================================================
   1. ENTRANCE ANIMATIONS
   Run once as soon as the element appears on the page.
   "both" = keep the starting style before it runs AND the end style after.
   ========================================================================== */
@keyframes anim-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes anim-slide-up {
  from { opacity: 0; transform: translateY(var(--anim-distance)); }
  to   { opacity: 1; transform: none; }
}
@keyframes anim-slide-down {
  from { opacity: 0; transform: translateY(calc(var(--anim-distance) * -1)); }
  to   { opacity: 1; transform: none; }
}
@keyframes anim-slide-left {
  from { opacity: 0; transform: translateX(var(--anim-distance)); }
  to   { opacity: 1; transform: none; }
}
@keyframes anim-slide-right {
  from { opacity: 0; transform: translateX(calc(var(--anim-distance) * -1)); }
  to   { opacity: 1; transform: none; }
}
@keyframes anim-zoom-in {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: none; }
}
@keyframes anim-flip-in {
  from { opacity: 0; transform: perspective(600px) rotateX(-70deg); }
  to   { opacity: 1; transform: none; }
}

.anim-fade-in     { animation: anim-fade-in     var(--anim-duration) var(--anim-ease) var(--anim-delay) both; }
.anim-slide-up    { animation: anim-slide-up    var(--anim-duration) var(--anim-ease) var(--anim-delay) both; }
.anim-slide-down  { animation: anim-slide-down  var(--anim-duration) var(--anim-ease) var(--anim-delay) both; }
.anim-slide-left  { animation: anim-slide-left  var(--anim-duration) var(--anim-ease) var(--anim-delay) both; }
.anim-slide-right { animation: anim-slide-right var(--anim-duration) var(--anim-ease) var(--anim-delay) both; }
.anim-zoom-in     { animation: anim-zoom-in     var(--anim-duration) var(--anim-ease) var(--anim-delay) both; }
.anim-flip-in     { animation: anim-flip-in     var(--anim-duration) var(--anim-ease) var(--anim-delay) both; transform-origin: top center; }


/* ==========================================================================
   2. SCROLL REVEAL  (requires js/scroll-reveal.js)
   Usage:  <section data-reveal="up">...</section>
   Types:  fade | up | down | left | right | zoom
   The ".anim-js" class is added to <html> by the script. If JavaScript is
   off or fails, nothing is hidden, so content is never lost.
   ========================================================================== */
.anim-js [data-reveal] {
  opacity: 0;
  transition:
    opacity   var(--anim-duration) var(--anim-ease) var(--anim-delay),
    transform var(--anim-duration) var(--anim-ease) var(--anim-delay);
  will-change: opacity, transform;
}
.anim-js [data-reveal="up"]    { transform: translateY(var(--anim-distance)); }
.anim-js [data-reveal="down"]  { transform: translateY(calc(var(--anim-distance) * -1)); }
.anim-js [data-reveal="left"]  { transform: translateX(var(--anim-distance)); }
.anim-js [data-reveal="right"] { transform: translateX(calc(var(--anim-distance) * -1)); }
.anim-js [data-reveal="zoom"]  { transform: scale(0.9); }

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


/* ==========================================================================
   3. HOVER EFFECTS
   Each one also reacts to keyboard focus (:focus-visible) so people who
   navigate with Tab get the same feedback as mouse users.
   ========================================================================== */

/* Lift: card rises and its shadow grows */
.anim-hover-lift {
  transition: transform 250ms var(--anim-ease), box-shadow 250ms var(--anim-ease);
}
.anim-hover-lift:hover,
.anim-hover-lift:focus-visible {
  transform: translateY(-6px);
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.14), 0 4px 10px rgba(0, 0, 0, 0.08);
}

/* Grow: gently scales up */
.anim-hover-grow {
  transition: transform 250ms var(--anim-ease);
}
.anim-hover-grow:hover,
.anim-hover-grow:focus-visible {
  transform: scale(1.05);
}

/* Underline: a line draws in from the left (great for nav links) */
.anim-underline {
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 2px;
  transition: background-size 300ms var(--anim-ease);
  padding-bottom: 2px;
}
.anim-underline:hover,
.anim-underline:focus-visible {
  background-size: 100% 2px;
}

/* Shine: a light streak sweeps across (buttons, images, badges) */
.anim-shine {
  position: relative;
  overflow: hidden;
}
.anim-shine::after {
  content: "";
  position: absolute;
  top: 0;
  left: -75%;
  width: 50%;
  height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.45), transparent);
  transform: skewX(-20deg);
  pointer-events: none;            /* the streak never blocks clicks */
}
.anim-shine:hover::after,
.anim-shine:focus-visible::after {
  left: 125%;
  transition: left 700ms ease;
}

/* Fill: background color sweeps in from the left (outline buttons) */
.anim-btn-fill {
  color: var(--anim-accent);
  border: 2px solid var(--anim-accent);
  background: linear-gradient(var(--anim-accent), var(--anim-accent)) no-repeat left / 0% 100%;
  transition: background-size 350ms var(--anim-ease), color 350ms var(--anim-ease);
}
.anim-btn-fill:hover,
.anim-btn-fill:focus-visible {
  background-size: 100% 100%;
  color: var(--anim-accent-contrast);
}


/* ==========================================================================
   4. LOADERS
   Show while something is loading. Add role="status" and a hidden label
   in your HTML so screen readers announce "Loading" (see README).
   ========================================================================== */
@keyframes anim-spin   { to { transform: rotate(360deg); } }
@keyframes anim-dot    { 0%, 80%, 100% { transform: scale(0.4); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
@keyframes anim-bar    { from { left: -40%; } to { left: 100%; } }
@keyframes anim-shimmer { from { background-position: 150% 0; } to { background-position: -50% 0; } }

/* Spinner: a ring with one colored side */
.anim-spinner {
  display: inline-block;
  width: 2.5em;
  height: 2.5em;
  border: 0.3em solid rgba(127, 127, 127, 0.25);
  border-top-color: var(--anim-accent);
  border-radius: 50%;
  animation: anim-spin 0.8s linear infinite;
}

/* Dots: three bouncing dots. HTML: <span class="anim-dots"><i></i><i></i><i></i></span> */
.anim-dots {
  display: inline-flex;
  gap: 0.4em;
}
.anim-dots > * {
  width: 0.7em;
  height: 0.7em;
  border-radius: 50%;
  background: var(--anim-accent);
  animation: anim-dot 1.2s ease-in-out infinite;
}
.anim-dots > *:nth-child(2) { animation-delay: 0.15s; }
.anim-dots > *:nth-child(3) { animation-delay: 0.3s; }

/* Progress bar: an "indeterminate" bar that slides back and forth */
.anim-progress {
  position: relative;
  overflow: hidden;
  height: 4px;
  border-radius: 4px;
  background: rgba(127, 127, 127, 0.2);
}
.anim-progress::before {
  content: "";
  position: absolute;
  top: 0;
  left: -40%;
  width: 40%;
  height: 100%;
  border-radius: inherit;
  background: var(--anim-accent);
  animation: anim-bar 1.2s ease-in-out infinite;
}

/* Skeleton: grey placeholder blocks with a moving shimmer */
.anim-skeleton {
  border-radius: 6px;
  background: linear-gradient(90deg,
              rgba(127, 127, 127, 0.15) 25%,
              rgba(127, 127, 127, 0.30) 50%,
              rgba(127, 127, 127, 0.15) 75%);
  background-size: 200% 100%;
  animation: anim-shimmer 1.4s linear infinite;
  color: transparent !important;   /* hides any placeholder text inside */
}


/* ==========================================================================
   5. ATTENTION SEEKERS
   Use sparingly: one per screen is plenty. Pulse and ping loop forever,
   shake/bounce/wiggle play once (add .anim-infinite to loop them).
   ========================================================================== */
@keyframes anim-pulse  { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }
@keyframes anim-ping   { 0% { transform: scale(1); opacity: 0.7; } 80%, 100% { transform: scale(2.2); opacity: 0; } }
@keyframes anim-shake  {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(3px); }
  30%, 50%, 70% { transform: translateX(-6px); }
  40%, 60% { transform: translateX(6px); }
}
@keyframes anim-bounce {
  0%, 20%, 53%, 100% { transform: translateY(0); }
  40%, 43% { transform: translateY(-18px); }
  70% { transform: translateY(-8px); }
  90% { transform: translateY(-3px); }
}
@keyframes anim-wiggle {
  0%, 100% { transform: rotate(0); }
  20% { transform: rotate(-10deg); }
  40% { transform: rotate(8deg); }
  60% { transform: rotate(-5deg); }
  80% { transform: rotate(3deg); }
}

.anim-pulse  { animation: anim-pulse 2s ease-in-out infinite; }
.anim-shake  { animation: anim-shake 0.6s cubic-bezier(.36, .07, .19, .97) var(--anim-delay) both; }
.anim-bounce { animation: anim-bounce 1s ease var(--anim-delay) both; transform-origin: center bottom; }
.anim-wiggle { animation: anim-wiggle 0.7s ease-in-out var(--anim-delay) both; }

/* Ping: a ring ripples out behind a badge/dot ("New!", "Live", notifications) */
.anim-ping {
  position: relative;
}
.anim-ping::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: inherit;
  z-index: -1;
  animation: anim-ping 1.6s cubic-bezier(0, 0, 0.2, 1) infinite;
}


/* ==========================================================================
   6. TEXT EFFECTS
   ========================================================================== */

/* Typewriter: text types itself out with a blinking caret.
   Set --anim-chars to the number of characters in your text:
     <h1 class="anim-typewriter" style="--anim-chars: 18">Welcome to Bloom!</h1>
   Works best with a monospace font and a single line of text. */
@keyframes anim-type  { from { width: 0; } }
@keyframes anim-caret { 50% { border-color: transparent; } }
.anim-typewriter {
  --anim-chars: 20;
  display: inline-block;
  width: calc(var(--anim-chars) * 1ch + 0.1em);
  white-space: nowrap;
  overflow: hidden;
  border-right: 0.08em solid currentColor;
  animation:
    anim-type  calc(var(--anim-chars) * 80ms) steps(var(--anim-chars)) var(--anim-delay) both,
    anim-caret 0.8s step-end infinite;
}

/* Gradient text: a slowly moving color gradient inside the letters */
@keyframes anim-gradient { to { background-position: 200% center; } }
.anim-gradient-text {
  background: linear-gradient(90deg, var(--anim-accent), #ec4899, #f59e0b, var(--anim-accent));
  background-size: 200% auto;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: anim-gradient 4s linear infinite;
}


/* ==========================================================================
   7. UTILITIES
   ========================================================================== */
.anim-delay-1 { --anim-delay: 100ms; }
.anim-delay-2 { --anim-delay: 200ms; }
.anim-delay-3 { --anim-delay: 300ms; }
.anim-delay-4 { --anim-delay: 400ms; }
.anim-delay-5 { --anim-delay: 500ms; }

.anim-fast { --anim-duration: 300ms; }
.anim-slow { --anim-duration: 1200ms; }

.anim-infinite { animation-iteration-count: infinite !important; }


/* ==========================================================================
   8. ACCESSIBILITY: REDUCED MOTION
   Some visitors turn on "Reduce motion" in their phone/computer settings
   because movement can cause dizziness or nausea. When that setting is on:
   - decorative motion is switched off (elements simply appear),
   - loaders keep running, but slower, so users still know something is happening.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  [class*="anim-"]:not(.anim-spinner):not(.anim-dots):not(.anim-progress):not(.anim-skeleton),
  [class*="anim-"]:not(.anim-progress)::before,
  [class*="anim-"]::after,
  [data-reveal] {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    animation-delay: 0ms !important;
  }
  .anim-js [data-reveal] { transform: none; }
  .anim-spinner,
  .anim-dots > *,
  .anim-progress::before,
  .anim-skeleton { animation-duration: 3s !important; }
}
