/* ==========================================================================
   Orvixa — Motion & Micro-interaction Layer
   Loaded after style.css. Pure additive polish: preloader, scroll choreo,
   card/button/form hover states, glass touches. No new markup dependency
   beyond the small, mechanical additions described in each section.
   Everything here inherits the global prefers-reduced-motion kill-switch
   already defined in style.css (forces all durations to ~0), and the
   JS-driven pieces (preloader, ripple, magnetic hover, parallax, FAQ
   animation) additionally no-op via a matchMedia check in motion.js.
   ========================================================================== */

/* ---------- 1. Preloader ---------- */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  transition: opacity 0.4s var(--ease), visibility 0.4s var(--ease);
}
.preloader-mark {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 3px solid var(--border);
  border-top-color: var(--blue);
  animation: preloaderSpin 0.8s linear infinite;
}
.preloader.done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
@keyframes preloaderSpin { to { transform: rotate(360deg); } }

/* ---------- 2. Page entrance fade ----------
   .motion-loading is present in each page's initial HTML (so there's no flash
   before this stylesheet loads) and is removed by motion.js shortly after
   DOMContentLoaded. A <noscript> override (added alongside the existing
   .reveal noscript fallback) forces opacity:1 if JS never runs, so content
   is never permanently hidden without JavaScript. */
body.motion-loading > *:not(.preloader) { opacity: 0; }
body:not(.motion-loading) > * {
  opacity: 1;
  transition: opacity 0.5s var(--ease-out);
}

/* ---------- 3. Scroll progress bar ---------- */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 1002;
  background: linear-gradient(90deg, var(--blue), var(--cyan));
  transform: scaleX(0);
  transform-origin: 0 50%;
  will-change: transform;
}

/* ---------- 4. Reveal variants (piggyback on the existing .reveal + IO in script.js) ---------- */
.reveal-left {
  transform: translateX(-28px);
}
.reveal-left.in { transform: none; }
.reveal-right {
  transform: translateX(28px);
}
.reveal-right.in { transform: none; }
.reveal-scale {
  transform: scale(0.94);
}
.reveal-scale.in { transform: none; }
/* Extend the stagger delay past the existing 8-item cap for larger grids (e.g. industries.html). */
.reveal-stagger .reveal:nth-child(9) { transition-delay: 0.64s; }
.reveal-stagger .reveal:nth-child(10) { transition-delay: 0.72s; }
.reveal-stagger .reveal:nth-child(11) { transition-delay: 0.8s; }
.reveal-stagger .reveal:nth-child(12) { transition-delay: 0.88s; }
.reveal-stagger .reveal:nth-child(n+13) { transition-delay: 0.92s; }

/* ---------- 5. Parallax (translateY driven by motion.js, only while IO reports the element visible) ---------- */
.parallax { will-change: transform; }

/* ---------- 6. Nav: hover underline + active state ---------- */
.nav-links > a {
  position: relative;
}
.nav-links > a::after {
  content: "";
  position: absolute;
  left: 0.85rem;
  right: 0.85rem;
  bottom: 0.3rem;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--blue), var(--cyan));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.25s var(--ease);
}
.nav-links > a:hover::after,
.nav-links > a.active::after {
  transform: scaleX(1);
}

/* ---------- 7. Mobile nav: smooth slide instead of instant display toggle ---------- */
.mobile-nav {
  display: block;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-10px);
  pointer-events: none;
  transition: opacity 0.28s var(--ease), transform 0.28s var(--ease), visibility 0.28s;
}
.mobile-nav.open {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
}
.mobile-sub {
  display: block;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  padding-top: 0;
  padding-bottom: 0;
  transition: max-height 0.35s var(--ease), opacity 0.25s var(--ease), padding 0.35s var(--ease);
}
.mobile-sub.open {
  max-height: 640px;
  opacity: 1;
  padding: 0.5rem 0 0.75rem 1rem;
}
/* Frosted-glass touch on the desktop mega-menu panel, consistent with the
   header's existing blur. Kept nearly opaque (not the header's ~0.85) —
   these panels sit directly over large bold heading text, and a lighter
   opacity let that text visibly ghost/smear through the blur. */
