/* ==========================================================================
   ZeroPvP motion layer (2026-10-15 redesign)
   ==========================================================================
   Linked after zp.css on every page; driven by assets/js/motion.js.

   - Scroll reveal: rise (default), from the left, from the right, scale, fade, and staggered
     children. Only elements motion.js marked .zr-pre are ever hidden (below the fold, with
     IntersectionObserver, motion welcome). Reduced motion: nothing hides. Lite: fades only.
   - Inside an entering element: the section kicker draws its rule, [data-grow] bars grow.
   - Hover: cards lift and press, rows slide an accent edge in, tier badges catch one shine,
     kit icons bob once. No coloured glows, nothing follows the cursor.
   - The ticker: a CSS transform animation that runs only while .is-running (on screen).
   - Transform and opacity only, so nothing here triggers layout.
   ========================================================================== */

/* ---------- scroll reveal ---------- */
/* Waiting below the fold: opacity 0 only. Opacity is not inherited, so starting an entrance
   restyles the element and nothing under it; there is no static transform on a waiting element
   (each one is a compositor node: the performance pass of 2026-10-15 measured that tripling the
   layerize time of a scroll). The entrance is a one-shot animation that exists only while it
   runs; the state classes sit on the element that moves (motion.js gives a staggered container's
   children their own), so no rule here reaches into a subtree except the few targeted ones. */
.zr-pre { opacity: 0; }
/* The entrance delay motion.js writes on an element. Registered as not inherited: an inherited
   custom property written inline restyles the element's whole subtree, which at four reveals a
   screen was the largest single cost of the motion layer. */
@property --zr-d { syntax: "<time>"; inherits: false; initial-value: 0ms; }
@keyframes zr-up { from { opacity: 0; transform: translateY(28px); } }
@keyframes zr-left { from { opacity: 0; transform: translateX(-36px); } }
@keyframes zr-right { from { opacity: 0; transform: translateX(36px); } }
@keyframes zr-scale { from { opacity: 0; transform: translateY(16px) scale(0.95); } }
@keyframes zr-op { from { opacity: 0; } }
@keyframes zr-grow-x { from { transform: scaleX(0); } }
@keyframes zr-grow-y { from { transform: scaleY(0); } }
.zr-in { animation: zr-up 560ms var(--zr-ease) backwards; animation-delay: var(--zr-d, 0ms); }
.zr-in[data-reveal="left"] { animation-name: zr-left; }
.zr-in[data-reveal="right"] { animation-name: zr-right; }
.zr-in[data-reveal="scale"] { animation-name: zr-scale; }
.zr-in[data-reveal="fade"] { animation-name: zr-op; }

/* What draws or grows as part of an entrance */

.zr-in [data-grow] { animation: zr-grow-x 700ms var(--zr-ease) backwards; animation-delay: calc(var(--zr-d, 0ms) + 180ms); }
[data-grow] { transform-origin: 0 50%; }
/* The site's bar fills (stats, UHC, profile, front page) grow from the left as their card enters. */
.kr-bar > i, .uhc-track > i, .pf-track > i, .kc-track > i, .rival-bar > i, .duel-bar > i, .act-bar > i, .hw-bar > i { transform-origin: 0 50%; }
.zr-in .kr-bar > i, .zr-in .uhc-track > i, .zr-in .pf-track > i, .zr-in .kc-track > i, .zr-in .rival-bar > i, .zr-in .duel-bar > i, .zr-in .act-bar > i, .zr-in .hw-bar > i {
  animation: zr-grow-x 700ms var(--zr-ease) backwards; animation-delay: calc(var(--zr-d, 0ms) + 120ms);
}
.zr-in [data-rise] { animation: zr-grow-y 460ms var(--ease-back) backwards; animation-delay: calc(var(--zr-d, 0ms) + 120ms); }
[data-rise] { transform-origin: 50% 100%; }

html[data-lite] .zr-in { animation-name: zr-op; animation-duration: 300ms; }
html[data-lite] .zr-in [data-grow], html[data-lite] .zr-in [data-rise], html[data-lite] .zr-in .zp-kicker::after,
html[data-lite] .zr-in .kr-bar > i, html[data-lite] .zr-in .uhc-track > i, html[data-lite] .zr-in .pf-track > i, html[data-lite] .zr-in .kc-track > i,
html[data-lite] .zr-in .rival-bar > i, html[data-lite] .zr-in .duel-bar > i, html[data-lite] .zr-in .act-bar > i, html[data-lite] .zr-in .hw-bar > i { animation: none; }
@media (prefers-reduced-motion: reduce) { .zr-pre { opacity: 1; } .zr-in, .zr-in * { animation: none !important; } }
@media print { .zr-pre { opacity: 1 !important; } }

