/* ==========================================================================
   Bizora — Animations & interactions layer
   --------------------------------------------------------------------------
   Progressive enhancement. Everything below only applies when JavaScript is
   available (html.js). Without JS, no content is ever hidden or moved.

   Performance rules:
   - Animations are CSS-only once triggered; JS only adds classes.
   - prefers-reduced-motion disables motion but keeps opacity changes.
   ========================================================================== */

/* ---------------------------------------------------------------------------
 * 0. Boot marker + loader
 * ------------------------------------------------------------------------- */
/* The loader is a static overlay included by the public/public_site layouts.
 * It fades out on its own (CSS only) so it can never trap the page when
 * JavaScript is unavailable. With JS, a returning visitor gets an instant,
 * invisible loader (html.sfx-intro-skipped). */

/* ---------------------------------------------------------------------------
 * 1. Loading experience — brief branded loader
 * ------------------------------------------------------------------------- */
.sfx-loader {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 18px;
  background: linear-gradient(160deg, #f7f9fd 0%, #e8eefb 55%, #dfe7f6 100%);
  animation: sfxLoaderExit .5s ease 950ms forwards;
}
html.sfx-intro-skipped .sfx-loader { animation: none; opacity: 0; visibility: hidden; }
@keyframes sfxLoaderExit {
  to { opacity: 0; visibility: hidden; }
}
.sfx-loader__mark {
  width: 54px; height: 54px; border-radius: 16px;
  background: linear-gradient(135deg, #2f4fb0 0%, #10265c 100%);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 12px 30px -8px rgba(16, 38, 92, .55);
  animation: sfxLoaderFloat 1.6s ease-in-out infinite;
}
.sfx-loader__mark .material-symbols-outlined { color: #fff; font-size: 26px; }
.sfx-loader__ring { position: relative; width: 46px; height: 46px; }
.sfx-loader__ring::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 3px solid rgba(30, 58, 138, .18); border-top-color: #f59e0b;
  animation: sfxSpin .8s linear infinite;
}
.sfx-loader__text {
  font: 600 12px/1 'Inter', sans-serif; letter-spacing: .18em; text-transform: uppercase;
  color: #464555; animation: sfxFadePulse 1.4s ease-in-out infinite;
}
@keyframes sfxSpin { to { transform: rotate(360deg); } }
@keyframes sfxLoaderFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
@keyframes sfxFadePulse { 0%,100% { opacity: .5; } 50% { opacity: 1; } }

/* Thin progress bar used for same-origin navigations */
.sfx-progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  z-index: 9998; border-radius: 0 999px 999px 0;
  background: linear-gradient(90deg, #f59e0b, #fbbf24);
  box-shadow: 0 0 12px rgba(30, 58, 138, .65);
  pointer-events: none; opacity: 0;
}
.sfx-progress.is-running { opacity: 1; transition: width .25s ease; }

/* ---------------------------------------------------------------------------
 * 2. Scroll reveal system (IntersectionObserver driven)
 * ------------------------------------------------------------------------- */
/* NOTE: every "hidden until revealed" rule is scoped to :not(.is-visible).
   html.js [data-reveal] alone (0,2,1) would outrank the .is-visible rule
   (0,2,0), keeping content permanently invisible even after the JS adds the
   class. This scoping makes the hidden state drop out the moment JS reveals
   the element. */
html.js [data-reveal]:not(.is-visible) { opacity: 0; will-change: opacity, transform; }
html.js [data-reveal="up"]:not(.is-visible)    { transform: translateY(26px); }
html.js [data-reveal="down"]:not(.is-visible)  { transform: translateY(-22px); }
html.js [data-reveal="left"]:not(.is-visible)  { transform: translateX(-30px); }
html.js [data-reveal="right"]:not(.is-visible) { transform: translateX(30px); }
html.js [data-reveal="scale"]:not(.is-visible) { transform: scale(.94); }
html.js [data-reveal="fade"]:not(.is-visible)  { transform: none; }

[data-reveal].is-visible {
  opacity: 1; transform: none;
  transition: opacity .65s ease, transform .65s cubic-bezier(.21,.65,.32,1);
}
/* Belt & braces: tie the visible state to the same html.js context so it can
   never be outranked by any future generic [data-reveal] rule. */
html.js [data-reveal].is-visible {
  opacity: 1; transform: none;
}

/* Staggered groups: children animate after the group enters the viewport */
html.js [data-stagger] > .r-item { opacity: 0; transform: translateY(18px); will-change: opacity, transform; }
html.js [data-stagger].is-inview > .r-item {
  opacity: 1; transform: none;
  transition: opacity .55s ease calc(var(--d, 0ms) + 60ms),
              transform .55s cubic-bezier(.21,.65,.32,1) calc(var(--d, 0ms) + 60ms);
}
[data-stagger].is-inview > .r-item.is-hovered { transform: translateY(-4px); }

/* Section header helpers for reveal: not hidden, only lifted when labelled */
.reveal-group { transition: opacity .5s ease; }

/* ---------------------------------------------------------------------------
 * 3. Hero entrance (public marketing + business websites)
 * ------------------------------------------------------------------------- */
html.js [data-hero] > .h-item {
  opacity: 0;
  animation: sfxHeroIn .7s cubic-bezier(.2,.65,.3,1) forwards;
  animation-delay: calc(120ms + var(--i, 0) * 110ms);
}
@keyframes sfxHeroIn {
  from { opacity: 0; transform: translateY(24px) scale(.995); filter: blur(3px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}
/* First visit: wait for the intro loader to fade before the hero plays */
html:not(.sfx-intro-skipped) [data-hero] > .h-item {
  animation-delay: calc(1150ms + var(--i, 0) * 110ms);
}
html.js [data-hero] > .h-item-blur { filter: none; }
/* Floating decorative blobs */
html.js .hero-float {
  animation: sfxFloat 7s ease-in-out infinite;
}
html.js .hero-float--alt { animation-duration: 9s; animation-delay: -2.5s; }
@keyframes sfxFloat {
  0%, 100% { transform: translateY(0) translateX(0) rotate(0deg); }
  33%      { transform: translateY(-16px) translateX(10px) rotate(2deg); }
  66%      { transform: translateY(-6px) translateX(-8px) rotate(-2deg); }
}
/* Slow drifting gradient in the hero backdrop */
.hero-gradient-move {
  background-size: 180% 180%;
  animation: sfxGradient 12s ease infinite;
}
@keyframes sfxGradient {
  0%, 100% { background-position: 0% 30%; }
  50%      { background-position: 100% 70%; }
}

/* ---------------------------------------------------------------------------
 * 4. Micro-interactions: cards, panels, icons, images
 * ------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  /* Whole-card lift for card-like blocks (rounded-xl) used across public, portal, admin */
  :where(main, .sfx-root) :is(a, .hover-card, .card)[class*="rounded-xl"]:not(.no-lift) {
    transition: transform .22s ease, box-shadow .25s ease, border-color .25s ease, background-color .25s ease;
  }
  :where(main, .sfx-root) :is(a, .hover-card, .card)[class*="rounded-xl"]:not(.no-lift):hover {
    transform: translateY(-3px);
    box-shadow: 0 18px 34px -18px rgba(23, 28, 39, .22);
  }
  /* Border glow on interactive cards */
  :where(main, .sfx-root) :is(a, .hover-card, .card)[class*="rounded-xl"]:not(.no-lift):hover {
    border-color: rgba(30, 58, 138, .45);
  }
  /* Image zoom inside overflow-hidden frames */
  .overflow-hidden > img { transition: transform .65s cubic-bezier(.2,.65,.3,1); }
  .overflow-hidden:hover > img { transform: scale(1.055); }
}
/* Pressed feedback for card links */
:where(main, .sfx-root) a[class*="rounded-xl"]:active { transform: translateY(-1px) scale(.995); }

/* Arrow / trailing icon nudge */
:where(a, button):hover > .material-symbols-outlined:last-child {
  transform: translateX(2px);
  transition: transform .2s ease;
}
.material-symbols-outlined { transition: transform .2s ease; }

/* ---------------------------------------------------------------------------
 * 5. Buttons — shine, glow, press feedback, loading state
 * ------------------------------------------------------------------------- */
/* Primary CTA shine sweep */
:where(a, button, input[type="submit"])[class~="bg-primary"] {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: transform .15s ease, box-shadow .25s ease, filter .25s ease, background-color .2s ease;
}
@media (hover: hover) and (pointer: fine) {
  :where(a, button, input[type="submit"])[class~="bg-primary"]:not(:disabled):hover { box-shadow: 0 10px 24px -10px rgba(16, 38, 92, .6); }
  :where(a, button, input[type="submit"])[class~="bg-primary"]:not(:disabled):hover::after {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: linear-gradient(115deg, transparent 30%, rgba(255,255,255,.35) 50%, transparent 70%);
    transform: translateX(-110%);
    animation: sfxShine .85s ease;
  }
}
@keyframes sfxShine { to { transform: translateX(110%); } }
button:not(:disabled):active, [type="submit"]:not(:disabled):active {
  transform: scale(.97);
}
button, [type="submit"], a { -webkit-tap-highlight-color: transparent; }

/* Loading spinner inside submit buttons */
button.is-loading {
  color: transparent !important;
  pointer-events: none;
  position: relative;
}
button.is-loading::before {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border: 2px solid currentColor; border-top-color: transparent;
  border-radius: 50%;
  opacity: 1; color: inherit;
  animation: sfxSpin .7s linear infinite;
}
/* In dark/white contexts keep spinner readable */
button.is-loading.text-on-primary::before { border-color: rgba(255,255,255,.85); border-top-color: transparent; }

/* "Continue with Google" link busy state (plain GET navigation — visual only) */
a.google-btn[data-oauth].is-loading {
  color: transparent !important;
  pointer-events: none;
  cursor: wait;
  position: relative;
  opacity: .85;
}
a.google-btn[data-oauth].is-loading svg { visibility: hidden; }
a.google-btn[data-oauth].is-loading::before {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  border: 2px solid #5f6368; border-top-color: transparent;
  border-radius: 50%;
  animation: sfxSpin .7s linear infinite;
}

/* ---------------------------------------------------------------------------
 * 6. Navigation
 * ------------------------------------------------------------------------- */
/* Smooth anchor scrolling (offset for sticky headers) */
html.sfx-anchor-smooth { scroll-behavior: smooth; }
section[id], [id="about"], [id="listings"], [id="contact"] { scroll-margin-top: 92px; }

/* Sticky public header transformation */
.site-header { position: sticky; top: 0; z-index: 50; }
.site-header { transition: box-shadow .25s ease, background-color .25s ease, border-color .25s ease; }
body.is-scrolled .site-header { box-shadow: 0 6px 24px -14px rgba(23,28,39,.28); }
/* subtle inner topbar shrink inside dashboards is handled by layout structure */

/* Public mobile menu — opening replays an entrance animation (display:none
   re-triggers CSS animations), closing fades via .hidden-menu first. */
#mobile-nav-menu { transition: opacity .22s ease, transform .24s cubic-bezier(.2,.65,.3,1); transform-origin: top right; }
#mobile-nav-menu.hidden-menu { opacity: 0; transform: translateY(-8px) scale(.99); pointer-events: none; }
#mobile-nav-menu:not(.hidden) { animation: sfxMenuIn .22s ease-out; }
#mobile-nav-btn .material-symbols-outlined { transition: transform .2s ease; }

/* Login dropdown entrance */
#login-dropdown-menu:not(.hidden) { animation: sfxMenuIn .16s ease-out; transform-origin: top right; }
@keyframes sfxMenuIn {
  from { opacity: 0; transform: translateY(-6px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* Sidebar nav items: icon + label micro-motion */
nav a { transition: color .15s ease, background-color .15s ease, border-color .15s ease; }
nav a .material-symbols-outlined { transition: transform .2s ease; }
nav a:hover .material-symbols-outlined { transform: scale(1.12); }

/* Drawer (mobile) — content slide handled by layout's translate-x utilities */
.drawer-panel { will-change: transform; }

/* ---------------------------------------------------------------------------
 * 7. Modals / panels / dropdowns (used by view-level inline JS too)
 * ------------------------------------------------------------------------- */
.modal-enter { animation: sfxModalIn .22s cubic-bezier(.2,.65,.3,1); }
@keyframes sfxModalIn {
  from { opacity: 0; transform: translateY(12px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
.backdrop-enter { animation: sfxBackdropIn .2s ease; }
@keyframes sfxBackdropIn { from { opacity: 0; } to { opacity: 1; } }

/* ---------------------------------------------------------------------------
 * 8. Forms
 * ------------------------------------------------------------------------- */
input, select, textarea { transition: border-color .18s ease, box-shadow .18s ease, background-color .18s ease, transform .18s ease; }
input:focus, select:focus, textarea:focus {
  box-shadow: 0 0 0 3px rgba(30, 58, 138, .16);
}
@keyframes sfxShake {
  20%, 60% { transform: translateX(-5px); }
  40%, 80% { transform: translateX(5px); }
}
input.is-invalid, select.is-invalid, textarea.is-invalid {
  border-color: #ba1a1a !important;
  animation: sfxShake .3s ease;
}

/* ---------------------------------------------------------------------------
 * 9. Flash messages as toast
 * ------------------------------------------------------------------------- */
.flash-enter {
  animation: sfxToastIn .32s cubic-bezier(.2,.65,.3,1);
  position: relative;
  overflow: hidden;
}
.flash-enter::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 3px; width: 100%;
  background: currentColor; opacity: .35;
  transform-origin: left;
  animation: sfxToastTimer 6.5s linear forwards;
}
.flash-enter.is-dismissing { transition: opacity .3s ease, transform .3s ease; opacity: 0; transform: translateY(-8px); }
@keyframes sfxToastIn {
  from { opacity: 0; transform: translateY(-10px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes sfxToastTimer { to { transform: scaleX(0); } }

/* ---------------------------------------------------------------------------
 * 10. Charts & counters
 * ------------------------------------------------------------------------- */
/* Vertical bar chart entrance (columns rise & fade, staggered) */
html.js .chart-bars > .bar-item { opacity: 0; transform: translateY(14px); will-change: opacity, transform; }
html.js .chart-bars.is-animated > .bar-item {
  opacity: 1; transform: none;
  transition: opacity .5s ease calc(var(--i, 0) * 60ms),
              transform .55s cubic-bezier(.21,.65,.32,1) calc(var(--i, 0) * 60ms);
}
/* Horizontal progress fills */
html.js .progress-fill { transform: scaleX(0); transform-origin: left; will-change: transform; }
html.js .progress-fill.is-animated {
  transform: scaleX(1);
  transition: transform .9s cubic-bezier(.2,.65,.3,1) .1s;
}
html.js .chart-bars, html.js .progress-fill { will-change: transform; }

/* Stat counters — number pop when finishing */
.stat-count.is-done { animation: sfxCountPop .35s ease; }
@keyframes sfxCountPop { 50% { transform: scale(1.06); } }

/* ---------------------------------------------------------------------------
 * 11. Empty states
 * ------------------------------------------------------------------------- */
.empty-state { animation: sfxEmptyIn .45s cubic-bezier(.2,.65,.3,1); }
@keyframes sfxEmptyIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------------------
 * 12. Business website extra polish
 * ------------------------------------------------------------------------- */
.business-hero::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(600px circle at 20% 20%, rgba(255,255,255,.14), transparent 55%),
              radial-gradient(500px circle at 85% 70%, rgba(255,255,255,.10), transparent 55%);
  pointer-events: none;
}
/* Offer tag pulse for live offers */
.offer-chip { animation: sfxChipPulse 2.4s ease-in-out infinite; }
@keyframes sfxChipPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(30,58,138,.28); } 50% { box-shadow: 0 0 0 6px rgba(30,58,138,0); } }

/* ---------------------------------------------------------------------------
 * 13. Cursor ambient glow (desktop, marketing layouts only)
 * ------------------------------------------------------------------------- */
.sfx-glow {
  position: fixed; top: 0; left: 0; width: 560px; height: 560px;
  margin: -280px 0 0 -280px;
  border-radius: 50%;
  pointer-events: none; z-index: 1;
  background: radial-gradient(circle, var(--glow-color, rgba(30,58,138,.10)) 0%, transparent 60%);
  opacity: 0; transition: opacity .5s ease;
  mix-blend-mode: multiply;
}
.sfx-glow.is-visible { opacity: 1; }

/* ---------------------------------------------------------------------------
 * 14. Reduced motion + low-power devices
 * ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html.js [data-reveal], html.js [data-stagger] > .r-item,
  html.js [data-hero] > .h-item,
  html.js .chart-bars > .bar-item, .progress-fill,
  .sfx-glow, .hero-float, .hero-gradient-move, .offer-chip {
    opacity: 1 !important; transform: none !important;
    animation: none !important;
    transition: none !important;
  }
  .sfx-loader, .sfx-progress { display: none !important; }
  html { scroll-behavior: auto !important; }
  button.is-loading::before, a.google-btn[data-oauth].is-loading::before { animation: none; }
}

/* Fail-safe: if app.js never finished initialising (head.php watchdog adds
   html.sfx-fallback), show everything. Content must never stay hidden
   behind animations. */
html.sfx-fallback [data-reveal],
html.sfx-fallback [data-stagger] > .r-item,
html.sfx-fallback [data-hero] > .h-item,
html.sfx-fallback .chart-bars > .bar-item,
html.sfx-fallback .progress-fill {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
  transition: none !important;
}
html.sfx-fallback .sfx-loader { display: none !important; }
@media (max-width: 767px) {
  .sfx-glow { display: none; }
}
