/* =========================================================================
   IzeChat — Runaway Login  (isolated · additive · presentation only)
   -------------------------------------------------------------------------
   A playful "not so fast" on the auth submit button: while the form is still
   empty the button dodges, dampens to half travel once one field is filled,
   and parks itself the moment the form is complete. Two modes share this one
   transform — .ize_ry_chase (a mouse approaching) and .ize_ry_tap (a finger
   that has just tapped) — because a finger cannot hover and so cannot be
   fled from. See js/ize-runaway.js.

   Everything here is gated behind .ize_ry_on, which ize-runaway.js adds ONLY
   after its policy gate passes. Under Reduce Motion, on a lean device, or
   with JS disabled, not one declaration in this file applies and the button
   renders exactly as the theme drew it. That is the failsafe: the gag can
   never break login.

   Composition note — the theme already animates .ize_btn's transform on
   :hover (lift) and :active (squash). Two rules cannot own one transform, so
   those states are re-expressed here as variables that feed the SINGLE
   transform below. Loaded after ize-auth.css so the .ize_btn.ize_ry_on tie
   resolves in our favour.
   ========================================================================= */

.ize_btn.ize_ry_on {
	/* JS writes these three inline; everything else is CSS-owned state. */
	--ize-ry-x: 0px;
	--ize-ry-y: 0px;
	--ize-ry-r: 0deg;
	/* Hover/press, kept out of JS's hands so the two never fight. */
	--ize-ry-lift: 0px;
	--ize-ry-squash: 1;
	--ize-ry-dur: .14s;
	--ize-ry-ease: var(--ize-mo-e-glide, cubic-bezier(.22, .61, .36, 1));

	transform: translate3d(var(--ize-ry-x), calc(var(--ize-ry-y) + var(--ize-ry-lift)), 0)
	           rotate(var(--ize-ry-r))
	           scale(var(--ize-ry-squash));
	transition: transform  var(--ize-ry-dur) var(--ize-ry-ease),
	            box-shadow .25s var(--ize-mo-e-standard, cubic-bezier(.2, 0, 0, 1)),
	            filter     .2s;
}

/* The compositor layer costs memory, so only the mode that can move at any
   moment holds one standing. Chase (desktop) keeps it while armed; tap (phone)
   takes one on touchstart — .ize_ry_hot — which lands a good hundred
   milliseconds before the click that actually moves the button, and gives it
   back when the hop is over. Promoting a layer at the same instant you animate
   is too late to help, which is exactly why touchstart does this and nothing
   else. */
.ize_btn.ize_ry_chase,
.ize_btn.ize_ry_hot { will-change: transform; }

.ize_btn.ize_ry_on:hover  { --ize-ry-lift: -2px; }
.ize_btn.ize_ry_on:active { --ize-ry-lift: 0px; --ize-ry-squash: .985; }

/* ------------------------------------------------------------ Keyboard
   The hard accessibility guarantee. Tab lands on the button and it is pinned
   instantly, whatever the pointer is doing: an author !important declaration
   outranks the normal inline values JS is writing, so no JS cooperation is
   needed for this to hold (ize-runaway.js stands down as well, but the CSS
   alone is sufficient). The button is never disabled, so Enter submits. */
.ize_btn.ize_ry_on:focus-visible {
	--ize-ry-x: 0px   !important;
	--ize-ry-y: 0px   !important;
	--ize-ry-r: 0deg  !important;
	--ize-ry-dur: .2s !important;
	outline: 3px solid var(--ize-ring, rgba(45, 108, 255, .35));
	outline-offset: 3px;
}

/* ------------------------------------------------------------- Fleeing
   While displaced the button reads as lifted OUT of the card rather than
   broken off it: raised above the card's siblings with a deeper shadow. */
.ize_btn.ize_ry_fleeing {
	z-index: 6;
	box-shadow: 0 22px 40px -14px rgba(var(--ize-rgb, 45, 108, 255), .72);
}

/* -------------------------------------------------------------- Parked
   Both fields filled: home, locked, and acknowledged with one quiet ring
   pulse so "it stopped running" reads as intentional, not as a glitch.
   Beats .ize_btn:hover's izeShift on specificity, so the pulse survives a
   hovering cursor. */
