/* ============================================================
   BLESSED GROUP OF SCHOOLS — BASNPS / BASHS
   assets/css/animations.css — Animations & Scroll Reveals v1
   Depends on: assets/css/main.css
   BASNPS Simbock · BASHS Nomayos · Yaoundé, Cameroon
   www.blessedgroupofschools.com

   PHILOSOPHY:
   Every animation here serves the emotional brief:
   IMMERSION — CAPTIVITY — ENGAGEMENT.
   No decoration for decoration's sake.
   Motion guides the visitor deeper into the campus experience.
   GPU-only properties: transform + opacity exclusively.
   ============================================================ */

/* ============================================================
   TABLE OF CONTENTS
   1.  PAGE LOADER SEQUENCE
   2.  HERO ENTRANCE CHOREOGRAPHY
   3.  SCROLL REVEAL SYSTEM (IntersectionObserver hooks)
   4.  COUNTER ANIMATION
   5.  SECTION ENTRANCE VARIANTS
   6.  TICKER / MARQUEE
   7.  CARD HOVER DEPTH
   8.  GOLD ACCENT REVEALS
   9.  NAVIGATION TRANSITIONS
   10. IMAGE PARALLAX READY
   11. CTA PULSE & ATTENTION
   12. STAGGER GRID CHILDREN
   13. TEXT REVEAL (LINE BY LINE)
   14. CAMPUS PANEL INTERACTION
   15. FORM FIELD INTERACTIONS
   16. PROGRESS / LOADER BARS
   17. SKELETON LOADING
   18. REDUCED MOTION OVERRIDES
   ============================================================ */


/* ============================================================
   1. PAGE LOADER SEQUENCE
   Emotional role: first 2 seconds — "I have never seen
   a school website like this." Brand blue #0E49A5 fills
   the screen. School identity is instant.
   ============================================================ */

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

