/* Command motion: one calm rhythm for everything that appears or leaves.
   Only opacity and transforms animate, so nothing here changes layout.

   iOS/macOS Reduce Motion does not switch any of this off: like the navbar and
   status bars, the motion plays in full (the user's explicit choice).

   Fixed bars inside the Planet card view (header, detail panels, tab footer,
   mobile messages) must never sit inside a transformed ancestor, or Safari
   re-anchors them mid-animation. So the card view fades as a whole and only
   leaf blocks move. */

:root {
	--cmd-ease: cubic-bezier(.22, 1, .36, 1);
	--cmd-ease-in: cubic-bezier(.4, 0, .9, .6);
	--cmd-in: 420ms;
	--cmd-fade: 280ms;
	--cmd-out: 220ms;
	--cmd-shift: 10px;
	--cmd-scale: .985;
	--cmd-settle: .97;
}
@media (max-width: 800px) {
	:root { --cmd-shift: 16px; }
}

@keyframes cmd-fade-in { from { opacity: 0; } }
@keyframes cmd-fade-out { to { opacity: 0; } }
@keyframes cmd-rise-in {
	from { opacity: 0; transform: translate3d(0, var(--cmd-shift), 0) scale(var(--cmd-scale)); }
}
@keyframes cmd-sink-out {
	to { opacity: 0; transform: translate3d(0, calc(var(--cmd-shift) * .5), 0) scale(var(--cmd-scale)); }
}
@keyframes cmd-drop-in {
	from { opacity: 0; transform: translate3d(0, calc(var(--cmd-shift) * -.5), 0); }
}
@keyframes cmd-settle-in {
	from { opacity: 0; transform: scale(var(--cmd-settle)); }
}

/* ---------- Planet card view ---------- */
/* The overlays themselves never fade. An element whose opacity is below 1 is a "backdrop root":
   the blur of the overlay's ::before (command-popups.css) then has nothing to blur, so only the
   dark tint faded in and the blur switched on at the end — a two-step entrance. Instead the
   tint + blur layer and the contents fade separately, so both arrive together. */