/* Rows written by a script after a tab switch or a filter: a short staggered rise. */
@keyframes zm-row { from { opacity: 0; transform: translate(0, 10px); } }
.zm-rows-in > * { animation: zm-row 340ms var(--zr-ease) both; }
.zm-rows-in > :nth-child(2) { animation-delay: 30ms; } .zm-rows-in > :nth-child(3) { animation-delay: 60ms; }
.zm-rows-in > :nth-child(4) { animation-delay: 90ms; } .zm-rows-in > :nth-child(5) { animation-delay: 120ms; }
.zm-rows-in > :nth-child(6) { animation-delay: 150ms; } .zm-rows-in > :nth-child(7) { animation-delay: 180ms; }
.zm-rows-in > :nth-child(8) { animation-delay: 210ms; } .zm-rows-in > :nth-child(9) { animation-delay: 240ms; }
.zm-rows-in > :nth-child(10) { animation-delay: 270ms; } .zm-rows-in > :nth-child(n+11) { animation-delay: 300ms; }
html[data-lite] .zm-rows-in > * { animation-name: zm-fade; }
@keyframes zm-fade { from { opacity: 0; } }

/* ---------- count-up figures ---------- */
.zm-counting { font-variant-numeric: tabular-nums; }

/* ---------- the live dot: three slow rings when the status lands, then still ---------- */
.zp-dot { position: relative; }
.zp-dot.is-on::after, .zp-dot.is-hot::after, .zp-dot.is-live::after {
  content: ""; position: absolute; inset: -1px; border-radius: 50%;
  border: 1px solid currentColor; color: var(--win); opacity: 0; pointer-events: none;
}
.zp-dot.is-hot::after, .zp-dot.is-live::after { color: var(--live); }
@keyframes zm-ring { 0% { opacity: 0.7; transform: scale(1); } 100% { opacity: 0; transform: scale(2.8); } }
@media (prefers-reduced-motion: no-preference) {
  .zp-dot.is-on::after, .zp-dot.is-hot::after, .zp-dot.is-live::after { animation: zm-ring 1.6s ease-out 3; }
  /* Not where nobody sees it ring: the closed phone menu and the footer. Every running
     animation is restyled on each frame of a scroll, seen or not. */
  .zp-sheet .zp-dot::after, .zp-foot .zp-dot::after { animation: none; }
}

/* ---------- the ticker ---------- */
.zm-marquee { position: relative; display: flex; overflow: hidden; }
.zm-track { display: flex; flex: none; gap: 0; min-width: max-content; }
@keyframes zm-marq { to { transform: translate(-100%, 0); } }
.zm-marquee.is-moving .zm-track { animation: zm-marq var(--zm-dur, 40s) linear infinite; animation-play-state: paused; }
.zm-marquee.is-moving.is-running .zm-track { animation-play-state: running; }
@media (hover: hover) and (pointer: fine) { .zm-marquee.is-moving.is-running:hover .zm-track { animation-play-state: paused; } }
.zm-marquee.is-still { overflow-x: auto; scrollbar-width: thin; }

/* ---------- hover ----------
   Only for a real pointer: a tap on a phone must not leave a card stuck in its lifted state. */
@keyframes zm-shine { from { transform: translateX(0) skewX(-20deg); } to { transform: translateX(380%) skewX(-20deg); } }
@keyframes zm-bob { 0% { transform: translateY(0); } 40% { transform: translateY(-5px); } 70% { transform: translateY(1px); } 100% { transform: translateY(0); } }

