/* CLAUDE 10-18 (design polish B4, analysis/DESIGN-POLISH-PLAN-2026-10-18.md section 5): the wow layer for the two
   marketing surfaces, the app's signed-out landing (#lp) and ripclip.org (site/src, which receives this file from
   tools/build_site.py). Persuade mode, so it may bend Graphite where section 5.4 says: bigger display type, one soft
   top light, one glass layer, one holographic ring, one particle canvas. Green stays state.

   Every motion here has one reason: the headline lands word by word (hierarchy), section headings rise through a
   mask as they are reached (story order), the crop lifts out of the long frame into the clip (what the product does), the plan ring turns on
   hover (which plan most people pick). Only transform and opacity move (and the ring's angle, on hover). Nothing runs without a
   reduced-motion answer: movement goes, opacity stays (plan 3.5). The class `wow-ready` is set by landing-wow.js, so a
   page without JavaScript shows everything at once. Tokens: --ease-out and --ease-soft come from motion.css in the app
   and from site.css on ripclip.org. */

:root {
  --wow-rise: 640ms; /* a heading rising through its mask (the observer path; the scroll-driven one follows the scroll) */
}

/* ===== kinetic type: words pre-split (hero) or split by landing-wow.js (section headings) ===== */
/* a word is a line mask: the outer box clips, the inner word rises through it. The mask is a static overflow, so only
   transform and opacity move (a clip-path animation repaints on the main thread, which cost frames on a throttled
   phone); the padding and the matching negative margin keep descenders and tight tracking inside the mask without
   changing the line's own metrics */
.kw {
  display: inline-block;
  vertical-align: top;
  overflow: hidden;
  padding: 0.14em 0.1em 0.28em;
  margin: -0.14em -0.1em -0.28em;
}
.kw > span {
  display: inline-block;
}
/* a whole section heading as one mask (landing-wow.js mask()) */
.kw-block,
.kw-block > span {
  display: block;
}
.kw-block {
  padding-inline: 0;
  margin-inline: 0;
}
@media (prefers-reduced-motion: no-preference) {
  /* the hero headline plays once on load, 70 ms between words. The app keeps its page at opacity 0 while it boots
     (body.booting, style.css), so the rise starts when the page is shown, not before */
  body:not(.booting) .wow-hero .kw > span {
    animation:
      wow-word 880ms var(--ease-out) calc(90ms + var(--w, 0) * 80ms) both,
      wow-stretch 440ms var(--ease-out) calc(90ms + var(--w, 0) * 80ms) both;
  }
  @keyframes wow-word {
    from {
      transform: translateY(108%);
      opacity: 0;
    }
  }
  /* the weight lands with the word: each word opens from condensed to the headline's own width in the first half of the
     rise. CLAUDE 10-20: it animated Mona Sans's width axis (font-stretch 76% -> 100%), which is layout: the centred line
     re-wrapped and slid while the words widened, a layout shift of 0.37 on every desktop load (Lighthouse calls over
     0.25 poor). `scale` (its own property, so it composes with wow-word's translate) draws the same opening on the
     compositor and the line keeps its final width from the first frame (analysis/LOAD-SPEED-2026-10-20.md) */
  @keyframes wow-stretch {
    from {
      scale: 0.78 1;
    }
  }
  /* once the words are in, a light sweeps across the second half of the line (.sw): a bright band through the text, word
     after word, left to right */
  body:not(.booting) .wow-hero .sw > span {
    background: linear-gradient(100deg, var(--soft) 0 38%, var(--ink) 50%, var(--soft) 62% 100%) 100% 0 / 320% 100%
      no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation:
      wow-word 880ms var(--ease-out) calc(90ms + var(--w, 0) * 80ms) both,
      wow-stretch 440ms var(--ease-out) calc(90ms + var(--w, 0) * 80ms) both,
      wow-sweep 1400ms ease-in-out calc(1500ms + (var(--w, 0) - 5) * 130ms) both;
  }
  @keyframes wow-sweep {
    from {
      background-position: 100% 0;
    }
    to {
      background-position: 0 0;
    }
  }
  /* the phone gets the lighter cut: the words rise, without the width axis and the sweep (both repaint the big text
     every frame, which a throttled phone pays for) */
  @keyframes wow-word-lite {
    from {
      transform: translateY(108%);
      opacity: 0;
    }
  }
  @media (max-width: 767.98px) {
    body:not(.booting) .wow-hero .kw > span,
    body:not(.booting) .wow-hero .sw > span {
      animation: wow-word-lite 760ms var(--ease-out) calc(90ms + var(--w, 0) * 70ms) both;
      background: none;
      -webkit-text-fill-color: currentColor;
    }
  }
}
@media (prefers-reduced-motion: reduce) {
  /* !important in the reduced blocks: the app's editor.css still ends with a blanket `* { animation: none !important }`
     for this setting (motion.css explains it), and the policy keeps the fades */
  body:not(.booting) .wow-hero .kw > span {
    animation: wow-fade 200ms ease both !important;
  }
}
@keyframes wow-fade {
  from {
    opacity: 0;
  }
}
/* section headings: the same rise, on reach. landing-wow.js observes the heading's group (its parent) and adds .in to it;
   the hidden state hangs on the group, so the page has about twenty observed targets, not one per block */
