/* ============================================================
   Kai — SCREEN TRANSITION LAYER

   One principle: the app is a single continuous surface and money
   flows across it. Two things therefore NEVER transition —
   the background and the nav. Everything else moves relative to them.

   Five transition types, keyed to the RELATIONSHIP between screens:
     LATERAL  peer → peer      (tab to tab)
     DRILL    parent → child   (list → detail)
     FLOW     fiat ↔ crypto    ← the signature
     SHEET    anything → modal (amount entry, pickers)
     REPLACE  step → step      (inside a flow)
   ============================================================ */

/* ---------- the two layers that never move ---------- */
.bg-layer {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(180deg, #FFFFFF 0%, #C0D4D7 67.9%);
  overflow: hidden;
}

/* ---------- the transitioning viewport ---------- */
.viewport {
  position: absolute; inset: 0; z-index: 5;
  overflow: hidden;
  transform-origin: 50% 100%;
  transition:
    transform var(--t-sheet) var(--ease-ios),
    filter    var(--t-sheet) var(--ease-ios),
    border-radius var(--t-sheet) var(--ease-ios);
}
/* pushed back when a sheet is up — the iOS stack idiom users already trust */
.phone.sheet-up .viewport {
  transform: scale(.935) translateY(-10px);
  filter: brightness(.86);
  border-radius: 28px;
}

.scr {
  position: absolute; inset: 0;
  display: none;
  will-change: transform, opacity, filter;
}
.scr.on   { display: block; }
.scr.exit { display: block; z-index: 1; }   /* outgoing stays painted mid-flight */
.scr.on   { z-index: 2; }

.scr-scroll {
  position: absolute; left: 0; top: 109px; width: 402px; height: 665px;
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: none; padding-bottom: 110px;
}
.scr-scroll::-webkit-scrollbar { display: none; }

/* ============================================================
   1 · LATERAL — peer to peer
   18% travel, not 100%. Reads as the viewport shifting over one
   continuous surface rather than two separate cards swapping.
   ============================================================ */
@keyframes lat-out-l { to { transform: translateX(-18%) scale(.955); opacity: 0; } }
@keyframes lat-out-r { to { transform: translateX( 18%) scale(.955); opacity: 0; } }
@keyframes lat-in-l  { from { transform: translateX(-18%) scale(.955); opacity: 0; } }
@keyframes lat-in-r  { from { transform: translateX( 18%) scale(.955); opacity: 0; } }

.scr[data-t="lateral-out-l"] { animation: lat-out-l var(--t-lateral) var(--ease-ios) both; }
.scr[data-t="lateral-out-r"] { animation: lat-out-r var(--t-lateral) var(--ease-ios) both; }
.scr[data-t="lateral-in-l"]  { animation: lat-in-l  var(--t-lateral) var(--ease-ios) both; }
.scr[data-t="lateral-in-r"]  { animation: lat-in-r  var(--t-lateral) var(--ease-ios) both; }

/* ============================================================
   2 · DRILL — parent to child
   Depth, not sideways. The parent recedes and stays visible
   underneath so the child reads as sitting ON it.
   ============================================================ */
@keyframes drill-out { to   { transform: scale(.945); opacity: .25; filter: blur(6px); } }
@keyframes drill-in  { from { transform: translateY(3.5%) scale(.94); opacity: 0; filter: blur(6px); } }
@keyframes drill-back-out { to   { transform: translateY(4%) scale(.94); opacity: 0; filter: blur(5px); } }
@keyframes drill-back-in  { from { transform: scale(.955); opacity: .3; filter: blur(4px); } }

.scr[data-t="drill-out"] { animation: drill-out var(--t-drill) var(--ease-ios) both; }
.scr[data-t="drill-in"]  { animation: drill-in  var(--t-drill) var(--ease-ios) both; }
/* back is faster and smaller — exits should never ask for the same attention */
.scr[data-t="drillBack-out"] { animation: drill-back-out var(--t-drill-back) var(--ease-ios) both; }
.scr[data-t="drillBack-in"]  { animation: drill-back-in  var(--t-drill-back) var(--ease-ios) both; }

/* ============================================================
   3 · FLOW — fiat ↔ crypto  ★ the signature
   Not a slide. The old screen dissolves UNDER a travelling band of
   light tinted with the destination's currency colour, while the
   balance figure carries across and re-rolls to its new value.
   Money is not swapped here. It is converted.
   ============================================================ */
@keyframes flow-out { to   { opacity: 0; filter: blur(7px) saturate(.5); transform: translateX(var(--flow-dx, 5%)) scale(.975); } }
@keyframes flow-in  { from { opacity: 0; filter: blur(7px) saturate(.5); transform: translateX(calc(var(--flow-dx, 5%) * -1)) scale(.975); } }

.scr[data-t="flow-out"] { animation: flow-out var(--t-flow) var(--ease-ios) both; }
.scr[data-t="flow-in"]  { animation: flow-in  var(--t-flow) var(--ease-ios) both; }

.flow-sweep {
  position: absolute; top: -12%; bottom: -12%; left: -55%; width: 75%;
  z-index: 40; pointer-events: none; opacity: 0;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--sweep-soft) 34%,
    var(--sweep-hot)  50%,
    var(--sweep-soft) 66%,
    transparent 100%);
  filter: blur(26px);
  /* NOT plus-lighter — on a light UI additive blending washes out to nothing.
     A plain tinted wash is what actually reads as coloured light here. */
  mix-blend-mode: normal;
}
.flow-sweep.run  { animation: sweep-lr var(--t-flow) var(--ease-ios) both; }
.flow-sweep.run.rtl { animation-name: sweep-rl; }
@keyframes sweep-lr {
  0%   { transform: translateX(0);    opacity: 0; }
  22%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { transform: translateX(240%); opacity: 0; }
}
@keyframes sweep-rl {
  0%   { transform: translateX(240%); opacity: 0; }
  22%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { transform: translateX(0);    opacity: 0; }
}