body.dashboard-app :is(.card-view-overlay, .popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-enter::before {
	animation: cmd-fade-in var(--cmd-fade) ease-out both;
}
body.dashboard-app :is(.card-view-overlay, .popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-leave::before {
	animation: cmd-fade-out var(--cmd-out) ease-in both;
}
body.dashboard-app .card-view-overlay.command-enter > * {
	animation: cmd-fade-in var(--cmd-fade) ease-out both;
}
body.dashboard-app .card-view-overlay.command-enter .card-view-header {
	animation: cmd-drop-in var(--cmd-in) var(--cmd-ease) 40ms both;
}
body.dashboard-app .card-view-overlay.command-enter .project-image {
	animation: cmd-settle-in 520ms var(--cmd-ease) 60ms both;
}
body.dashboard-app .card-view-overlay.command-enter :is(.card-content, .single-content) > :not([data-box="messages"]):not(.dashboard-tab-content),
body.dashboard-app .card-view-overlay.command-enter .dashboard-tab-content:not([data-tab-section="messages"]) > .card-box {
	animation: cmd-rise-in var(--cmd-in) var(--cmd-ease) 110ms both;
}
body.dashboard-app .card-view-overlay.command-enter :is(.dashboard-tabs-footer, .dashboard-detail-panels, [data-box="messages"], .dashboard-tab-content[data-tab-section="messages"] > .card-box) {
	animation: cmd-fade-in var(--cmd-in) ease-out 110ms both;
}
body.dashboard-app .card-view-overlay.command-leave {
	pointer-events: none;
}
body.dashboard-app .card-view-overlay.command-leave > * {
	animation: cmd-fade-out var(--cmd-out) ease-in both;
}
body.dashboard-app .card-view-overlay.command-leave .project-image {
	animation: cmd-sink-out var(--cmd-out) var(--cmd-ease-in) both;
}

/* ---------- Popups, search and transmissions ---------- */
body.dashboard-app :is(.popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-enter > * {
	animation: cmd-fade-in var(--cmd-fade) ease-out both;
}
body.dashboard-app :is(.popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-enter .popup-content {
	animation: cmd-rise-in var(--cmd-in) var(--cmd-ease) 30ms both;
}
body.dashboard-app :is(.popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-leave {
	pointer-events: none;
}
body.dashboard-app :is(.popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-leave > * {
	animation: cmd-fade-out var(--cmd-out) ease-in both;
}
body.dashboard-app :is(.popup-overlay, .command-search-overlay, .pm-compose-overlay, .command-error-overlay).command-leave .popup-content {
	animation: cmd-sink-out var(--cmd-out) var(--cmd-ease-in) both;
}

/* ---------- Filter panel and nav (wallet) menu ----------
   Desktop: both sit at the right edge, so they slide in horizontally from that side.
   Mobile: full-screen panels drop in from the top. */
body.dashboard-app :is(.filters, #navOverlay) {
	opacity: 0;
	transform: translate3d(calc(var(--cmd-shift) * 2.4), 0, 0);
	transition: opacity var(--cmd-out) ease-in, transform var(--cmd-out) var(--cmd-ease-in), visibility 0s linear var(--cmd-out);
}
body.dashboard-app :is(.filters, #navOverlay).open {
	opacity: 1;
	transform: none;
	transition: opacity 300ms ease-out, transform var(--cmd-in) var(--cmd-ease), visibility 0s;
}
/* Desktop: the panel slides in across its own width with the grid's glide (render.js GRID_GLIDE),
   so the panel visibly pushes the cards aside: open 420ms ease-out, close 300ms, both together. */
@media (min-width: 801px) {
	body.dashboard-app :is(.filters, #navOverlay) {
		transform: translate3d(100%, 0, 0);
		transition: opacity 300ms cubic-bezier(.4, 0, .2, 1), transform 300ms cubic-bezier(.4, 0, .2, 1), visibility 0s linear 300ms;
	}
	body.dashboard-app :is(.filters, #navOverlay).open {
		transition: opacity 240ms ease-out, transform var(--cmd-in) var(--cmd-ease), visibility 0s;
	}
}
@media (max-width: 800px) {
	body.dashboard-app :is(.filters, #navOverlay) { transform: translate3d(0, calc(var(--cmd-shift) * -.6), 0); }
	body.dashboard-app :is(.filters, #navOverlay).open { transform: none; }
	/* The page behind a full-screen menu or filter panel fades instead of vanishing. */
	body.dashboard-app .site { transition: opacity var(--cmd-fade) ease-out; }
}

/* ---------- Content arriving ---------- */
/* Result cards appear with the skeleton handover and their own image fade; no extra card motion. */
/* Real content replacing a skeleton, and a newly selected detail tab. */
body.dashboard-app .command-reveal {
	animation: cmd-fade-in 360ms ease-out both;
}
body.dashboard-app .dashboard-tab-content.command-reveal:not([data-tab-section="messages"]) > .card-box {
	animation: cmd-rise-in var(--cmd-in) var(--cmd-ease) both;
}

/* Progress rings fill like the status bars when they first appear. Needs a
   registered property to interpolate (Safari 16.4+); older browsers show the
   final value. */
@property --linked-progress {
	syntax: "<percentage>";
	inherits: true;
	initial-value: 0%;
}
@keyframes cmd-ring-fill { from { --linked-progress: 0%; } }
body.dashboard-app :is(.command-enter, .command-reveal, .command-reveal-box) .planet-game-count-ring {
	animation: cmd-ring-fill 900ms var(--cmd-ease) 160ms both;
}

/* Transmission progress advances every 250ms; glide between the steps. */
body.dashboard-app .pm-progress i { transition: width 250ms linear; }

/* Images already fade in when loaded; give them the same easing. */
body.dashboard-app .project-image img { transition-duration: .32s; transition-timing-function: ease-out; }

/* ---------- Under the intro ---------- */
/* The home grid loads behind the intro doors. Hold its entrance motion until
   the doors part (command-intro.js removes html.command-intro-active), so the
   cascade, bar fills and ring fills play as the site is revealed. */
html.command-intro-active body.dashboard-app .command-enter .planet-game-count-ring {
	animation-play-state: paused;
}
html.command-intro-active body.dashboard-app .planet-status-bars.planet-status-revealed .planet-status-bar::before {
	clip-path: inset(0 100% 0 0);
}

/* ---------- Planet loader and appearance (card view and single page) ---------- */
/* The loader only appears when loading takes longer than 1s (render.js adds
   .planet-loader-shown) and fades in slowly, so a quick Planet never flashes it and a
   Planet ready just after that point only shows a faint hint of it. A visible loader fades
   out first, then the Planet fades in while settling to size. */
body.dashboard-app .dashboard-planet-detail > .project-image.planet-iframe-media > .dashboard-planet-artwork::before {
	opacity: 0;
	transition: opacity .6s ease-in;
}
body.dashboard-app .dashboard-planet-detail > .project-image.planet-iframe-media.planet-loader-shown:not(.loaded):not(.load-failed) > .dashboard-planet-artwork::before {
	opacity: 1;
}
body.dashboard-app .dashboard-planet-detail > .project-image.planet-iframe-media:is(.loaded, .load-failed) > .dashboard-planet-artwork::before {
	opacity: 0;
	transition: opacity .24s ease-in;
}
body.dashboard-app .dashboard-planet-detail > .project-image.planet-iframe-media.loaded iframe[data-planet-media="1"] {
	transition: none;
}
body.dashboard-app .dashboard-planet-detail > .project-image.planet-iframe-media iframe[data-planet-media="1"].planet-reveal {
	animation: cmd-planet-in 900ms var(--cmd-ease) var(--planet-reveal-delay, 0ms) both;
}
@keyframes cmd-planet-in {
	from { opacity: 0; transform: scale(var(--cmd-planet-from, .94)); }
}

/* ---------- Transmissions: scanning, empty and first messages ---------- */
body.dashboard-app .pm-feed > .pm-empty-loading-transmissions {
	animation: cmd-fade-in 400ms ease-out 400ms both;
}
body.dashboard-app .pm-feed > .pm-empty-loading-transmissions.pm-leaving {
	animation: cmd-fade-out 220ms ease-in both;
}
body.dashboard-app .pm-feed > .pm-empty-no-transmissions:not(.pm-from-scan) {
	animation: cmd-fade-in 450ms ease-out both;
}
/* From the scanner: the same dish stops scanning in place; only its label crossfades. */
body.dashboard-app .pm-feed > .pm-empty-no-transmissions.pm-from-scan > .pm-empty-label {
	animation: cmd-fade-in 450ms ease-out both;
}
body.dashboard-app .pm-feed.pm-feed-reveal > .pm-msg {
	animation: cmd-fade-in 450ms ease-out both;
}
/* The create-message area appears together with the first messages: it waits for the scanner
   to fade out (220ms) and then fades in on the same curve. */
body.dashboard-app .pm-composer-reveal {
	animation: cmd-fade-in 450ms ease-out 220ms both;
}

/* The site's own Ordinal logo stays in place above the intro doors, grey, and
   turns white as the doors part; the other header controls fade in then. */
html.command-intro-active body.dashboard-app > header {
	z-index: 10000;
	background-color: transparent;
	/* command-theme.css sets every border colour with !important */
	border-bottom-color: transparent !important;
	pointer-events: none;
}
/* While the page is still loading, the header's bottom border showed for a moment and then
   disappeared once the grid or Planet view existed (dashboard-layout.css removes it for
   those views). Command views never show it, so it stays off while loading too. */
body.dashboard-app > header.loading-active { border-bottom-width: 0; }
/* As the doors part, the header bar and its border fade in with the controls (no snap). */
body.dashboard-app > header { transition: background-color .6s ease .4s, border-bottom-color .6s ease .4s; }
html.command-intro-active body.dashboard-app > header #logoLink { color: var(--command-grey, #727578); }
html.command-intro-active body.dashboard-app > header #logoLink i { color: inherit; }
html.command-intro-active body.dashboard-app > header .top > :not(#logoLink) { opacity: 0; }
body.dashboard-app > header #logoLink,
body.dashboard-app > header #logoLink i { transition: color .9s ease; }
body.dashboard-app > header .top > :not(#logoLink) { transition: opacity .6s ease .4s; }
/* Skip the fun: no doors to wait for, so the header fades in with the rest of the site. */
html.command-intro-skipped body.dashboard-app > header { transition: background-color .55s ease, border-bottom-color .55s ease; }
html.command-intro-skipped body.dashboard-app > header .top > :not(#logoLink) { transition: opacity .55s ease; }

/* Hero moment when a detail Planet appears: a soft bloom from the centre and a
   single diagonal light sweep, after the early-2000s Flash reveals. Tinted with
   the Planet's dimension colour (render.js sets --planet-hero-rgb). */
body.dashboard-app .dashboard-planet-artwork > .planet-hero-light {
	position: absolute;
	inset: 0;
	z-index: 4;
	pointer-events: none;
	overflow: hidden;
	background: radial-gradient(circle at 50% 50%, rgba(var(--planet-hero-rgb, 255, 255, 255), .2), rgba(var(--planet-hero-rgb, 255, 255, 255), 0) 55%);
	opacity: 0;
	animation: cmd-hero-bloom 1100ms ease-out var(--planet-reveal-delay, 0ms) both;
}
body.dashboard-app .dashboard-planet-artwork > .planet-hero-light::after {
	content: "";
	position: absolute;
	top: -20%;
	bottom: -20%;
	left: 0;
	width: 38%;
	background: linear-gradient(105deg, rgba(var(--planet-hero-rgb, 255, 255, 255), 0) 0%, rgba(var(--planet-hero-rgb, 255, 255, 255), .11) 50%, rgba(var(--planet-hero-rgb, 255, 255, 255), 0) 100%);
	transform: translate3d(-120%, 0, 0) skewX(-12deg);
	animation: cmd-hero-sweep 1000ms var(--cmd-ease) calc(var(--planet-reveal-delay, 0ms) + 120ms) both;
}
@keyframes cmd-hero-bloom { 0% { opacity: 0; } 25% { opacity: 1; } 100% { opacity: 0; } }
@keyframes cmd-hero-sweep { to { transform: translate3d(330%, 0, 0) skewX(-12deg); } }

/* ---------- Grid: filter and owner changes ---------- */
/* render.js markGridSwap(): the shimmer fades in over the old cards, then the new
   cards fade in with a slight lift. Only opacity/transform; no stagger. */
@keyframes cmd-grid-in {
	from { opacity: 0; transform: translate3d(0, 6px, 0); }
}
body.dashboard-app #results.command-grid-wait > .skeleton-card {
	animation: cmd-fade-in 260ms ease-out both;
}
body.dashboard-app #results.command-grid-reveal > .collection-card:not(.skeleton-card) {
	animation: cmd-grid-in 420ms var(--cmd-ease) both;
}
/* The results count, while it loads: a small shimmer bone in place of the text. */
body.dashboard-app #idList .showing > .skeleton-bone {
	display: inline-block;
	border-radius: 4px;
}