.wow-ready [data-kw] .kw > span {
  transition:
    transform var(--wow-rise) var(--ease-out),
    opacity 320ms ease;
}
.wow-ready [data-wow-group]:not(.in) [data-kw] .kw > span {
  transform: translateY(108%);
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  .wow-ready [data-kw] .kw > span {
    transition: opacity 200ms ease !important;
  }
  .wow-ready [data-wow-group]:not(.in) [data-kw] .kw > span {
    transform: none;
  }
}

/* Where the browser has scroll-driven animations (Chrome, Edge, Safari 26) the same rise is tied to the scroll position
   and runs off the main thread: no observer callback, no class flip, no style recalculation per reveal. landing-wow.js
   sets .wow-sd and marks every group .in at once, so the observer path's hidden state never applies. Other browsers,
   and reduced motion, keep the observer path above. (The blocks under a heading do not rise at all: a rise on each of
   about forty paragraphs and cards cost a throttled phone 15 points of dropped frames while videos played.) */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .wow-sd .kw-block {
      view-timeline: --wow-kw block;
    }
    .wow-sd [data-kw] .kw-block > span {
      transition: none;
      animation: wow-sd-mask linear both;
      animation-timeline: --wow-kw;
      animation-range: entry 0% entry 70%;
    }
    @keyframes wow-sd-mask {
      from {
        opacity: 0;
        transform: translateY(108%);
      }
    }
  }
}

/* ===== the host of the hero scene (landing-scene.css): what sits above the scene ===== */
.wow-host {
  position: relative;
}
.wow-host > :not(.wow-scene) {
  position: relative;
  z-index: 1;
}

/* ===== the crop that lifts out of the long frame into the clip (landing-wow.js morph) ===== */
.wow-crop {
  position: absolute;
  border-radius: 10px;
  box-shadow:
    0 0 0 1.5px var(--ink),
    0 0 0 999px rgba(6, 6, 8, 0.5);
  opacity: 0;
  pointer-events: none;
  z-index: 2;
}

/* ===== the plan ring: near-monochrome conic edge, turning on hover (one place only, plan 5.1) ===== */
@property --wow-a {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}
.wow-ring {
  position: relative;
}
.wow-ring::before {
  --wow-a: 0deg; /* where @property is not supported the ring is simply a still one */
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1.5px;
  background: conic-gradient(
    from var(--wow-a),
    rgba(242, 240, 235, 0.85),
    rgba(150, 178, 255, 0.5),
    rgba(255, 196, 150, 0.5),
    rgba(242, 240, 235, 0.85)
  );
  -webkit-mask:
    linear-gradient(black 0 0) content-box,
    linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(black 0 0) content-box,
    linear-gradient(black 0 0);
  mask-composite: exclude;
  opacity: 0.5;
  pointer-events: none;
  transition: opacity 240ms ease;
}
/* CLAUDE 10-20: with the plan picker (planpick.js) the ring is the picked card's, in place of the picker's outline */
.wow-ring[aria-checked='false']::before {
  opacity: 0;
}
.wow-ring.pp-card[aria-checked='true']:not(:focus-visible) {
  outline-color: transparent;
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .wow-ring:not([aria-checked='false']):hover::before {
    opacity: 0.95;
    animation: wow-spin 6s linear infinite;
  }
}
@keyframes wow-spin {
  to {
    --wow-a: 360deg;
  }
}
@media (hover: hover) and (pointer: fine) {
  .wow-ring:not([aria-checked='false']):hover::before {
    opacity: 0.95;
  }
}
@media (prefers-reduced-motion: reduce) {
  .wow-ring:hover::before {
    animation: none;
  }
}

/* ===== the waitlist check: drawn once when the sign-up lands (ripclip.org) ===== */
.wow-check {
  width: 44px;
  height: 44px;
  justify-self: center;
}
.wow-check circle {
  fill: none;
  stroke: rgba(83, 252, 24, 0.35);
  stroke-width: 1.5;
}
.wow-check path {
  fill: none;
  stroke: var(--sig);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 30;
  stroke-dashoffset: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .wow-check path {
    animation: wow-draw 420ms var(--ease-out) 120ms both;
  }
  .wow-check circle {
    animation: wow-fade 300ms ease both;
  }
  @keyframes wow-draw {
    from {
      stroke-dashoffset: 30;
    }
  }
}

/* ===== depth (B4 v2, the owner: stronger depth everywhere): layered surfaces, a real shadow, an inner highlight, a soft
   mesh behind the tinted bands. Static paints only: nothing here moves or blurs. ===== */
#lp .shell {
  box-shadow:
    inset 0 0 0 1px var(--hair),
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 2px 5px rgba(0, 0, 0, 0.3),
    0 30px 60px -30px rgba(0, 0, 0, 0.85);
}
#lp .core {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
#lp .console {
  box-shadow:
    inset 0 0 0 1px var(--hair2),
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 4px 10px rgba(0, 0, 0, 0.35),
    0 70px 130px -50px rgba(0, 0, 0, 0.95);
}
#lp .band.tint {
  background:
    radial-gradient(58% 50% at 10% 0%, rgba(118, 150, 196, 0.09), transparent 70%),
    radial-gradient(46% 44% at 94% 100%, rgba(83, 252, 24, 0.035), transparent 70%), var(--bg2);
}
#lp .close {
  background:
    radial-gradient(52% 80% at 18% 60%, rgba(118, 150, 196, 0.1), transparent 70%),
    radial-gradient(40% 70% at 86% 40%, rgba(255, 168, 120, 0.08), transparent 70%);
}
