/*
 * React tab host: the slot the tab renders, and the body-level layer the iframe lives
 * in. See emro-react-host.js for why the frame is not inside the tab.
 *
 * House rule from emro-workbench.css applies: no literal colours, tokens only.
 *
 * Only two moments are designed here, because everything else on screen belongs to the
 * React app inside the frame. The first is waiting: instead of a spinner, the slot
 * shows the silhouette of the screen that is coming -- a title, a filter strip, a run
 * of grid rows -- so the tab reads as "your screen is arriving" rather than "something
 * is happening somewhere". The second is failing, which is the one place with any
 * weight on it, because it is the only state the user can act on.
 */

/* ---------------------------------------------------------------- the slot */

.emro-react-slot {
	position: relative;
	display: block;
	width: 100%;
	min-height: 260px;
	overflow: hidden;
	background-color: var(--emro-color-surface);
}

/* A tab whose whole body is a React frame wants none of the shell's screen padding:
   the app inside draws its own edges, and the inherited gutter just pushed it off
   centre and cost height the frame was already short of. */
.emro-react-tab > .ui-panel-content,
.emro-react-tab.object-padding > .ui-panel-content {
	padding: 0 !important;
}

/* The template closes every tab body with three <br/>. Harmless under a normal screen,
   wrong under a full-height one: the frame is sized to end exactly at the footer, so
   ~60px of trailing breaks is pure overflow and puts back the outer scrollbar this host
   exists to remove. Declarative on purpose -- JSF rebuilds this markup on every ajax
   update, and a rule reapplies itself where a one-off script pass would not. */
.ui-tabs-panel:has(.emro-react-slot) > br {
	display: none;
}

.emro-react-slot__skeleton,
.emro-react-slot__error {
	position: absolute;
	inset: 0;
}

.emro-react-slot[data-emro-state='ready'] .emro-react-slot__skeleton,
.emro-react-slot[data-emro-state='error'] .emro-react-slot__skeleton,
.emro-react-slot[data-emro-state='loading'] .emro-react-slot__error,
.emro-react-slot[data-emro-state='ready'] .emro-react-slot__error {
	display: none;
}

/* ------------------------------------------------------------- the waiting */

.emro-react-slot__skeleton {
	display: flex;
	flex-direction: column;
	gap: var(--emro-space-3);
	padding: var(--emro-space-4);
	background-color: var(--emro-color-surface);
}

.emro-react-slot__bar {
	border-radius: var(--emro-radius-sm);
	background-color: var(--emro-color-surface-muted);
	flex: none;
}

/* The title block: one heavier bar, then a thinner one, at the weights a screen
   header actually has. */
.emro-react-slot__bar--title { width: 13rem; height: 1.125rem; }
.emro-react-slot__bar--sub { width: 22rem; height: var(--emro-font-size-sm); }

.emro-react-slot__strip {
	display: flex;
	gap: var(--emro-space-2);
	margin-top: var(--emro-space-2);
	padding-bottom: var(--emro-space-3);
	border-bottom: 1px solid var(--emro-color-border);
}

.emro-react-slot__strip .emro-react-slot__bar {
	height: var(--emro-control-height);
	border-radius: var(--emro-radius-sm);
}

.emro-react-slot__strip .emro-react-slot__bar:nth-child(1) { width: 9rem; }
.emro-react-slot__strip .emro-react-slot__bar:nth-child(2) { width: 7rem; }
.emro-react-slot__strip .emro-react-slot__bar:nth-child(3) { width: 11rem; }
.emro-react-slot__strip .emro-react-slot__bar:last-child { width: 5.5rem; margin-left: auto; }

.emro-react-slot__rows {
	display: flex;
	flex-direction: column;
	gap: 1px;
	flex: 1 1 auto;
	overflow: hidden;
}

.emro-react-slot__row {
	display: flex;
	align-items: center;
	gap: var(--emro-space-4);
	height: calc(var(--emro-control-height-coarse) * var(--emro-density));
	padding: 0 var(--emro-space-2);
	border-bottom: 1px solid var(--emro-color-border);
	flex: none;
}

/* Bars shrink rather than overflow: a narrow frame should compress the silhouette, not
   push it past the edge where the row clips mid-bar. */
.emro-react-slot__row .emro-react-slot__bar { height: var(--emro-font-size-sm); min-width: 0; }
.emro-react-slot__row .emro-react-slot__bar:nth-child(1) { flex: 0 1 6rem; }
.emro-react-slot__row .emro-react-slot__bar:nth-child(2) { flex: 1 1 30%; }
.emro-react-slot__row .emro-react-slot__bar:nth-child(3) { flex: 0 1 5rem; }
.emro-react-slot__row .emro-react-slot__bar:nth-child(4) { flex: 0 1 7rem; }