.zp-tier, .tier-pill { position: relative; overflow: hidden; }
.zp-tier::after, .tier-pill::after {
  /* Parked just outside the badge (which clips it) with no transform at rest: a static transform
     on every badge would be a compositor node each. The shine is a one-shot animation on hover. */
  content: ""; position: absolute; top: -2px; bottom: -2px; left: -48%; width: 40%;
  background: rgba(255, 255, 255, 0.24); pointer-events: none;
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* Cards: lift on hover, press on click. The shadow deepens in black, never in colour. */
  a.zp-card, .zp-kit, .kcard:not(.unranked), .lb-pod-card, .zp-step, .home-event, .zp-mcard, .lb-sum-card, .hw-card,
  .home-champ, .rival, .badge.earned, .hk-card, .hz-card, a.zp-panel {
    transition: transform 220ms var(--ease), box-shadow 220ms var(--ease), background-color var(--fast), border-color var(--fast);
  }
  a.zp-card:hover, .zp-kit:hover, .kcard:not(.unranked):hover, a.lb-pod-card:hover, .zp-step:hover, .home-event:hover,
  a.zp-mcard:hover, .lb-sum-card:hover, a.hw-card:hover, .home-champ:hover, .rival:hover, .badge.earned:hover, .hk-card:hover, .hz-card:hover, a.zp-panel:hover {
    transform: translateY(-4px);
    border-color: var(--line-3);
    box-shadow: var(--lip), var(--raise-hi);
  }
  a.zp-card:active, .zp-kit:active, .kcard:not(.unranked):active, a.lb-pod-card:active, a.zp-mcard:active, a.hw-card:active, .home-champ:active, .rival:active, .hk-card:active, a.zp-panel:active {
    transform: translateY(0) scale(0.99); transition-duration: 70ms;
  }
  .zp-kit:hover { z-index: 1; }

  /* Buttons: rise a hair before they press. */
  .zp-btn:not(.is-ghost):not(:active):hover, .zp-ipbox:not(:active):hover { transform: translateY(-1px); }

  /* Ladder rows: an accent edge slides in from the left and the row eases right by a hair. */
  a.zp-lrow, .lb-row, .home-mrow, .ph-rows > li > a, .home-hof li > a, .hw-feed-row { position: relative; }
  a.zp-lrow::after, .lb-row::after, .home-mrow::after, .ph-rows > li > a::after, .home-hof li > a::after, a.hw-feed-row::after {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--accent);
    transform: scaleY(0); transform-origin: 50% 0; transition: transform 220ms var(--ease); pointer-events: none;
  }
  a.zp-lrow:hover::after, .lb-row:hover::after, .home-mrow:hover::after, .ph-rows > li > a:hover::after, .home-hof li > a:hover::after, a.hw-feed-row:hover::after { transform: scaleY(1); }
  a.zp-lrow > *, .lb-row > .lb-cell, .home-mrow > * { transition: transform 220ms var(--ease); }
  a.zp-lrow:hover > :nth-child(2), .lb-row:hover > .lb-c-who, .home-mrow:hover > .hm-side.is-a { transform: translateX(5px); }

  /* Tier badges catch one shine when their row or card is hovered. */
  a.zp-lrow:hover .zp-tier::after, .lb-row:hover .zp-tier::after, .zp-kit:hover .zp-tier::after, .kcard:hover .zp-tier::after,
  .zp-tier:hover::after, .lb-pod-card:hover .zp-tier::after, .hw-card:hover .zp-tier::after, .hk-card:hover .zp-tier::after,
  .ht-step:hover .zp-tier::after, .hw-pod:hover .zp-tier::after {
    animation: zm-shine 560ms ease-out 1;
  }

  /* Kit icons bob once. */
  .zp-kit:hover .zp-kit-ico img, .zp-tab:hover img, .kcard:hover .zp-kit-ico img, .zp-kchip:hover img,
  .home-mrow:hover > img, .lb-kit-pick:hover img, .hw-kit:hover img, .hk-card:hover .hk-ico img {
    animation: zm-bob 460ms var(--ease) 1;
  }

  /* Section links: the chevron nudges forward. */
  .zp-sec-link::after { transition: transform 180ms var(--ease); }
  .zp-sec-link:hover::after { transform: translateX(3px) rotate(45deg); }

  /* The arrow inside a primary button nudges. */
  .zp-btn svg { transition: transform 180ms var(--ease); }
  .zp-btn.is-primary:hover svg:last-child:not(:first-child) { transform: translateX(3px); }

  [data-skin3d] { cursor: pointer; }
}

/* Keyboard focus gets the same lift as the pointer. */
.zp-kit:focus-visible, .kcard:focus-visible, a.lb-pod-card:focus-visible, .hk-card:focus-visible { transform: translateY(-2px); }
