/*
 | THE JEMPUTSHARE PRELOADER, ported from jemputPro's lab
 | (public/_lab/jemputshare-preloader, owner-tuned 2026-10-18) in Kilat.
 | A drop and its ripple; the tagline slides in from the right line by line,
 | holds, and leaves to the left as #JemputShare lands with a hand-written
 | "okey!" and holds; then one last ring carries the ground away and the
 | brand stays exactly where it is, over the wall's own copy of it.
 |
 | Every size is a container unit of .jsp__stage: a phone-shaped column that
 | is the whole screen on a phone and at most 760 px wide (or 90% of the
 | height) on a desk, so the type keeps the proportions the owner approved. The ground, the hole and
 | the ring fill the whole window. Every time is a multiple of --jsp-speed.
 */

.jsp {
  --jsp-speed: 1;
  --jsp-ring: clamp(3px, 1vw, 6px);
  position: fixed;
  inset: 0;
  z-index: 50;
  overflow: hidden;
  color: var(--ink);
  font-family: 'Jost', system-ui, sans-serif;
  cursor: pointer;
  /* Lifts itself when no script has taken it over (jemputPro's BAIL), after the whole show has had its time. */
  animation: jsp-bail 1ms linear 8s forwards;
}

/* The night itself, a layer of its own so it can dissolve while the brand stays. */
.jsp__ground {
  position: absolute;
  inset: 0;
  background: var(--ground);
}

html.is-seen .jsp {
  display: none;
}

@keyframes jsp-bail {
  to { visibility: hidden; opacity: 0; pointer-events: none; }
}

/* No weight (as in the lab): .jsp__okey's own margin is the owner-tuned placement and must win. */
:where(.jsp p) {
  margin: 0;
}

.jsp__stage {
  position: absolute;
  inset: 0;
  /* Capped by the height too: a phone on its side keeps the brand to the size of its screen. */
  width: min(100%, 760px, 90svh);
  margin-inline: auto;
  container-type: size;
}

/* ── 1. The drop and its ripple ──────────────────────────────────── */