.nav-dropdown-panel {
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(8px) saturate(1.2);
  -webkit-backdrop-filter: blur(8px) saturate(1.2);
}
.mobile-nav {
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* ---------- 8. Buttons: richer hover, ripple, magnetic support ---------- */
.btn {
  position: relative;
  overflow: hidden;
}
.btn:hover {
  transform: translateY(-2px) scale(1.015);
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.1);
}
.btn:active {
  transform: translateY(0) scale(0.99);
}
.btn-ripple {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
  transform: translate(-50%, -50%) scale(0);
  pointer-events: none;
  animation: btnRipple 0.6s var(--ease-out);
}
.btn-outline-navy .btn-ripple,
.btn-ghost .btn-ripple,
.btn-secondary .btn-ripple {
  background: rgba(0, 102, 255, 0.18);
}
@keyframes btnRipple {
  to { transform: translate(-50%, -50%) scale(18); opacity: 0; }
}
/* .btn-magnetic itself needs no rules — script.js drives its transform (and
   toggles a transition only on release) directly via inline style so the
   follow tracks the pointer instantly and only springs back smoothly. */

/* ---------- 9. Cards: lift, border-glow, gradient sheen, image/icon zoom ---------- */
.industry-card,
.why-card,
.module-card,
.blog-card,
.leader-card,
.security-card,
.cost-card {
  position: relative;
}
.why-card,
.module-card {
  overflow: hidden;
}
.industry-card::before,
.why-card::before,
.module-card::before,
.blog-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(0, 102, 255, 0.06), rgba(0, 209, 255, 0.04) 40%, transparent 70%);
  opacity: 0;
  transition: opacity 0.35s var(--ease);
  pointer-events: none;
}
.industry-card:hover::before,
.why-card:hover::before,
.module-card:hover::before,
.blog-card:hover::before {
  opacity: 1;
}
.industry-card:hover,
.why-card:hover {
  box-shadow: var(--shadow-lg), 0 0 0 1px rgba(0, 102, 255, 0.16);
}
.module-card:hover {
  box-shadow: var(--shadow-md), 0 0 0 1px rgba(0, 102, 255, 0.14);
}
.leader-card,
.security-card,
.cost-card {
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease), border-color 0.3s var(--ease);
}
.leader-card:hover,
.security-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
  border-color: rgba(0, 102, 255, 0.25);
}
.cost-card.winner:hover,
.cost-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}
.testimonial-card {
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease), border-color 0.3s var(--ease);
}
.testimonial-card:hover {
  border-color: rgba(0, 102, 255, 0.25);
}
.leader-avatar,
.avatar {
  transition: transform 0.3s var(--ease);
}
.leader-card:hover .leader-avatar {
  transform: scale(1.06);
}
/* Blog card image placeholder zoom, mirroring the existing why-card icon zoom pattern. */
.blog-card-img {
  overflow: hidden;
}
.blog-card-img > * { transition: transform 0.4s var(--ease); }
.blog-card:hover .blog-card-img > * { transform: scale(1.08); }
.blog-card:hover .blog-card-img { filter: saturate(1.1); }
/* Integration logo pills already have a hover transition in style.css; add a soft glow to match. */
.integration-logo:hover {
  box-shadow: var(--shadow-md);
}

/* ---------- 10. Images: fade-in on load, ready-for-future skeleton ----------
   Progressive enhancement: motion.js opts individual <img> elements INTO the
   fade (adds .motion-fade + sets initial opacity via JS) rather than CSS
   hiding all images by default — so images stay visible if JS never runs. */
.motion-fade {
  opacity: 0;
  transition: opacity 0.4s var(--ease-out);
}
.motion-fade.motion-loaded {
  opacity: 1;
}
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--surface);
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, 0.55) 50%, transparent 70%);
  transform: translateX(-100%);
  animation: skeletonShimmer 1.4s ease-in-out infinite;
}
@keyframes skeletonShimmer { to { transform: translateX(100%); } }

/* ---------- 11. Counters & progress: linear bars + donut entrance ----------
   No CSS trick needed for the "start empty" state: motion.js reads each bar's
   author-set inline width, temporarily zeroes it via JS (same element, same
   inline style — no !important games, no no-JS trap), then restores it once
   the bar scrolls into view so this transition animates the fill in. If JS
   never runs, the bars simply keep their original static width. */
