/* ============================================
   RiverReady — transitions
   A wave rises to cover the screen, the panels swap behind it,
   then it drains back down. Same metaphor as the rest of the app:
   the water comes up, the water goes down.
   ============================================ */

.wipe {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  height: 100%;
  z-index: 60;
  pointer-events: none;
  transform: translateY(100%);
  will-change: transform;
}

/* Two layers, same as the backdrop waves. The lighter one leads by a
   fraction so the crest reads as depth rather than one flat shape. */
.wipe--back  { z-index: 60; }
.wipe--front { z-index: 61; }

.wipe-body { position: absolute; inset: 0; }

/* Gradients rather than flat fills, so the sweep has some depth to it
   instead of reading as two colored rectangles. */
.wipe--back  .wipe-body { background: linear-gradient(180deg, #a8d3ef 0%, #7fb8de 100%); }
.wipe--front .wipe-body { background: linear-gradient(180deg, #3d7fb5 0%, #245c8c 100%); }

/* The crest sits above the body, so it clears the screen edge first. */
.wipe-crest {
  position: absolute;
  bottom: 100%;
  left: 0;
  width: 100%;
  /* Scales with the screen. A fixed 74px crest was half the height of a
     phone's viewport and looked squashed against the bottom. */
  height: clamp(18px, 5vh, 44px);
  display: block;
}

.wipe--back  .wipe-crest path { fill: #a8d3ef; }
.wipe--front .wipe-crest path { fill: #3d7fb5; }

/* Foam. Cartoon water needs bubbles or it reads as a colored rectangle
   with a wobbly edge. */


/* Rising is the slower half — that's the part you actually watch.
   Draining is quicker, because by then you want the next screen. */
/* Quick enough to read as a transition rather than an animation you sit
   through. The old 620/520 was noticeable by the tenth tab switch. */
.wipe--rise { transition: transform 460ms cubic-bezier(0.4, 0, 0.2, 1); }
.wipe--fall { transition: transform 380ms cubic-bezier(0.4, 0, 1, 1); }

.wipe--up { transform: translateY(0); }

/* The panel underneath comes up a touch as the water drains off it. */
.panel--fresh { animation: surface 420ms cubic-bezier(0.22, 1, 0.36, 1); }

@keyframes surface {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}


/* ============================================
   Loader — the logo, with the ring spinning
   ============================================ */

.loader {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 48px 0 60px;
}

body.is-loading .loader { display: flex; }
body.is-loading #roads,
body.is-loading #status { display: none; }

.loader-mark { width: 62px; height: 62px; }

/* One short arc chasing round the circle. The dash is deliberately
   much shorter than the 170-unit circumference so it reads as a
   spinner rather than a rotating ring. */
.loader-ring {
  fill: none;
  stroke: var(--deep);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 42 128;
  transform-origin: 32px 32px;
  animation: chase 1.15s linear infinite;
}

@keyframes chase {
  to { transform: rotate(360deg); }
}

/* The rest of the mark holds still and the wave inside bobs, so the
   logo stays legible instead of tumbling. */
.loader-wave {
  fill: none;
  stroke: var(--deep);
  stroke-width: 4;
  stroke-linecap: round;
  animation: bob 2.1s ease-in-out infinite;
}

@keyframes bob {
  0%, 100% { transform: translateY(0.6px); }
  50%      { transform: translateY(-1.8px); }
}

.loader-road {
  fill: none;
  stroke: var(--riverbed);
  stroke-width: 3.5;
  stroke-linecap: round;
}

.loader-pier {
  fill: none;
  stroke: var(--riverbed);
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0.45;
}

.loader-text {
  margin: 0;
  font-size: 14px;
  color: var(--ink-60);
}

@media (prefers-reduced-motion: reduce) {
  .wipe { display: none; }
  .panel--fresh { animation: none; }
  .loader-ring { animation: none; stroke-dasharray: none; opacity: 0.3; }
  .loader-wave { animation: none; }
}