.ize_btn.ize_ry_on.ize_ry_locked,
.ize_btn.ize_ry_on.ize_ry_done { will-change: auto; }

.ize_btn.ize_ry_on.ize_ry_lock {
	animation: izeRyLock .58s var(--ize-mo-e-spring, cubic-bezier(.34, 1.4, .64, 1)) 1;
}
@keyframes izeRyLock {
	0%   { box-shadow: 0 12px 26px -10px rgba(var(--ize-rgb, 45, 108, 255), .6), 0 0 0 0   rgba(var(--ize-rgb, 45, 108, 255), .45); }
	100% { box-shadow: 0 12px 26px -10px rgba(var(--ize-rgb, 45, 108, 255), .6), 0 0 0 15px rgba(var(--ize-rgb, 45, 108, 255), 0); }
}

/* --------------------------------------------------------- Preferences
   Belt and braces: the JS never mounts under Reduce Motion, and ize-auth.css
   already kills transitions inside .ize_auth. If either ever changes, the
   button still must not move. */
@media (prefers-reduced-motion: reduce) {
	.ize_btn.ize_ry_on {
		--ize-ry-x: 0px !important;
		--ize-ry-y: 0px !important;
		--ize-ry-r: 0deg !important;
		will-change: auto;
	}
	.ize_btn.ize_ry_on.ize_ry_lock { animation: none; }
}

/* Mode is decided once, at mount. If a hybrid device flips to touch after
   that, the CHASE field must not be left hanging over a finger that can never
   trigger it — it is pinned home. Tap mode is untouched: its hop is a reaction
   to a completed tap, which is exactly what a coarse pointer produces. */
@media (hover: none), (pointer: coarse) {
	.ize_btn.ize_ry_chase {
		--ize-ry-x: 0px !important;
		--ize-ry-y: 0px !important;
		--ize-ry-r: 0deg !important;
		will-change: auto;
	}
}

/* =========================================================================
   THE DOCK — home is a slot, not a copy of the button
   -------------------------------------------------------------------------
   The button stops being a thing that sits in the form and becomes a thing
   that BELONGS somewhere: a socket holds its place in the layout, and an
   elastic band keeps it tethered to that socket however far it runs. Three
   jobs, and the first is the one that matters most:

     1. .ize_dock_socket owns the layout box. Because the socket never moves,
        the engine can read the button's true home position at any instant
        without ever neutralising a live transform — no measuring tricks, no
        stale anchor after the error block pushes the form down.
     2. It gives the escape somewhere to escape FROM. A button that drifts
        with nothing left behind just looks broken; a button that has visibly
        left its slot reads as deliberate.
     3. It gives the band two endpoints to stretch between.

   Not one new colour is introduced here. Every value below is derived from
   --main / --main-hover / --ize-rgb, the same brand tokens the CTA gradient
   already uses, so the dock is whatever colour IzeChat is.
   ========================================================================= */

.ize_dock { position: relative; }
.ize_dock_socket { display: block; width: 100%; overflow: visible; }
.ize_dock_band { display: none; }

/* --- armed ------------------------------------------------------------- */

