/* animations.css, keyframes + reveal */

@keyframes drift {
    0%   { transform: translate(0,0) scale(1); }
    100% { transform: translate(40px, -30px) scale(1.05); }
}
@keyframes bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-10px); }
}
@keyframes eq {
    0%, 100% { transform: scaleY(1); }
    50%      { transform: scaleY(0.4); }
    25%      { transform: scaleY(1.3); }
}
@keyframes hero-kenburns {
    /* Pure center scale, no translate, so the image breathes from the middle
       instead of drifting from a corner. Starts at the intro's end scale (1.06)
       for a seamless handoff. */
    0%   { transform: scale(1.06); }
    100% { transform: scale(1.12); }
}

/* Sheen, occasional diagonal sweep with a long quiet tail so it feels
   atmospheric, not constant. ~85% of the cycle has the highlight off-screen. */
@keyframes hero-sheen {
    /* Reworked 2026-05-30: the slow background wash now spends most of the cycle
       actually crossing the scene (a long, gentle, near-still drift) instead of
       sitting off-screen, so it comes around often enough to be seen. ~78% of
       the cycle is the on-screen drift; a short quiet tail resets it. */
    0%   { transform: translateX(-120%) skewX(-12deg); opacity: 0; }
    8%   { opacity: 1; }
    70%  { transform: translateX(60%)  skewX(-12deg); opacity: 1; }
    78%  { opacity: 0; }
    80%, 100% { transform: translateX(120%) skewX(-12deg); opacity: 0; }
}

/* Glass-card gloss, a soft diagonal highlight that drifts across the hero
   panels by shifting a wide gradient's background-position. No transform and
   no parent overflow clipping, so it stays inside the rounded corners and
   never touches the Visual Editor pencils. Long quiet tail keeps it premium,
   not a metronome. */
@keyframes hero-glass-sheen {
    /* One continuous, constant-speed sweep (paired with linear timing). Position
       is keyed only at entry and exit so it never stutters mid-panel; opacity is
       a soft bell so it fades in, holds low, and fades out, with no pop and no
       slow-down. Long transparent tail keeps it a rare accent. */
    0%   { opacity: 0; background-position: 130% -10%; }
    12%  { opacity: 0.9; }
    24%  { opacity: 0.9; }
    36%  { opacity: 0; background-position: -30% 100%; }
    100% { opacity: 0; background-position: -30% 100%; }
}
@keyframes hero-status-sheen {
    0%   { opacity: 0; background-position: 125% -8%; }
    14%  { opacity: 0.85; }
    26%  { opacity: 0.85; }
    40%  { opacity: 0; background-position: -25% 100%; }
    100% { opacity: 0; background-position: -25% 100%; }
}

/* Cinematic intro, the photo arrives crisp and fully clear, holds for a
   beat so the visitor registers the scene, then gently darkens and softens
   into a backdrop as the glass panel and copy slide in over it.
   Runs once, then the steady-state kenburns takes over. */