.hd-bar > i,
.app-progress-row .track > i {
  transition: width 1.1s var(--ease-out);
}
/* The donut's entrance reuses the existing .reveal/.reveal-scale mechanism
   (see section 4) rather than a bespoke class — same safe, noscript-covered
   path as everything else, one animation system instead of two. It just gets
   `reveal reveal-scale` added alongside `app-donut` in the markup. */
.app-donut.reveal {
  transform-origin: 50% 50%;
}

/* ---------- 12. Sections: timeline draw-in, FAQ smooth height ----------
   Same principle as the donut: reuse .reveal on the .timeline container
   itself (safe/noscript-covered) instead of a bespoke JS-only class, so the
   connecting line only ever animates — it's never permanently hidden. */
.timeline.reveal::before {
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 1s var(--ease-out);
}
.timeline.reveal.in::before {
  transform: scaleX(1);
}
.faq-item p {
  overflow: hidden;
  transition: max-height 0.32s var(--ease), opacity 0.25s var(--ease), padding 0.32s var(--ease);
}
/* When motion.js is managing a faq-item it adds .js-managed and takes over the
   [open] toggle itself, animating max-height (JS sets the open target to the
   content's measured scrollHeight in px, since percentages/auto don't
   transition) before flipping [open] off after the close animation finishes.
   Without JS, .js-managed is never added and native <details> behaves as
   today — instant, unanimated, fully accessible. */
.faq-item.js-managed p {
  max-height: 0;
  opacity: 0;
  padding-top: 0;
  padding-bottom: 0;
}
.faq-item.js-managed[open] p {
  /* Resting state once open: unclipped, so content doesn't snap shut the
     instant motion.js clears its temporary inline max-height after the
     open transition finishes (the base rule above is max-height:0). Bottom
     padding is restored here too — the base rule zeroes it so the close
     animation can collapse all the way to 0, but that meant open answers
     had no breathing room above the card's bottom edge. */
  max-height: none;
  opacity: 1;
  padding-bottom: 1.25rem;
}

/* ---------- 13. Forms: floating labels, validation shake, success animation ---------- */
.form-group-float {
  position: relative;
}
.form-group-float .form-control,
.form-group-float .form-select {
  padding-top: 1.35rem;
  padding-bottom: 0.45rem;
}
.form-group-float .form-label {
  position: absolute;
  left: 1rem;
  top: 0.9rem;
  margin: 0;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-muted);
  background: transparent;
  pointer-events: none;
  transform-origin: 0 50%;
  transition: transform 0.18s var(--ease), color 0.18s var(--ease), top 0.18s var(--ease);
}
.form-group-float .form-control:focus + .form-label,
.form-group-float .form-control:not(:placeholder-shown) + .form-label,
.form-group-float textarea.form-control:focus + .form-label,
.form-group-float textarea.form-control:not(:placeholder-shown) + .form-label {
  top: 0.35rem;
  transform: scale(0.78);
  color: var(--blue);
}
.was-validated .form-control:invalid,
.was-validated .form-select:invalid {
  animation: formShake 0.4s var(--ease);
}
@keyframes formShake {
  20%, 60% { transform: translateX(-4px); }
  40%, 80% { transform: translateX(4px); }
}
.form-success .check {
  transform: scale(0);
  transition: transform 0.4s var(--ease);
}
.form-success.show { display: block; }
.form-success.show .check { transform: scale(1); }

/* ---------- 14. Background: ambient gradient drift, extra floating shapes, glass ---------- */
.bg-drift {
  background-size: 160% 160%;
  animation: bgDrift 26s ease-in-out infinite;
}
@keyframes bgDrift {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}
.glass-panel {
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.5);
}

/* ---------- 16. Scrollbar: compact, theme-matched (replaces the bulky OS default) ---------- */
html {
  scrollbar-width: thin; /* Firefox */
  scrollbar-color: var(--border-strong) transparent;
}
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: var(--bg);
}
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 999px;
  border: 2px solid var(--bg);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--blue);
  background-clip: padding-box;
}

/* ---------- 15. Respect reduced motion (JS-created elements) ---------- */
@media (prefers-reduced-motion: reduce) {
  .preloader { display: none; }
  body.motion-loading > *:not(.preloader) { opacity: 1; }
  .motion-fade { opacity: 1; }
  .skeleton::after { animation: none; }
  .btn-ripple { display: none; }
}