/* currency glyph cross-fade on the carried value */
.glyph-swap { display: inline-block; position: relative; }
.glyph-swap > span { display: inline-block; }
.glyph-swap .gl-out {
  position: absolute; left: 0; top: 0;
  animation: glyph-out var(--t-flow) var(--ease-ios) both;
}
.glyph-swap .gl-in { animation: glyph-in var(--t-flow) var(--ease-ios) both; }
@keyframes glyph-out { to   { opacity: 0; transform: translateY(-.14em) scale(.8); filter: blur(5px); } }
@keyframes glyph-in  { from { opacity: 0; transform: translateY( .14em) scale(.8); filter: blur(5px); } }

/* ============================================================
   4 · SHEET — modals, pickers, amount entry
   Container lands FIRST, content fills after. That 300ms offset is
   the whole difference between "built" and "popped".
   ============================================================ */
.sheet-layer { position: absolute; inset: 0; z-index: 30; pointer-events: none; }
.scrim {
  position: absolute; inset: 0; background: rgba(9,28,47,.3);
  opacity: 0; transition: opacity var(--t-sheet) var(--ease-ios);
  backdrop-filter: blur(0px);
}
.phone.sheet-up .scrim { opacity: 1; pointer-events: auto; backdrop-filter: blur(2px); }

