/* PHP2Shell campaign motion. Entrance and interaction only; nothing loops. Disabled under prefers-reduced-motion. */

/* Staggered fade-rise for supporting copy and the form. */
.enter { animation: rise .9s var(--ease-out) both; }
.enter-0 { animation-delay: .05s; }
.enter-2 { animation-delay: .25s; }
.enter-3 { animation-delay: .4s; }
.enter-4 { animation-delay: .52s; }
.enter-5 { animation-delay: .64s; }
@keyframes rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }

/* Title lines wipe up into view; the cyan dot lands last. The clip ends well outside the text so the dot's ripple is never cut off. */
.line-inner { animation: line-up 1.1s var(--ease-out) both; }
.line-1 { animation-delay: .1s; }
.line-2 { animation-delay: .22s; }
@keyframes line-up { from { transform: translateY(60%); clip-path: inset(0 0 100% 0); } to { transform: none; clip-path: inset(-1em); } }
.name-dot { animation: dot-land .8s cubic-bezier(.34, 1.56, .64, 1) .85s both, ripple 1.2s var(--ease-out) 1.4s; }
@keyframes dot-land { from { opacity: 0; transform: translateY(-1.1em) scale(.3); } to { opacity: 1; transform: none; } }
@keyframes ripple { from { box-shadow: 0 0 0 0 #2fe3eecc; } to { box-shadow: 0 0 0 .5em #2fe3ee00; } }

/* Stripes sweep in along their diagonal, then follow the pointer with depth (--px/--py set by app.js). */
.stripe { transform-box: fill-box; animation: stripe-in 1.2s var(--ease-out) both; }
.stripe-1 { animation-delay: .35s; }
.stripe-2 { animation-delay: .45s; }
.stripe-3 { animation-delay: .55s; }
@keyframes stripe-in { from { opacity: 0; transform: translate(-28%, 60%); } to { opacity: 1; transform: none; } }
.stripes { transform: translate(calc(var(--px, 0) * 14px), calc(var(--py, 0) * 10px)); transition: transform .8s var(--ease-out); }
.stripe-2 { translate: calc(var(--px, 0) * 10px) calc(var(--py, 0) * 6px); }
.stripe-3 { translate: calc(var(--px, 0) * 22px) calc(var(--py, 0) * 14px); }
.stripe-2, .stripe-3 { transition: translate .9s var(--ease-out); }
.vulnerability-name { transform: translate(calc(var(--px, 0) * -6px), calc(var(--py, 0) * -4px)); transition: transform 1s var(--ease-out); }

/* Eyebrow bar draws in. */
.eyebrow-bar { transform-origin: left; animation: grow-x .8s var(--ease-out) .5s both; }
@keyframes grow-x { from { transform: scaleX(0); } to { transform: none; } }

/* Steps: circles pop, connectors draw, copy follows. */
.step-number { animation: pop .6s cubic-bezier(.34, 1.56, .64, 1) both; }
.step h3, .step p { animation: rise .8s var(--ease-out) both; }
.step:not(:last-child)::after { animation: grow-x .9s var(--ease-out) both; }
.step-1 .step-number { animation-delay: .8s; }
.step-1 h3, .step-1 p, .step-1::after { animation-delay: .9s; }
.step-2 .step-number { animation-delay: 1.05s; }
.step-2 h3, .step-2 p, .step-2::after { animation-delay: 1.15s; }
.step-3 .step-number { animation-delay: 1.3s; }
.step-3 h3, .step-3 p { animation-delay: 1.4s; }
@keyframes pop { from { opacity: 0; transform: scale(.4); } to { opacity: 1; transform: none; } }
@media (max-width: 640px) { .step:not(:last-child)::after { animation-name: grow-y; } }
@keyframes grow-y { from { transform: scaleY(0); } to { transform: none; } }
.step-number { transition: background .3s, color .3s, transform .3s var(--ease-out); }
.step:hover .step-number { background: var(--blue); color: #fff; transform: scale(1.08); }

/* Button shine on hover. */
.button::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(105deg, transparent 35%, #ffffff40 50%, transparent 65%);
  transform: translateX(-120%);
}
.button:hover::after { transition: transform .8s var(--ease-out); transform: translateX(120%); }

/* Loader: spinning ring with a soft pulse; step text fades as it changes. */
.loader-ring { animation: spin .9s linear infinite; }
.loader::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: #0b5cf514; animation: pulse 1.8s ease-out infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse { from { transform: scale(.6); opacity: 1; } to { transform: scale(1.5); opacity: 0; } }

/* Validation feedback and results. */
.shake { animation: shake .45s cubic-bezier(.36, .07, .19, .97); }
@keyframes shake { 20%, 60% { transform: translateX(-6px); } 40%, 80% { transform: translateX(6px); } }
.field-error:not([hidden]) { animation: fade-down .3s var(--ease-out); }
@keyframes fade-down { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.result:not([hidden]) .result-icon { animation: pop .6s cubic-bezier(.34, 1.56, .64, 1) .15s both; }
.result:not([hidden]) .result-facts, .result:not([hidden]) .result-message, .result:not([hidden]) .result-evidence-wrap { animation: rise .6s var(--ease-out) .2s both; }
.result:not([hidden]) .trial { animation: rise .7s var(--ease-out) .3s both; }
.result:not([hidden]) .scan-again { animation: rise .7s var(--ease-out) .4s both; }
/* View changes inside the checker (form → loader → result → form), driven by app.js. */
.checker-stage.is-resizing { overflow: clip; overflow-clip-margin: 48px; transition: height .4s var(--ease-out); }
.is-leaving { opacity: 0; transform: translateY(-8px); transition: opacity .2s ease, transform .2s ease; }
.is-entering { animation: stage-in .42s var(--ease-out) both; }
@keyframes stage-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes result-in { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }

/* Below the fold: reveal on scroll once app.js marks the page as scripted. */
.js .reveal { opacity: 0; transform: translateY(28px); transition: opacity .9s var(--ease-out), transform .9s var(--ease-out); }
.js .reveal.is-in { opacity: 1; transform: none; }
.js .benefit.reveal:nth-of-type(1) { transition-delay: .1s; }
.js .benefit.reveal:nth-of-type(2) { transition-delay: .2s; }
.benefit-icon * { stroke-dasharray: 1; stroke-dashoffset: 0; }
.js .benefit .benefit-icon * { stroke-dashoffset: 1; transition: stroke-dashoffset 1.6s var(--ease-out) .4s; }
.js .benefit.is-in .benefit-icon * { stroke-dashoffset: 0; }
.help-band::before { transition: transform 1.4s var(--ease-out); transform: skewX(-34deg) translateX(-140px); }
.help-band.is-in::before { transform: skewX(-34deg); }

@media (prefers-reduced-motion: reduce) {
  :root { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .js .reveal { opacity: 1; transform: none; }
  .js .benefit .benefit-icon * { stroke-dashoffset: 0; }
  .stripes, .stripe, .vulnerability-name { transform: none; translate: none; }
  .help-band::before { transform: skewX(-34deg); }
}