@keyframes hero-intro-photo {
    0%   { opacity: 0; transform: scale(1.02); filter: saturate(1.0) contrast(1.0) brightness(1.0) blur(0px); }
    22%  { opacity: 1; transform: scale(1.02); filter: saturate(1.0) contrast(1.0) brightness(1.0) blur(0px); }
    100% { opacity: 1; transform: scale(1.06); filter: saturate(1.12) contrast(1.2) brightness(1.0) blur(2px); }
}
@keyframes hero-intro-overlay {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes hero-intro-content {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes hero-intro-content-lead {
    from { opacity: 0; transform: translate3d(-14px, 6px, 0); }
    to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* Faint atmospheric haze, slow diagonal drift, very low opacity, gives
   subliminal sense of motion without distraction. */
@keyframes hero-haze {
    0%   { transform: translate3d(-4%, -2%, 0) scale(1.05); opacity: 0.16; }
    50%  { transform: translate3d( 2%,  1%, 0) scale(1.08); opacity: 0.22; }
    100% { transform: translate3d(-4%, -2%, 0) scale(1.05); opacity: 0.16; }
}

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

/* Reveal system, prostarplanet pattern:
   - The hidden initial state is gated on `body.pp-intro`, which the layout
     template prints into the server-rendered HTML. There is no JS race, elements paint hidden on the very first frame.
   - JS removes the body class on cleanup and adds `intro-show` to each
     element in sequence so they transition in.
   - No-JS visitors never get `intro-show`, but they don't get the hidden
     state either after the body class is gone (we strip it on cleanup).
     If JS never runs at all, content is still visible because the only
     hidden state is the body.pp-intro selector, see no-JS fallback below.
*/
body.pp-intro [data-reveal] {
    opacity: 0;
    transform: translateY(24px);
    transition:
        opacity 900ms cubic-bezier(0.2, 0.85, 0.25, 1),
        transform 900ms cubic-bezier(0.2, 0.85, 0.25, 1);
    will-change: opacity, transform;
}
body.pp-intro [data-reveal].intro-show {
    opacity: 1;
    transform: none;
}

/* ===== Scroll Motion Polish (1.22.90) =====
   Tasteful reveal variety on the EXISTING data-reveal system, so the page reads
   as designed instead of one flat fade. The universal .intro-show reset above
   clears every variant (transform:none), so each variant only declares its
   hidden state. Transform + opacity only, so nothing shifts layout (no CLS).
   Movement stays in the 14-42px range, no spin, no bounce. */
body.pp-intro [data-reveal="fade-left"]:not(.intro-show)  { transform: translateX(-34px); }
body.pp-intro [data-reveal="fade-right"]:not(.intro-show) { transform: translateX(34px); }
body.pp-intro [data-reveal="panel-rise"]:not(.intro-show) { transform: translateY(42px); }
body.pp-intro [data-reveal="soft-scale"]:not(.intro-show) { transform: translateY(14px) scale(0.97); }

/* Cards ease in with a soft scale and their authored stagger (data-reveal-delay,
   now wired to a transition-delay in app.js), so a grid flows in one card at a
   time instead of all at once. The :not(.intro-show) guard lets the universal
   settle (transform:none) win once the card has arrived, so cards never get
   stuck at the offset. */
body.pp-intro .card[data-reveal]:not(.intro-show) { transform: translateY(18px) scale(0.985); }

/* Call-to-action panels get one calm light glow as they arrive, then rest. Not
   a casino sweep: a single soft blue/gold bloom that fades back to normal. */
@keyframes cta-arrive-glow {
    0%   { box-shadow: 0 8px 24px rgba(20, 32, 43, 0.08); }
    45%  { box-shadow: 0 18px 50px rgba(27, 78, 143, 0.20), 0 0 26px rgba(232, 184, 51, 0.15); }
    100% { box-shadow: 0 8px 24px rgba(20, 32, 43, 0.08); }
}
body.pp-intro .final-cta.intro-show,
body.pp-intro .cross-store.intro-show { animation: cta-arrive-glow 2200ms ease 220ms 1; }

/* Call-to-action panels also rise a touch further than a normal section so they
   read as the important moment on the page (paired with the glow above). Keyed
   by their existing classes, so no markup change. */
body.pp-intro .final-cta[data-reveal]:not(.intro-show),
body.pp-intro .cross-store[data-reveal]:not(.intro-show) { transform: translateY(40px); }

/* Mobile: lighter and quicker so phones never feel janky. Directional slides and
   the panel rise flatten to a small fade-up, and the transition shortens. */
@media (max-width: 700px) {
    body.pp-intro [data-reveal] { transition-duration: 620ms; }
    body.pp-intro [data-reveal="fade-left"]:not(.intro-show),
    body.pp-intro [data-reveal="fade-right"]:not(.intro-show),
    body.pp-intro [data-reveal="panel-rise"]:not(.intro-show),
    body.pp-intro [data-reveal="soft-scale"]:not(.intro-show),
    body.pp-intro .card[data-reveal]:not(.intro-show),
    body.pp-intro .final-cta[data-reveal]:not(.intro-show),
    body.pp-intro .cross-store[data-reveal]:not(.intro-show) { transform: translateY(16px); }
}

/* No-JS fallback: if JS is blocked entirely, the body class stays on but
   we still need content visible. A <noscript> tag in the head adds a
   `no-js-fallback` class via CSS; here we use feature detection instead. */
html:not(.js) body.pp-intro [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
}

/* Reduced-motion floor intentionally removed by owner decision: scroll-reveal
   animations run with full transform/slide for every visitor regardless of the
   OS prefers-reduced-motion flag (parity with chadsanders.com). Do not re-add
   prefers-reduced-motion gating to public reveals. */

[data-tilt] { transform-style: preserve-3d; transition: transform var(--dur) var(--ease-out); }
/* will-change only activates on hover/focus to stay under Firefox's compositor budget (1.23.72) */
[data-tilt]:hover, [data-tilt]:focus-within { will-change: transform; }

/* Hover micro-interactions */
.card { transform-style: preserve-3d; }