.sht {
  position: absolute; left: 0; right: 0; bottom: 0;
  border-radius: 26px 26px 0 0;
  background: linear-gradient(180deg, #F7FAFB 0%, #DCE7E9 100%);
  box-shadow: 0 -20px 60px rgba(10,36,38,.28);
  transform: translateY(100%);
  transition: transform var(--t-sheet) var(--ease-ios);
  pointer-events: auto;
  display: none;
  padding: 10px 20px 28px;
  max-height: 86%;
}
.sht.mounted { display: block; }
.sht.up { transform: translateY(0); }

.sht-grip {
  width: 38px; height: 4px; border-radius: 4px;
  background: rgba(14,33,41,.22); margin: 0 auto 16px;
}

/* content fills after the container lands */
.sht .stg { opacity: 0; }
.sht.up .stg {
  animation: enter-block calc(380ms * var(--speed)) var(--ease-settle) both;
  animation-delay: calc((300ms + var(--i, 0) * 60ms) * var(--speed));
}
/* ...but only ONCE. Once the sheet is settled, a step change must not make the
   content wait for a container that is already there — the REPLACE animation
   on the step block is the whole motion at that point. */
.sht.up.settled .stg,
.sht.up.settled .pad-key { animation: none !important; opacity: 1; }

/* keypad arrives as a 3-column wave, not as a block */
.pad-key { opacity: 0; }
.sht.up .pad-key {
  animation: key-in calc(320ms * var(--speed)) var(--ease-settle) both;
  animation-delay: calc((380ms + var(--k, 0) * 28ms) * var(--speed));
}
@keyframes key-in {
  from { opacity: 0; transform: translateY(14px) scale(.92); filter: blur(3px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

/* ============================================================
   5 · REPLACE — step to step inside a flow
   These repeat many times in a row. Anything larger gets tiring
   by the third screen, so this one stays deliberately small.
   ============================================================ */
@keyframes rep-out { to   { opacity: 0; transform: translateY(-14px); filter: blur(4px); } }
@keyframes rep-in  { from { opacity: 0; transform: translateY( 14px); filter: blur(4px); } }
.step[data-t="replace-out"] { animation: rep-out var(--t-replace) var(--ease-state) both; }
.step[data-t="replace-in"]  { animation: rep-in  var(--t-replace) var(--ease-state) both; }
.step { display: none; }
.step.on, .step[data-t] { display: block; }

.step-bar { display: flex; gap: 5px; margin-bottom: 18px; }
.step-bar i {
  flex: 1; height: 3px; border-radius: 3px; background: rgba(14,33,41,.14);
  transition: background var(--t-replace) var(--ease-state);
}
.step-bar i.done { background: var(--teal); }

/* ============================================================
   NAV — part of the frame, never part of the transition.
   Only the active indicator moves.
   ============================================================ */
.nav-wrap { z-index: 20; }
.nav-item .nav-dot {
  position: absolute; left: 50%; bottom: 6px; width: 4px; height: 4px;
  border-radius: 50%; background: var(--ink);
  transform: translateX(-50%) scale(0);
  transition: transform var(--d-fast) var(--ease-settle);
}
.nav-item.active .nav-dot { transform: translateX(-50%) scale(1); }

/* ============================================================
   REDUCED MOTION — no movement, but never no transition.
   Stripping the change entirely makes navigation feel broken.
   ============================================================ */
.reduced .scr[data-t] { animation-duration: calc(180ms * var(--speed)) !important; }
.reduced .scr[data-t="lateral-out-l"],
.reduced .scr[data-t="lateral-out-r"],
.reduced .scr[data-t="drill-out"],
.reduced .scr[data-t="drillBack-out"],
.reduced .scr[data-t="flow-out"]  { animation-name: rm-out !important; }
.reduced .scr[data-t="lateral-in-l"],
.reduced .scr[data-t="lateral-in-r"],
.reduced .scr[data-t="drill-in"],
.reduced .scr[data-t="drillBack-in"],
.reduced .scr[data-t="flow-in"]   { animation-name: rm-in !important; }
@keyframes rm-out { to   { opacity: 0; } }
@keyframes rm-in  { from { opacity: 0; } }
.reduced .flow-sweep { display: none; }
.reduced .phone.sheet-up .viewport,
.reduced.phone.sheet-up .viewport { transform: none; filter: brightness(.9); }
.reduced .sht .stg, .reduced .pad-key { animation: rm-in calc(160ms * var(--speed)) both !important; }