.jsp__ripple {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.jsp__drop,
.jsp__ring {
  grid-area: 1 / 1;
  border-radius: 50%;
}

.jsp__drop {
  width: 5cqw;
  height: 5cqw;
  background: var(--accent);
  animation: jsp-drop calc(450ms * var(--jsp-speed)) var(--ease-out) both;
}

.jsp__ring {
  width: 22cqw;
  height: 22cqw;
  border: .5cqw solid var(--accent);
  animation: jsp-ring calc(1100ms * var(--jsp-speed)) var(--ease-out) calc(var(--d) * var(--jsp-speed)) both;
}

.jsp__ring:nth-child(2) { --d: 60ms; }
.jsp__ring:nth-child(3) { --d: 160ms; border-color: color-mix(in srgb, var(--ink) 45%, transparent); }
.jsp__ring:nth-child(4) { --d: 260ms; border-width: .3cqw; }

@keyframes jsp-drop {
  0% { transform: scale(0); opacity: 0; }
  30% { transform: scale(1.15); opacity: 1; }
  60% { transform: scale(.9); opacity: 1; }
  100% { transform: scale(0); opacity: 0; }
}

@keyframes jsp-ring {
  0% { transform: scale(.1); opacity: 0; }
  12% { opacity: .9; }
  100% { transform: scale(7); opacity: 0; }
}

/* ── 2. The tagline, right to left ───────────────────────────────── */

.jsp__lines {
  /* Off the window's right edge on the way in, past its left on the way out, however wide the window is. */
  --jsp-in: calc(50vw + 60cqw);
  --jsp-out: calc(-50vw - 70cqw);
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 8cqw;
  font-size: 13cqw;
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -.02em;
  text-transform: lowercase;
  pointer-events: none;
}

/* The way out fills forwards only, or its first frame would hold the line still before it ever came in. */
.jsp__line {
  display: block;
  white-space: nowrap;
  color: transparent;
  -webkit-text-stroke: .32cqw var(--ink);
  animation:
    jsp-line-in calc(1000ms * var(--jsp-speed)) var(--ease-out) calc((300ms + var(--i) * 120ms) * var(--jsp-speed)) both,
    jsp-line-out calc(600ms * var(--jsp-speed)) var(--ease-in) calc((2900ms + var(--i) * 90ms) * var(--jsp-speed)) forwards;
}

/* One word on each line is filled, so the eye reads the sentence by its keys. */
.jsp__line b {
  font-weight: inherit;
  color: var(--ink);
  -webkit-text-stroke: 0;
}

.jsp__line:nth-child(1) { --i: 0; }
.jsp__line:nth-child(2) { --i: 1; }
.jsp__line:nth-child(3) { --i: 2; }
.jsp__line:nth-child(4) { --i: 3; }

@keyframes jsp-line-in {
  from { transform: translateX(var(--jsp-in)); }
  to { transform: translateX(0); }
}

@keyframes jsp-line-out {
  from { transform: translateX(0); opacity: 1; }
  to { transform: translateX(var(--jsp-out)); opacity: 0; }
}

/* ── 3. #JemputShare okey! ───────────────────────────────────────── */

.jsp__brand {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  pointer-events: none;
}

.jsp__word {
  display: flex;
  align-items: baseline;
  line-height: 1;
  animation: jsp-brand-in calc(900ms * var(--jsp-speed)) var(--ease-out) calc(3050ms * var(--jsp-speed)) both;
}

.jsp__hash {
  font-weight: 300;
  font-size: 8.5cqw;
  color: var(--accent);
  margin-right: .5cqw;
  transform: translateY(-1.5cqw);
}

.jsp__mark {
  font-family: 'Sacramento', cursive;
  font-size: 16cqw;
  font-weight: 400;
  color: var(--ink);
}

.jsp__okey {
  justify-self: end;
  margin: -2cqw 9cqw 0 0;
  font-family: 'Caveat', cursive;
  font-weight: 700;
  font-size: 8cqw;
  color: var(--accent);
  transform: rotate(-8deg);
  transform-origin: 20% 60%;
  animation: jsp-okey calc(500ms * var(--jsp-speed)) cubic-bezier(.34, 1.56, .64, 1) calc(3700ms * var(--jsp-speed)) both;
}

@keyframes jsp-brand-in {
  from { transform: translateX(75cqw); opacity: 0; }
  40% { opacity: 1; }
  to { transform: translateX(0); opacity: 1; }
}

@keyframes jsp-okey {
  from { transform: scale(.4) rotate(-22deg); opacity: 0; }
  to { transform: scale(1) rotate(-8deg); opacity: 1; }
}

/* The partner's line, at the foot under skip: there from the first frame, with no motion of its own. */
.jsp__by {
  position: absolute;
  left: 50%;
  bottom: calc(5cqw + env(safe-area-inset-bottom));
  translate: -50% 0;
  display: flex;
  align-items: center;
  gap: 1.6cqw;
  white-space: nowrap;
  font-size: clamp(12px, 3.2cqw, 14px);
  font-weight: 500;
  letter-spacing: .02em;
  color: var(--ink-soft);
  pointer-events: none;
}

.jsp__by svg {
  width: 1.1em;
  height: 1.1em;
}

/* ── skip: barely there, a little clearer once the brand has landed ─ */

.jsp__skip {
  position: absolute;
  left: 50%;
  bottom: calc(12cqw + env(safe-area-inset-bottom));
  translate: -50% 0;
  min-height: 44px;
  padding: 0 clamp(16px, 5cqw, 24px);
  border: 1px solid currentColor;
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font: 500 clamp(13px, 3.6cqw, 15px)/1 'Jost', system-ui, sans-serif;
  letter-spacing: .14em;
  text-transform: lowercase;
  cursor: pointer;
  opacity: .22;
  transition: opacity 200ms;
  animation: jsp-skip calc(400ms * var(--jsp-speed)) ease calc(3700ms * var(--jsp-speed)) forwards;
}

.jsp__skip:hover,
.jsp__skip:focus-visible {
  opacity: 1;
  animation: none;
}

@keyframes jsp-skip {
  from { opacity: .22; }
  to { opacity: .5; }
}

/* ── 4. Into the page: one last ring carries the ground away ───────── */

/*
 | Owner, 2026-10-21, on a phone: the hole that opened through a full-screen
 | gradient was not smooth (a repaint every frame), and the brand blinked as
 | it faded out over its own copy on the wall. Now the brand does not move:
 | the wall's lockup lies exactly under it (tested to 2 px), so the swap at
 | the end cannot be seen. Around it the ground dissolves while one yellow
 | ring, the drop's ripple once more, sweeps out past the corners. Opacity
 | and transform only, so the phone's compositor does all of it.
 */
.jsp__exit {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 150vmax;
  height: 150vmax;
  margin: -75vmax 0 0 -75vmax;
  border-radius: 50%;
  border: calc(var(--jsp-ring) * 3) solid var(--accent);
  opacity: 0;
  transform: scale(.01);
  pointer-events: none;
}

.jsp.is-leaving {
  pointer-events: none;
}

.jsp.is-leaving .jsp__ground {
  animation: jsp-ground-out calc(900ms * var(--jsp-speed)) ease-out calc(80ms * var(--jsp-speed)) forwards;
}

.jsp.is-leaving .jsp__exit {
  animation: jsp-exit calc(1000ms * var(--jsp-speed)) cubic-bezier(.4, 0, .2, 1) forwards;
}

.jsp.is-leaving .jsp__ripple,
.jsp.is-leaving .jsp__lines {
  display: none;
}

.jsp.is-leaving .jsp__skip,
.jsp.is-leaving .jsp__by {
  animation: jsp-quiet calc(200ms * var(--jsp-speed)) ease forwards;
}

@keyframes jsp-ground-out {
  to { opacity: 0; }
}

/* The border is drawn thick and scaled down to a thin line, so it stays a crisp ring as it grows. */
@keyframes jsp-exit {
  0% { transform: scale(.01); opacity: 1; }
  65% { opacity: 1; }
  100% { transform: scale(1); opacity: 0; }
}

@keyframes jsp-quiet {
  to { opacity: 0; }
}

/* A skip, and the still version's way out: a plain fade. html.is-skipped is a skip that came before the script (boot.js). */
/* An animation, not a transition: WebKit held the transition's opacity at 1 and cut. Visibility flips only at the end. */
html.is-skipped .jsp,
.jsp.is-fading {
  pointer-events: none;
  animation: jsp-fade calc(320ms * var(--jsp-speed)) ease forwards;
}

@keyframes jsp-fade {
  to { opacity: 0; visibility: hidden; }
}

/* ── Reduced motion: the brand and the line, still, then a fade ──── */

@media (prefers-reduced-motion: reduce) {
  .jsp *,
  .jsp *::before,
  .jsp *::after {
    animation: none !important;
  }

  .jsp__ripple,
  .jsp__lines {
    display: none;
  }

  .jsp__skip {
    opacity: .5;
  }

  .jsp__brand::after {
    content: attr(data-line);
    margin-top: 5cqw;
    max-width: 76cqw;
    text-align: center;
    font-size: clamp(15px, 4.4cqw, 20px);
    line-height: 1.4;
    color: var(--ink);
  }
}