@keyframes loaderLogoReveal {
  0%   { opacity: 0; transform: scale(0.7) translateY(12px); }
  60%  { opacity: 1; transform: scale(1.05) translateY(-2px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

@keyframes loaderBarFill {
  0%   { width: 0%; opacity: 1; }
  80%  { width: 100%; opacity: 1; }
  100% { width: 100%; opacity: 0; }
}

@keyframes loaderTextFade {
  0%   { opacity: 0; transform: translateY(6px); }
  40%  { opacity: 1; transform: translateY(0); }
  80%  { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes loaderScreenExit {
  0%   { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-20px); }
}

.page-loader {
  animation: loaderFadeIn 0.3s ease forwards;
}

.page-loader__logo {
  animation: loaderLogoReveal 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both;
}

.page-loader__bar-fill {
  animation: loaderBarFill 1.6s ease-in-out 0.6s both;
}

.page-loader__tagline {
  animation: loaderTextFade 1.6s ease 0.5s both;
}

.page-loader.is-exiting {
  animation: loaderScreenExit 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  pointer-events: none;
}


/* ============================================================
   2. HERO ENTRANCE CHOREOGRAPHY
   Emotional role: 0–2 seconds — The campus appears.
   Staggered reveal: overline → headline → subheadline →
   actions → info panel. Each element builds conviction.
   ============================================================ */

@keyframes heroOverlineIn {
  from { opacity: 0; transform: translateX(-16px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes heroHeadlineIn {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes heroSubIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes heroActionsIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes heroPanelIn {
  from { opacity: 0; transform: translateX(32px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes heroImageReveal {
  from { opacity: 0; transform: scale(1.06); }
  to   { opacity: 1; transform: scale(1); }
}

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

.hero__bg img {
  animation: heroImageReveal 1.4s cubic-bezier(0.16, 1, 0.3, 1) 0.3s both;
}

.hero__overlay {
  animation: heroOverlayReveal 1s ease 0.5s both;
}

.hero .section-overline {
  animation: heroOverlineIn 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.6s both;
}

.hero__headline {
  animation: heroHeadlineIn 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.75s both;
}

.hero__subheadline {
  animation: heroSubIn 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.95s both;
}

.hero__actions {
  animation: heroActionsIn 0.7s cubic-bezier(0.16, 1, 0.3, 1) 1.1s both;
}

.hero__info-panel {
  animation: heroPanelIn 0.9s cubic-bezier(0.16, 1, 0.3, 1) 1.0s both;
}

/* Scroll indicator entrance */
@keyframes scrollIndicatorIn {
  from { opacity: 0; transform: translateX(-50%) translateY(12px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

.hero__scroll {
  animation: scrollIndicatorIn 0.8s ease 1.8s both;
}

/* Continuous scroll bounce */
@keyframes scrollBounce {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%       { transform: translateX(-50%) translateY(6px); }
}

.hero__scroll.is-ready {
  animation: scrollBounce 2.4s ease-in-out infinite;
}


/* ============================================================
   3. SCROLL REVEAL SYSTEM
   Used by assets/js/animations.js + IntersectionObserver.
   JS adds .is-revealed when element enters viewport.
   Each variant is purposeful — direction tells a story.
   ============================================================ */

/* Base state — all reveal targets start hidden */
[data-reveal] {
  opacity: 0;
  transition-property: opacity, transform;
  transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  transition-duration: 0.75s;
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none !important;
}

/* Fade up — default, most common */
[data-reveal="fade-up"] {
  transform: translateY(32px);
}

/* Fade down — used for dropdowns, results revealed from above */
[data-reveal="fade-down"] {
  transform: translateY(-24px);
}

/* Fade left — used for right-column content, info panels */
[data-reveal="fade-left"] {
  transform: translateX(32px);
}

/* Fade right — used for left-column content, campus cards */
[data-reveal="fade-right"] {
  transform: translateX(-32px);
}

/* Scale up — used for cards, images, CTA blocks */
[data-reveal="scale-up"] {
  transform: scale(0.93);
}

/* Fade only — used for backgrounds, overlays */
[data-reveal="fade"] {
  transform: none;
}

/* Slide up strong — used for section headings */
[data-reveal="slide-up"] {
  transform: translateY(48px);
}

/* Stagger delays — applied to children via JS data-stagger attribute */
[data-stagger="1"]  { transition-delay: 0.08s; }
[data-stagger="2"]  { transition-delay: 0.16s; }
[data-stagger="3"]  { transition-delay: 0.24s; }
[data-stagger="4"]  { transition-delay: 0.32s; }
[data-stagger="5"]  { transition-delay: 0.40s; }
[data-stagger="6"]  { transition-delay: 0.48s; }
[data-stagger="7"]  { transition-delay: 0.56s; }
[data-stagger="8"]  { transition-delay: 0.64s; }

/* Fast reveals — for small elements, inline badges */
[data-reveal][data-speed="fast"] {
  transition-duration: 0.45s;
}

/* Slow reveals — for full-bleed images, background sections */
[data-reveal][data-speed="slow"] {
  transition-duration: 1.1s;
}


/* ============================================================
   4. COUNTER ANIMATION
   Emotional role: 2–10 seconds — stats section.
   "They understand: this is serious, professional."
   Numbers count up from 0 to target via JS.
   Gold (#C8A55A) numbers on dark blue (#092D6B) background.
   ============================================================ */

@keyframes counterAppear {
  from {
    opacity: 0;
    transform: translateY(20px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes counterLabelAppear {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes counterGoldPulse {
  0%   { text-shadow: 0 0 0 rgba(200, 165, 90, 0); }
  50%  { text-shadow: 0 0 24px rgba(200, 165, 90, 0.4); }
  100% { text-shadow: 0 0 0 rgba(200, 165, 90, 0); }
}

[data-counter] {
  animation: counterAppear 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}

[data-counter].is-counting {
  animation: counterGoldPulse 1.5s ease-in-out;
}

.stat-item.is-revealed [data-counter] {
  animation: counterAppear 0.6s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}

.stat-item.is-revealed .stat-label {
  animation: counterLabelAppear 0.5s ease 0.4s both;
}

/* Gold underline sweep on stat reveal */
@keyframes statUnderlineSweep {
  from { width: 0; opacity: 0; }
  to   { width: 40px; opacity: 1; }
}

.stat-item.is-revealed::after {
  animation: statUnderlineSweep 0.4s ease 0.6s both;
}


/* ============================================================
   5. SECTION ENTRANCE VARIANTS
   Each section has a purposeful entrance that matches
   its emotional role in the 10-act journey.
   ============================================================ */

/* Section header — overline sweeps in from left, title rises */
@keyframes overlineSweep {
  from { transform: translateX(-24px); opacity: 0; }
  to   { transform: translateX(0); opacity: 1; }
}

@keyframes titleRise {
  from { transform: translateY(28px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

@keyframes dividerGrow {
  from { width: 0; opacity: 0; }
  to   { width: 60px; opacity: 1; }
}

.section-header.is-revealed .section-overline {
  animation: overlineSweep 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.section-header.is-revealed h2 {
  animation: titleRise 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}

.section-header.is-revealed .title-divider {
  animation: dividerGrow 0.5s ease 0.35s both;
}

.section-header.is-revealed p {
  animation: heroSubIn 0.6s ease 0.25s both;
}

/* About section — two-column entrance */
@keyframes aboutImageReveal {
  from {
    opacity: 0;
    transform: translateX(-40px) scale(0.97);
  }
  to {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}

@keyframes aboutContentReveal {
  from {
    opacity: 0;
    transform: translateX(40px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.about__image-col.is-revealed {
  animation: aboutImageReveal 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.about__content-col.is-revealed {
  animation: aboutContentReveal 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.15s both;
}

/* Campus cards — staggered entrance */
@keyframes campusCardReveal {
  from {
    opacity: 0;
    transform: translateY(40px) scale(0.97);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.card--campus.is-revealed {
  animation: campusCardReveal 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Programme cards entrance */
.prog-card.is-revealed {
  animation: campusCardReveal 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Testimonial cards */
@keyframes testimonialReveal {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.testimonial.is-revealed {
  animation: testimonialReveal 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Leader cards */
.leader-card.is-revealed {
  animation: campusCardReveal 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* News / event cards */
.news-card.is-revealed,
.event-card.is-revealed {
  animation: campusCardReveal 0.65s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Admissions section — splits in from both sides */
@keyframes admissionsLeft {
  from { opacity: 0; transform: translateX(-32px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes admissionsRight {
  from { opacity: 0; transform: translateX(32px); }
  to   { opacity: 1; transform: translateX(0); }
}

.admissions__text-col.is-revealed {
  animation: admissionsLeft 0.85s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.admissions-form.is-revealed {
  animation: admissionsRight 0.85s cubic-bezier(0.16, 1, 0.3, 1) 0.15s both;
}

/* Principal card — rises from below */
.principal-card.is-revealed {
  animation: campusCardReveal 1s cubic-bezier(0.16, 1, 0.3, 1) both;
}


/* ============================================================
   6. TICKER / MARQUEE
   Continuous horizontal scroll — gold dots punctuate items.
   Speed calculated for comfortable reading on mobile.
   ============================================================ */

@keyframes tickerScroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

.ticker__track {
  animation: tickerScroll 32s linear infinite;
}

.ticker:hover .ticker__track {
  animation-play-state: paused;
}

/* Ticker entrance — slides up from below on page load */
@keyframes tickerEntrance {
  from { opacity: 0; transform: translateY(100%); }
  to   { opacity: 1; transform: translateY(0); }
}

.ticker {
  animation: tickerEntrance 0.6s ease 1.4s both;
}


/* ============================================================
   7. CARD HOVER DEPTH
   Hover states deepen immersion — the site responds
   to touch/cursor like a living space.
   ============================================================ */

/* Gold shimmer sweep on card hover */
@keyframes goldShimmer {
  0%   { transform: translateX(-100%) skewX(-15deg); }
  100% { transform: translateX(250%) skewX(-15deg); }
}

.card--campus::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 40%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(200, 165, 90, 0.12) 50%,
    transparent 100%
  );
  z-index: 2;
  transform: translateX(-100%) skewX(-15deg);
  pointer-events: none;
}

.card--campus:hover::before {
  animation: goldShimmer 0.7s ease forwards;
}

/* Prog card image zoom — handled in components.css via transition,
   animations.css adds the scale keyframe for JS-triggered variants */
@keyframes imageZoomIn {
  from { transform: scale(1); }
  to   { transform: scale(1.06); }
}

@keyframes imageZoomOut {
  from { transform: scale(1.06); }
  to   { transform: scale(1); }
}

/* Institution badge float */
@keyframes badgeFloat {
  0%, 100% { transform: translateY(0) rotate(-3deg); }
  50%       { transform: translateY(-8px) rotate(-3deg); }
}

.institution-badge {
  animation: badgeFloat 4s ease-in-out infinite;
}

/* Gold bar — used in About section image overlay */
@keyframes goldBarDrop {
  from { transform: scaleY(0); opacity: 0; }
  to   { transform: scaleY(1); opacity: 1; }
}

.about__image-badge.is-revealed .institution-badge {
  animation: badgeFloat 4s ease-in-out 0.5s infinite;
}


/* ============================================================
   8. GOLD ACCENT REVEALS
   Gold (#C8A55A) is the reward colour — it appears as
   the visitor reaches each section, confirming they've
   arrived somewhere meaningful.
   ============================================================ */

/* Gold divider line — grows from center out */
@keyframes goldDividerGrow {
  from {
    width: 0;
    opacity: 0;
    transform: translateX(-50%);
  }
  to {
    width: 60px;
    opacity: 1;
    transform: translateX(0);
  }
}

.title-divider--center.is-revealed {
  animation: goldDividerGrow 0.5s ease both;
}

/* Gold overline sweep */
@keyframes goldOverlineSweep {
  from {
    opacity: 0;
    max-width: 0;
    overflow: hidden;
  }
  to {
    opacity: 1;
    max-width: 400px;
  }
}

/* Gold border pulse — used on blockquotes, principal card */
@keyframes goldBorderPulse {
  0%, 100% { border-color: var(--gold); }
  50%       { border-color: rgba(200, 165, 90, 0.4); }
}

.principal-card__attribution.is-revealed {
  animation: goldBorderPulse 3s ease-in-out 1s infinite;
}

/* Gold section top line reveal */
@keyframes goldTopLine {
  from { transform: scaleX(0); transform-origin: left; }
  to   { transform: scaleX(1); transform-origin: left; }
}

.stats.is-revealed::before {
  animation: goldTopLine 0.8s ease 0.3s both;
}

.footer.is-revealed::before {
  animation: goldTopLine 0.8s ease 0.2s both;
}

/* Gold dot bounce — used in timeline events */
@keyframes dotBounceIn {
  0%   { transform: scale(0); opacity: 0; }
  60%  { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

.timeline-event__dot.is-revealed {
  animation: dotBounceIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}


/* ============================================================
   9. NAVIGATION TRANSITIONS
   Nav transition from transparent to white on scroll
   is handled in JS, these keyframes support it.
   ============================================================ */

@keyframes navSlideDown {
  from { transform: translateY(-100%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

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

.nav {
  animation: navSlideDown 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.1s both;
}

/* Mobile menu slide in */
@keyframes mobileMenuIn {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}

@keyframes mobileMenuOut {
  from { transform: translateX(0); }
  to   { transform: translateX(100%); }
}

.nav__mobile-menu.is-open {
  animation: mobileMenuIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.nav__mobile-menu.is-closing {
  animation: mobileMenuOut 0.3s ease forwards;
}

/* Mobile nav links — stagger in */
@keyframes mobileLinkIn {
  from { opacity: 0; transform: translateX(20px); }
  to   { opacity: 1; transform: translateX(0); }
}

.nav__mobile-menu.is-open .nav__mobile-link {
  animation: mobileLinkIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.nav__mobile-menu.is-open .nav__mobile-link:nth-child(1) { animation-delay: 0.05s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(2) { animation-delay: 0.10s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(3) { animation-delay: 0.15s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(4) { animation-delay: 0.20s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(5) { animation-delay: 0.25s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(6) { animation-delay: 0.30s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(7) { animation-delay: 0.35s; }
.nav__mobile-menu.is-open .nav__mobile-link:nth-child(8) { animation-delay: 0.40s; }

/* Hamburger morph */
@keyframes hamburgerTopOpen {
  from { transform: translateY(0) rotate(0); }
  to   { transform: translateY(7px) rotate(45deg); }
}

@keyframes hamburgerBotOpen {
  from { transform: translateY(0) rotate(0); }
  to   { transform: translateY(-7px) rotate(-45deg); }
}


/* ============================================================
   10. IMAGE PARALLAX READY
   Parallax is applied via JS (requestAnimationFrame).
   These classes mark targets and define their CSS state.
   ============================================================ */

[data-parallax] {
  will-change: transform;
  transform: translateY(0);
  transition: none; /* JS handles interpolation */
}

/* Hero bg — slow parallax */
.hero__bg[data-parallax="slow"] {
  /* JS sets: transform: translateY(scrollY * 0.25px) */
}

/* Section images — medium parallax */
[data-parallax="medium"] {
  /* JS sets: transform: translateY(offsetFromCenter * 0.15px) */
}

/* Decorative elements — fast parallax */
[data-parallax="fast"] {
  /* JS sets: transform: translateY(offsetFromCenter * 0.35px) */
}


/* ============================================================
   11. CTA PULSE & ATTENTION
   Red (#E53E3E) CTA buttons impossible to miss.
   WhatsApp float draws the eye continuously.
   ============================================================ */

/* Admissions urgency dot pulse */
@keyframes urgencyPulse {
  0%   { transform: scale(1); opacity: 1; }
  50%  { transform: scale(1.6); opacity: 0.5; }
  100% { transform: scale(1); opacity: 1; }
}

.admissions__urgency-dot {
  animation: urgencyPulse 1.8s ease-in-out infinite;
}

/* WhatsApp float entrance */
@keyframes whatsappEntrance {
  0%   { opacity: 0; transform: translateY(20px) scale(0.8); }
  70%  { transform: translateY(-4px) scale(1.05); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

.whatsapp-float {
  animation: whatsappEntrance 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) 2.5s both;
}

/* WhatsApp continuous breath */
@keyframes whatsappBreath {
  0%, 100% {
    box-shadow: 0 4px 20px rgba(229, 62, 62, 0.45);
  }
  50% {
    box-shadow: 0 4px 36px rgba(229, 62, 62, 0.65),
                0 0 0 8px rgba(229, 62, 62, 0.1);
  }
}

.whatsapp-float.is-ready {
  animation: whatsappBreath 3s ease-in-out infinite;
}

/* CTA button ripple effect */
@keyframes btnRipple {
  0%   { transform: scale(0); opacity: 0.4; }
  100% { transform: scale(2.5); opacity: 0; }
}

.btn--primary .btn__ripple,
.btn--secondary .btn__ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.35);
  width: 100%;
  padding-top: 100%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0);
  pointer-events: none;
}

.btn--primary .btn__ripple.is-rippling,
.btn--secondary .btn__ripple.is-rippling {
  animation: btnRipple 0.6s ease-out forwards;
}

/* Back to top entrance */
@keyframes backToTopIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.back-to-top.is-visible {
  animation: backToTopIn 0.35s ease both;
}

/* Admissions section entrance — full section */
@keyframes admissionsBgReveal {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.admissions.is-revealed {
  animation: admissionsBgReveal 0.8s ease both;
}


/* ============================================================
   12. STAGGER GRID CHILDREN
   When a grid enters the viewport, its children
   cascade in with measured delay — creates a sense
   of the campus "opening up" to the visitor.
   ============================================================ */

/* Auto-stagger for direct children of revealed grids */
.stagger-grid.is-revealed > *:nth-child(1)  { animation-delay: 0.05s; }
.stagger-grid.is-revealed > *:nth-child(2)  { animation-delay: 0.12s; }
.stagger-grid.is-revealed > *:nth-child(3)  { animation-delay: 0.19s; }
.stagger-grid.is-revealed > *:nth-child(4)  { animation-delay: 0.26s; }
.stagger-grid.is-revealed > *:nth-child(5)  { animation-delay: 0.33s; }
.stagger-grid.is-revealed > *:nth-child(6)  { animation-delay: 0.40s; }
.stagger-grid.is-revealed > *:nth-child(7)  { animation-delay: 0.47s; }
.stagger-grid.is-revealed > *:nth-child(8)  { animation-delay: 0.54s; }
.stagger-grid.is-revealed > *:nth-child(9)  { animation-delay: 0.61s; }
.stagger-grid.is-revealed > *:nth-child(10) { animation-delay: 0.68s; }
.stagger-grid.is-revealed > *:nth-child(11) { animation-delay: 0.75s; }
.stagger-grid.is-revealed > *:nth-child(12) { animation-delay: 0.82s; }

/* Stagger grid items use the base [data-reveal] system */
.stagger-grid > * {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.stagger-grid.is-revealed > * {
  opacity: 1;
  transform: translateY(0);
}

/* Footer links stagger */
.footer__grid.is-revealed .footer__column:nth-child(1) { transition-delay: 0.05s; }
.footer__grid.is-revealed .footer__column:nth-child(2) { transition-delay: 0.12s; }
.footer__grid.is-revealed .footer__column:nth-child(3) { transition-delay: 0.19s; }
.footer__grid.is-revealed .footer__column:nth-child(4) { transition-delay: 0.26s; }


/* ============================================================
   13. TEXT REVEAL (LINE BY LINE)
   Used for hero headline and major section titles.
   Each line clips upward from a hidden state.
   ============================================================ */

.text-reveal {
  overflow: hidden;
}

.text-reveal__line {
  display: block;
  overflow: hidden;
}

@keyframes textLineReveal {
  from { transform: translateY(105%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

.text-reveal.is-revealed .text-reveal__line {
  animation: textLineReveal 0.75s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.text-reveal.is-revealed .text-reveal__line:nth-child(1) { animation-delay: 0s; }
.text-reveal.is-revealed .text-reveal__line:nth-child(2) { animation-delay: 0.1s; }
.text-reveal.is-revealed .text-reveal__line:nth-child(3) { animation-delay: 0.2s; }
.text-reveal.is-revealed .text-reveal__line:nth-child(4) { animation-delay: 0.3s; }

/* Word-by-word reveal variant */
.text-reveal--words .text-reveal__word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}

.text-reveal--words.is-revealed .text-reveal__word {
  animation: textLineReveal 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}


/* ============================================================
   14. CAMPUS PANEL INTERACTION
   Hover on BASNPS/BASHS panel — campus "opens" to visitor.
   ============================================================ */

@keyframes campusPanelReveal {
  from { opacity: 0; transform: translateY(32px); }
  to   { opacity: 1; transform: translateY(0); }
}

.campus-panel.is-revealed {
  animation: campusPanelReveal 0.9s cubic-bezier(0.16, 1, 0.3, 1) both;
}

/* Gold divider pulse between campuses */
@keyframes dividerPulse {
  0%, 100% { opacity: 1; transform: translateX(-50%) scaleY(1); }
  50%       { opacity: 0.6; transform: translateX(-50%) scaleY(0.95); }
}

.campus-panel__divider {
  animation: dividerPulse 3s ease-in-out infinite 1s;
}

/* Campus info content reveal on hover */
@keyframes campusInfoReveal {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.campus-panel__side:hover .campus-panel__cta {
  animation: campusInfoReveal 0.3s ease forwards;
}


/* ============================================================
   15. FORM FIELD INTERACTIONS
   Form fields in admissions section acknowledge input —
   the visitor feels the site is responsive to them.
   ============================================================ */

@keyframes fieldFocusBorder {
  from { transform: scaleX(0); transform-origin: left; }
  to   { transform: scaleX(1); transform-origin: left; }
}

/* Focus indicator line — below input */
.form-field::after {
  content: '';
  display: block;
  height: 2px;
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.25s ease;
  margin-top: -1px;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

.form-field:focus-within::after {
  transform: scaleX(1);
}

/* Form success state */
@keyframes formSuccess {
  0%   { transform: scale(0.95); opacity: 0; }
  60%  { transform: scale(1.02); }
  100% { transform: scale(1); opacity: 1; }
}

.admissions-form.is-submitted {
  animation: formSuccess 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* Input error shake */
@keyframes inputShake {
  0%, 100% { transform: translateX(0); }
  20%       { transform: translateX(-6px); }
  40%       { transform: translateX(6px); }
  60%       { transform: translateX(-4px); }
  80%       { transform: translateX(4px); }
}

.form-input.has-error,
.form-select.has-error {
  animation: inputShake 0.4s ease;
  border-color: var(--red);
}


/* ============================================================
   16. PROGRESS / LOADER BARS
   Used in page loader and form submission state.
   ============================================================ */

@keyframes progressFill {
  from { width: 0%; }
  to   { width: var(--progress-value, 100%); }
}

@keyframes progressIndeterminate {
  0%   { left: -40%; width: 40%; }
  50%  { left: 30%; width: 60%; }
  100% { left: 110%; width: 40%; }
}

.progress-bar {
  height: 4px;
  background: var(--border);
  border-radius: var(--radius-full);
  overflow: hidden;
  position: relative;
}

.progress-bar__fill {
  height: 100%;
  background: var(--brand);
  border-radius: var(--radius-full);
  transition: width 0.4s ease;
}

.progress-bar--gold .progress-bar__fill {
  background: var(--gold);
}

.progress-bar--indeterminate .progress-bar__fill {
  position: absolute;
  animation: progressIndeterminate 1.5s ease-in-out infinite;
}


/* ============================================================
   17. SKELETON LOADING
   Used while JSON data (news, events) loads via fetch.
   Maintains layout shape — no content shift on load.
   ============================================================ */

@keyframes skeletonShimmer {
  0%   { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.skeleton {
  background: linear-gradient(
    90deg,
    var(--border) 25%,
    #d0d7eb 50%,
    var(--border) 75%
  );
  background-size: 200% 100%;
  animation: skeletonShimmer 1.6s ease-in-out infinite;
  border-radius: var(--radius-md);
}

.skeleton--text {
  height: 1em;
  margin-bottom: var(--space-2);
  border-radius: var(--radius-sm);
}

.skeleton--text-sm {
  height: 0.8em;
  width: 75%;
}

.skeleton--title {
  height: 1.5em;
  width: 60%;
  margin-bottom: var(--space-3);
}

.skeleton--image {
  aspect-ratio: 16/9;
  width: 100%;
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}

.skeleton--avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  flex-shrink: 0;
}

.skeleton--card {
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  overflow: hidden;
  padding-bottom: var(--space-6);
}

.skeleton--card .skeleton--image {
  border-radius: 0;
  margin-bottom: var(--space-4);
}


/* ============================================================
   18. REDUCED MOTION OVERRIDES
   Mandatory — Cameroon mobile users on lower-end devices
   and users with vestibular disorders.
   All animations disabled. Transitions only where essential.
   ============================================================ */

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

  /* Disable all keyframe animations */
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.15ms !important;
    scroll-behavior: auto !important;
  }

  /* Reset all reveal states — show content immediately */
  [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .stagger-grid > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  /* Keep functional transitions at minimal duration */
  .nav {
    transition: background 0.1ms, box-shadow 0.1ms !important;
  }

  .btn {
    transition: background 0.1ms !important;
  }

  .modal-backdrop {
    transition: opacity 0.1ms, visibility 0.1ms !important;
  }

  /* Ticker — stop animation, show content statically */
  .ticker__track {
    animation: none !important;
    overflow-x: auto;
  }

  /* Institution badge — no float */
  .institution-badge {
    animation: none !important;
  }

  /* WhatsApp float — no breath */
  .whatsapp-float {
    animation: none !important;
  }

  /* Page loader — skip animation */
  .page-loader {
    animation: none !important;
  }

  /* Campus panel divider — no pulse */
  .campus-panel__divider {
    animation: none !important;
  }

  /* Skeleton shimmer — solid grey instead */
  .skeleton {
    animation: none !important;
    background: var(--border);
  }

  /* Gold pulse on badge — off */
  .badge--new {
    animation: none !important;
  }

  /* Urgency dot — off */
  .admissions__urgency-dot {
    animation: none !important;
  }
}

/* ============================================================
   END OF animations.css — Blessed Group of Schools
   BASNPS / BASHS — Yaoundé, Cameroon
   www.blessedgroupofschools.com
   ============================================================ */