/* Rows fade toward the bottom, so the silhouette ends rather than being cut off by the
   frame edge -- the grid reads as continuing past the fold, which it will. */
.emro-react-slot__row:nth-child(n + 5) { opacity: 0.66; }
.emro-react-slot__row:nth-child(n + 7) { opacity: 0.38; }
.emro-react-slot__row:nth-child(n + 9) { opacity: 0.18; }

/* One slow sweep, left to right. It is the only motion on the page and it exists to say
   the wait is live rather than hung. */
.emro-react-slot[data-emro-state='loading'] .emro-react-slot__skeleton::after {
	content: '';
	position: absolute;
	inset: 0;
	background-image: linear-gradient(
		100deg,
		transparent 30%,
		var(--emro-color-surface-raised) 50%,
		transparent 70%
	);
	background-size: 220% 100%;
	animation: emro-react-sweep 1.8s ease-in-out infinite;
	pointer-events: none;
}

@keyframes emro-react-sweep {
	from { background-position: 140% 0; }
	to { background-position: -40% 0; }
}

/* The one piece of real information on an otherwise blank screen, so it is pinned where
   the eye lands last and carries its own surface: the silhouette scrolls under it and
   the two must never collide into an unreadable overlap. */
.emro-react-slot__note {
	position: absolute;
	left: var(--emro-space-4);
	bottom: var(--emro-space-4);
	margin: 0;
	padding: var(--emro-space-1) var(--emro-space-3);
	border-radius: var(--emro-radius-sm);
	background-color: var(--emro-color-surface);
	color: var(--emro-color-text-muted);
	font-family: var(--emro-font-family);
	font-size: var(--emro-font-size-sm);
	line-height: var(--emro-line-height);
}

@media (prefers-reduced-motion: reduce) {
	.emro-react-slot[data-emro-state='loading'] .emro-react-slot__skeleton::after {
		animation: none;
	}
}

/* ------------------------------------------------------------- the failing */

.emro-react-slot__error {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	justify-content: center;
	gap: var(--emro-space-3);
	padding: var(--emro-space-6);
	max-width: 34rem;
	background-color: var(--emro-color-surface);
	font-family: var(--emro-font-family);
}

.emro-react-slot__error h2 {
	margin: 0;
	color: var(--emro-color-text);
	font-size: var(--emro-font-size-xl);
	font-weight: 600;
	line-height: 1.25;
}

.emro-react-slot__error p {
	margin: 0;
	max-width: 46ch;
	color: var(--emro-color-text-muted);
	font-size: var(--emro-font-size-md);
	line-height: var(--emro-line-height);
}

.emro-react-slot__retry {
	margin-top: var(--emro-space-2);
	padding: 0 var(--emro-space-4);
	height: var(--emro-control-height-coarse);
	border: 0;
	border-radius: var(--emro-radius-sm);
	background-color: var(--emro-color-action);
	color: var(--emro-color-text-inverse);
	font-family: inherit;
	font-size: var(--emro-font-size-md);
	font-weight: 600;
	cursor: pointer;
}

.emro-react-slot__retry:hover { background-color: var(--emro-color-action-hover); }

.emro-react-slot__retry:focus-visible {
	outline: 2px solid var(--emro-color-focus);
	outline-offset: 2px;
}

/* --------------------------------------------------------------- the layer */

/* Parked on <body>, outside mainForm, so no PrimeFaces update can reach the iframes.
   z-index sits on --emro-z-content: above the tab body it covers, below the topbar,
   sidebar, dialogs and every overlay the shell raises over the workspace. */
.emro-react-layer {
	position: absolute;
	inset: 0 auto auto 0;
	width: 0;
	height: 0;
	z-index: var(--emro-z-content);
}

.emro-react-frame {
	position: fixed;
	overflow: hidden;
	background-color: var(--emro-color-surface);
}

.emro-react-frame[data-emro-visible='false'] {
	visibility: hidden;
	pointer-events: none;
}

/* Until the app is up, the frame stays transparent to the slot's skeleton behind it --
   a half-painted bundle should not flash over a state that is telling the truth. */
.emro-react-frame[data-emro-state='loading'],
.emro-react-frame[data-emro-state='error'] {
	opacity: 0;
	pointer-events: none;
}

.emro-react-frame__doc {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/* A postback is replacing the panel under this frame. The shell's own block overlay
   covers mainPnl, which the frame is deliberately not inside, so it gets blocked here
   instead -- otherwise you could keep typing into a screen the server is rebuilding. */
.emro-react-layer[data-emro-busy='true'] .emro-react-frame {
	pointer-events: none;
}