.ize_dock.ize_dock_live {
	/* 0 at home -> 1 at full stretch. JS writes it; everything below reacts. */
	--ize-ry-away: 0;
	--ize-ry-brand:   var(--main, #2D6CFF);
	--ize-ry-brand-2: var(--main-hover, #1B4FD8);
}

/* The socket itself: a shallow well in brand tint, lit from inside as the
   button pulls away from it. */
.ize_dock.ize_dock_live .ize_dock_socket {
	position: relative;
	border-radius: var(--ize-radius-sm, 14px);
	display: flex; justify-content: center;
	background: rgba(var(--ize-rgb, 45, 108, 255), calc(.05 + .05 * var(--ize-ry-away)));
	box-shadow:
		inset 0 0 0 1px rgba(var(--ize-rgb, 45, 108, 255), calc(.14 + .26 * var(--ize-ry-away))),
		inset 0 2px 6px -2px rgba(10, 14, 30, .10);
	transition: background .3s var(--ize-mo-e-glide, cubic-bezier(.22, .61, .36, 1)),
	            box-shadow .3s var(--ize-mo-e-glide, cubic-bezier(.22, .61, .36, 1));
}

/* The glow the button leaves behind. Sits under the button, so it only ever
   reads once the button has actually gone somewhere. */
.ize_dock.ize_dock_live .ize_dock_socket::after {
	content: "";
	position: absolute; inset: -1px;
	border-radius: inherit;
	background: radial-gradient(60% 120% at 50% 50%,
		rgba(var(--ize-rgb, 45, 108, 255), .30) 0%,
		rgba(var(--ize-rgb, 45, 108, 255), 0) 72%);
	opacity: var(--ize-ry-away);
	pointer-events: none;
	transition: opacity .28s var(--ize-mo-e-glide, cubic-bezier(.22, .61, .36, 1));
}

/* --- the band ----------------------------------------------------------
   No viewBox on purpose: one user unit is then one CSS pixel with its origin
   at the dock's top-left corner, which is exactly the space the engine
   already computes displacement in. overflow:visible lets it draw outside
   the dock, because the button does too. */
.ize_dock.ize_dock_live .ize_dock_band {
	display: block;
	position: absolute; inset: 0;
	width: 100%; height: 100%;
	overflow: visible;
	pointer-events: none;
	z-index: 0;
}
.ize_dock_band_line {
	fill: none;
	stroke-linecap: round;
	stroke-width: var(--ize-ry-band-w, 2.4);
	opacity: var(--ize-ry-band-o, 0);
	filter: drop-shadow(0 2px 6px rgba(var(--ize-rgb, 45, 108, 255), .38));
}
/* The button rides above its own tether. */
.ize_dock.ize_dock_live .ize_dock_socket { z-index: 1; }

/* --- loose vs seated ---------------------------------------------------
   Chase mode only, and this is the reading that makes the whole thing mean
   something: while the form is incomplete the button is LOOSE — narrower
   than its socket, with room to run inside it. Complete the form and it
   seats: it settles into the slot and fills it, and IzeChat's full-width
   brand CTA is back, in the one state where it matters. Touch keeps the
   full-width CTA throughout; a thumb has no cursor to be teased by, and
   nearly all of our users are on it. */
.ize_dock.ize_dock_live.ize_dock_chase .ize_btn {
	width: var(--ize-ry-loose, 72%);
	transition: transform var(--ize-ry-dur) var(--ize-ry-ease),
	            width .38s var(--ize-mo-e-spring, cubic-bezier(.34, 1.4, .64, 1)),
	            box-shadow .25s var(--ize-mo-e-standard, cubic-bezier(.2, 0, 0, 1)),
	            filter .2s;
}
.ize_dock.ize_dock_live.ize_dock_chase.ize_dock_seated .ize_btn { width: 100%; }

/* --- the hint ----------------------------------------------------------
   The gag explains itself. Without a line of copy a fleeing button is a bug
   report; with one it is a joke the user is in on. */
.ize_dock_hint {
	display: flex; align-items: center; justify-content: center; gap: 7px;
	margin: 11px 0 0;
	font-size: 12.5px; line-height: 1.4; font-weight: 500;
	color: var(--ize-ink-3, #8a90a3);
	text-align: center;
	transition: opacity .3s var(--ize-mo-e-glide, cubic-bezier(.22, .61, .36, 1));
}
.ize_dock_hint::before {
	content: "";
	width: 5px; height: 5px; border-radius: 50%; flex: none;
	background: var(--ize-ry-brand, var(--main, #2D6CFF));
}
.ize_dock_hint.is-done { opacity: 0; }

/* --- stand-downs -------------------------------------------------------
   Same rule as the rest of this file: if the effect is not armed, the dock
   is nothing but a <div> around a button. */
@media (prefers-reduced-motion: reduce) {
	.ize_dock.ize_dock_live .ize_dock_band { display: none; }
	.ize_dock.ize_dock_live.ize_dock_chase .ize_btn { width: 100%; }
}
@media (hover: none), (pointer: coarse) {
	.ize_dock.ize_dock_live.ize_dock_chase .ize_btn { width: 100%; }
}
