/* Network view chrome — shared by both graph surfaces in Step 3.
 *
 * Step 3 draws two networks and, until this file, they had nothing in common
 * but the word "network":
 *
 *   Pathways network (KEGG / Reactome / MapMan) — PA_Step3Views.js, sigma.js.
 *     Its toolbar was a row of bare anchors floated left inside a 30px box with
 *     `margin-bottom: -20px`, so the strip sat *on top of* the diagram it
 *     controls. No control had a shape; the only feedback was the text
 *     changing colour. The two download links were not in the bar at all —
 *     they floated above the card's own title.
 *
 *   MORE Regulator–Target network — PA_Step3RegTargetNetworkView.js,
 *     Cytoscape.js. Its toolbar was a proper band with a surface, a border and
 *     real buttons, written against design-system tokens — but half of those
 *     tokens (`--pa-surface-toolbar`, `--pa-ink-control`, …) were only ever
 *     declared in dark.css, so in light mode every one of them fell through to
 *     a hardcoded cold grey. The panel that looked the most designed was the
 *     one least connected to the palette.
 *
 * Two graph views on the same page should not be two products. This file is
 * the one description of what a network toolbar, its status line and its
 * canvas look like, and both views are styled from it: selectors name the
 * `.pa-net-*` classes used by the pathways view and the `.more-net-*` classes
 * used by the MORE view together, so a change here cannot drift between them.
 * What stays local to each view is what genuinely differs — MORE's legend and
 * hub list in more-network.css, the pathways view's hover menus and lateral
 * panels in main.css.
 *
 * Everything below resolves through tokens declared for both themes, so dark
 * mode needs no counterpart block.
 */

/* ---- The toolbar band --------------------------------------------------- */

/* Flex rather than floats. The float version could not express "these belong
   to the left, those to the right" without an inline `float: right` on each
   item — which a `float: left !important` in main.css then overrode anyway, so
   the two groups had silently collapsed into one undifferentiated row. That is
   the row in the screenshot this work started from. */
/* Spacing is margin on the items, not `gap` on the container. The pathways
   toolbar holds three hover menus whose trigger is hidden until nodes are
   selected; they are zero-width most of the time, and `gap` charges 4px for
   each of them anyway. That was enough to push the last control onto a second
   row on a 660px panel. A zero-width item with margins costs zero. */
.pa-net-toolbar,
.more-net-toolbar,
.omnipath-net-toolbar,
.pa-hub-net-toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0;
	/* The bar's own inset is derived, not chosen, because what has to land on
	   the card's rail is the first control's *icon* - not the bar, and not the
	   control's box. A tool carries 1px of margin, 1px of transparent border and
	   8px of padding before its icon starts, so the bar is inset by the card
	   rail less those 10px and the icon comes out on 26px exactly. The same
	   subtraction on the right puts the last control's icon on the right rail.

	   Written as 8px this card stacked three different left edges: the `h2` on
	   --pa-card-inset (26px, so x=293 on a 1440px window), the first tool's icon
	   on 18px (285), and the status line under it on 12px (279). Three rails
	   inside one card, none of them agreeing with the heading that names it. */
	padding: 6px calc(var(--pa-card-inset, 26px) - 10px);
	background: var(--pa-surface-toolbar, #F6F5F0);
	border-bottom: 1px solid var(--pa-border);
	font-size: 12px;
	color: var(--pa-ink-label, #52525B);
	/* The hover menus and the reorder popover are absolutely positioned
	   children of the pathways toolbar. */
	position: relative;
	z-index: 3;
	box-sizing: border-box;
	width: 100%;
	/* Size containment in the inline axis, and it is load-bearing.

	   A flex container's max-content width is the sum of its items laid out on
	   one line - here about 730px. The pathways toolbar sits inside an ExtJS
	   `box` whose column is an auto-width `display: table` cell, so ExtJS
	   measured that 730 and wrote `width: 1184px` as an inline style on the tab
	   panel child: 70px wider than its own 1114px container, which pushed the
	   classification card and the Details column past the right edge of every
	   other card on the page and, on a five-omic job, gave the document a
	   horizontal scrollbar. The float-based bar this replaced did not have that
	   contribution, so this arrived with the rewrite.

	   `contain: inline-size` makes the element's intrinsic inline size be
	   computed as if it were empty, so the cell is sized by the rest of the
	   card and the bar takes the width it is given. The used width still comes
	   from `width: 100%` above, so wrapping is unaffected - and because ExtJS
	   writes that 1184 into an inline style, this has to be true at *its*
	   measuring time. Setting it afterwards from the console does nothing,
	   which is what makes it a stylesheet rule rather than a JS fix. */
	contain: inline-size;
}

/* No per-view override any more. MORE's bar carried labelled sliders and was
   given `gap: 14px; padding: 9px 12px` to breathe around them, which made it a
   visibly different band from the pathways bar 900px above it on the same page
   - taller, and with its items spaced differently. The sliders have moved to
   the side rail, next to the filters the pathways network keeps in its own
   Tools column, so what is left in either bar is the same thing: a row of
   controls. One band, one description. */

/* Pushes everything after it to the right edge. Replaces five inline
   `float: right` declarations that were not doing anything. */
.pa-net-toolbar-gap {
	flex: 1 1 auto;
	min-width: 8px;
}

/* Groups controls that act on the same thing — the layout trio, then the two
   downloads. A hairline reads as grouping without adding another surface. */
.pa-net-toolbar-sep {
	flex: 0 0 auto;
	width: 1px;
	height: 18px;
	margin: 0 5px;
	background: var(--pa-border);
}

/* ---- Controls ----------------------------------------------------------- */

/* One shape for every control in either bar: the anchors the pathways view
   uses and the `<button>`s the MORE view uses. Transparent until pointed at,
   because a secondary toolbar's job is to be available without competing with
   a diagram drawn in six saturated category colours. */
.pa-net-tool,
.more-net-toolbar button {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	flex: 0 0 auto;
	margin: 1px;
	padding: 5px 8px;
	font-size: 12px;
	line-height: 1.35;
	font-family: inherit;
	white-space: nowrap;
	color: var(--pa-ink-control, #3F3F46);
	background-color: transparent;
	border: 1px solid transparent;
	border-radius: var(--pa-radius-btn, 8px);
	cursor: pointer;
	text-decoration: none;
	appearance: none;
	-webkit-appearance: none;
	transition: background-color var(--pa-transition, 150ms ease),
	            border-color var(--pa-transition, 150ms ease),
	            color var(--pa-transition, 150ms ease);
}

/* MORE's three buttons used to keep a resting outline, because they sat among
   sliders and without one they read as labels. They sit among the same kind of
   neighbours as the pathways tools now — a search field and each other — and
   each carries an icon, so they are legible as controls at rest and can be as
   quiet as their counterparts two panels up. */

/* The hover fill is a token pair, not a literal, because "a control lights up
   under the cursor" points in opposite directions in the two themes: on the
   ivory band a control goes to white, and on the #212227 band it has to go
   *lighter* than the band, not to the darker card colour. Writing this as
   `--pa-surface` looked right in light mode and produced a hover that was
   darker than its own toolbar in dark mode. */
.pa-net-tool:hover,
.pa-net-tool:focus,
.more-net-toolbar button:hover {
	color: var(--pa-ink, #18181B);
	background-color: var(--pa-control-hover-bg, #FFFFFF);
	border-color: var(--pa-border);
	text-decoration: none;
}

.pa-net-tool:active,
.more-net-toolbar button:active {
	background-color: var(--pa-control-active-bg, #EEEDE7);
}

/* Icons are a size cue, not a second voice: one step down and one step muted,
   so the label leads. */
.pa-net-tool > i,
.pa-net-tool > .fa {
	font-size: 12px;
	color: var(--pa-ink-label, #52525B);
	/* Font Awesome's own line-height is what would otherwise decide how high the
	   glyph sits inside a control whose height comes from its label. */
	line-height: 1;
}

.pa-net-tool:hover > i,
.pa-net-tool:hover > .fa {
	color: inherit;
}

.pa-net-tool:focus-visible,
.more-net-toolbar button:focus-visible,
.more-net-toolbar input:focus-visible,
.more-net-side select:focus-visible,
.more-net-side input:focus-visible {
	outline: 2px solid var(--pa-focus-ring, #0A84FF);
	outline-offset: 1px;
}

/* ---- Text and select fields --------------------------------------------- */

/* One field shape, wherever a field appears in either view: the search box in
   the toolbar, the condition select in MORE's rail. */
.more-net-toolbar select,
.more-net-toolbar input[type="search"],
.more-net-side select,
.more-net-side input[type="search"] {
	padding: 4px 7px;
	font-size: 12px;
	font-family: inherit;
	color: var(--pa-ink-control, #3F3F46);
	background: var(--pa-control-bg, #FFFFFF);
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm, 6px);
	box-sizing: border-box;
}

/* The same 1px the tools carry, so the first control in either bar starts on
   the same rail. */
.more-net-toolbar input[type="search"] {
	min-width: 190px;
	margin: 1px;
}

/* ---- Filter fields in the side rail (MORE) ------------------------------ */

/* A label stacked over its control, which is what a 216px column can hold —
   the pathways network's Tools panel states its filters the same way ("Min
   features in pathway (50%)" over its slider). Beside the control, as this was
   in the toolbar, a label and a 96px range do not fit side by side here. */
.more-net-field {
	display: block;
	margin: 0 0 12px;
	font-weight: 400;
	cursor: pointer;
}

.more-net-field select,
.more-net-field input[type="range"] {
	display: block;
	width: 100%;
	margin: 0;
}

.more-net-field input[type="range"] {
	/* A range has no intrinsic height, and its thumb overhangs its track, so it
	   needs the space stated or the next label sits on the thumb. */
	height: 18px;
}

/* The live readout in each filter's label. Tabular figures so the label does
   not twitch sideways as the digits change while a slider is dragged. */
.more-net-r2-value,
.more-net-coef-value,
.more-net-edges-value {
	display: inline-block;
	min-width: 3.2em;
	font-variant-numeric: tabular-nums;
	color: var(--pa-ink, #18181B);
}

/* ---- Status line -------------------------------------------------------- */

/* "445 nodes · 400 of 1382 edges · condition Ctr_0H" under the MORE toolbar
   told you at a glance what you were looking at. The pathways network had no
   equivalent, which is why "the Reactome network is nearly empty" had to be
   diagnosed from the browser console instead of read off the page. Both bars
   now carry one. */
.pa-net-subtitle,
.more-net-subtitle {
	/* The card rail, so this line starts under the `h2` that names the panel and
	   under the first tool's icon in the bar between them, rather than on a 12px
	   inset of its own. It is prose, so its own edge is where its glyphs start -
	   no subtraction needed here, unlike the toolbar above. */
	padding: 7px var(--pa-card-inset, 26px);
	font-size: 12px;
	line-height: 1.5;
	color: var(--pa-ink-muted, #595959);
	background: var(--pa-surface, #FFFFFF);
	border-bottom: 1px solid var(--pa-border);
	box-sizing: border-box;
}

.pa-net-subtitle b,
.more-net-subtitle b {
	color: var(--pa-ink, #18181B);
	font-weight: 600;
}

/* Said when a filter has emptied the graph rather than when the data is thin.
   Warns without the alarm of an error colour. */
.pa-net-subtitle .pa-net-warn {
	color: var(--pa-accent-orange, #AD5022);
}

/* ---- Canvas ------------------------------------------------------------- */

/* One height for both graphs. It was 775px for the pathways network, written as
   an inline style in PA_Step3Views, and 600px for MORE's, written in
   more-network.css — so two panels one scroll apart, on the same page, showing
   the same kind of object, were 175px different in the one dimension a reader
   compares them by. Declared as a variable rather than repeated, because the
   next person to change it will change it once. */
:root {
	--pa-net-canvas-height: 720px;
}

.pa-net-canvas,
.more-net-canvas,
.omnipath-net-canvas {
	position: relative;
	background: var(--pa-surface-sunken, #F5F4EF);
}

.pa-net-canvas,
.more-net-body {
	height: var(--pa-net-canvas-height);
}

/* ---- Narrow viewports --------------------------------------------------- */

@media (max-width: 900px) {
	.pa-net-toolbar,
	.more-net-toolbar {
		gap: 3px;
		/* Same derivation as the full-width rule, restated only because this
		   block is changing `gap`. Writing the literal back here would undo the
		   rail below 900px, which is where the wrapping makes a ragged left edge
		   easiest to see. */
		padding: 6px calc(var(--pa-card-inset, 26px) - 10px);
	}

	:root {
		--pa-net-canvas-height: 420px;
	}

	/* At this width the two groups no longer fit on one line, and a spacer that
	   still claims the row would push the right group onto a line of its own
	   with a gap of dead air above it. */
	.pa-net-toolbar-gap {
		flex-basis: 100%;
		min-width: 0;
		height: 0;
	}

	.pa-net-toolbar-sep {
		display: none;
	}
}


/* ---- OmniPath pathway network ------------------------------------------- */
/* The third graph surface, and the only one that IS a pathway rather than an
   overview of pathways: OmniPath ships no diagram, so Step 4 renders its
   pathways here instead of painting them onto a raster. It borrows the toolbar
   band above and adds only what it genuinely needs -- a sign legend, since a
   signed edge is the thing this source knows that a diagram does not. */

.omnipath-net-toolbar {
	justify-content: flex-start;
	gap: 18px;
	flex-wrap: wrap;
}

.omnipath-net-legend {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: 12px;
	color: var(--pa-ink-label, #52525B);
	white-space: nowrap;
}

.omnipath-key {
	display: inline-block;
	width: 16px;
	height: 2px;
	border-radius: 1px;
	margin-left: 10px;
	vertical-align: middle;
}
.omnipath-net-legend .omnipath-key:first-child { margin-left: 0; }

.omnipath-net-status {
	font-size: 12px;
	color: var(--pa-ink-label, #52525B);
	margin-left: auto;
	text-align: right;
}

.omnipath-net-canvas {
	width: 100%;
	/* The panel body is sized by its container; the graph fills what is left
	   under the toolbar rather than collapsing to zero height, which is what a
	   Cytoscape container does without an explicit one. The height is then
	   re-measured from the panel in JS once the browser has laid it out.

	   The background comes from the shared .pa-net-canvas rule above, which
	   resolves through --pa-surface-sunken. Declaring it here against a
	   --pa-surface-canvas token that exists nowhere left the graph a bright
	   white rectangle in the dark theme. */
	height: calc(100% - 42px);
	min-height: 420px;
}

.omnipath-net-field {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: 12px;
	color: var(--pa-ink-label, #52525B);
	white-space: nowrap;
}

.omnipath-net-field select {
	font-size: 12px;
	padding: 3px 6px;
	color: var(--pa-ink, #27272A);
	background: var(--pa-surface, #FFFFFF);
	border: 1px solid var(--pa-border, #D4D4D8);
	border-radius: 4px;
}


/* ---- Metabolite hop-ring network ------------------------------------------
   Colours are validated, not chosen by eye: the two poles clear CVD dE 21.6
   (target >=8) and normal-vision dE 32.3 (floor >=15) against the light
   surface. Hop distance is carried by RING POSITION, so it deliberately takes
   no colour; the hue is spent on DE direction, which is what the panel exists
   to show.

   NOTE: this application ships light-only -- there is no prefers-color-scheme
   rule anywhere in resources/css. The validated dark steps are recorded here
   for whenever a theme lands: up #e66767, down #3987e5 on surface #1a1a19
   (CVD dE 19.2, normal-vision 29.0). Do not add a dark block for this one
   component; it would be the only one in the app. */
.pa-hub-net {
	--hub-accent:    #e34948;   /* significance accent, NOT a direction.   */
	--hub-link:      #2a78d6;   /* the clear-filter link, NOT a direction.  */
	--hub-quiet:     #ffffff;   /* hollow: the STROKE carries the contrast.  */
	--hub-quiet-ink: #595959;   /* #f0efec was rejected at 1.12:1 on white.  */
	--hub-absent:    #a1a1aa;
	--hub-ring:      #e7e7e4;
	--hub-ink:       var(--pa-ink, #18181b);
	--hub-muted:     var(--pa-ink-muted, #595959);
	--hub-surface:   var(--pa-surface, #ffffff);
}

/* Truncation and empty-state text above a network canvas. A cap must never
   read as "this is all there is". */
.pa-net-notice {
	min-height: 1.2em;
	margin: 0 0 6px 0;
	font-size: 12px;
	color: var(--hub-muted, #595959);
}
.pa-net-notice:empty { display: none; }

/* A legend is always present: identity must never be colour-alone. */
.pa-hub-legend {
	display: flex;
	flex-wrap: wrap;
	gap: 14px;
	margin: 0 0 8px 0;
	font-size: 12px;
	color: var(--hub-muted, #595959);
}
.pa-hub-legend span { display: inline-flex; align-items: center; gap: 5px; }
.pa-hub-legend .sw {
	width: 11px;
	height: 11px;
	border-radius: 50%;
	border: 1.5px solid var(--hub-quiet-ink, #595959);
	background: var(--hub-quiet, #ffffff);
}
/* Shared with the connection rows and the facet chips: a swatch means the
   same thing wherever it appears, so the colours are not scoped to the
   legend that happened to need them first. */
/* The swatch IS the encoding it explains: four steps of the same diverging
   ramp the heatmaps use, wedged the same way the nodes are. A row in the
   connection list and a node in the graph therefore read as the same claim. */
.pa-hub-legend .sw.de,
.pa-hub-conns .sw.de,
.pa-hub-facet .sw.de {
	background: conic-gradient(#a51f1e 0deg 90deg, #e8e8e8 90deg 180deg,
	                           #0f59a9 180deg 270deg, #8fb4d8 270deg 360deg);
	border-color: var(--hub-ink, #18181b);
}
.pa-hub-legend .sw.absent,
.pa-hub-conns .sw.absent,
.pa-hub-facet .sw.absent { border-style: dashed; border-color: var(--hub-absent); }
.pa-hub-legend .sw.seed   {
	border-radius: 2px;
	transform: rotate(45deg);
	border-width: 2px;
	border-color: var(--hub-ink);
}

/* The hovered node's own numbers. The tooltip is absolutely positioned and
   offset down-right of the cursor, so growing it cannot move the node out from
   under the pointer -- the oscillation that a self-resizing hover readout
   caused on the class map is not reachable here. */
.pa-hub-tip-omic {
	margin: 6px 0 3px 0;
	font-weight: 600;
	color: var(--hub-ink, #18181b);
}
.pa-hub-tip-values { border-collapse: collapse; margin: 0 0 4px 0; }
.pa-hub-tip-values td { padding: 1px 6px 1px 0; white-space: nowrap; }
.pa-hub-tip-values td:last-child {
	text-align: right;
	font-variant-numeric: tabular-nums;
	padding-right: 0;
}
.pa-hub-cell {
	display: inline-block;
	width: 10px;
	height: 10px;
	border: 1px solid rgba(0, 0, 0, 0.18);
}

/* The stage is a column: head, notice, graph, detail card. The graph is the
   only flexible row, so opening the card SHRINKS the canvas instead of
   covering it, and everything a click produces stays inside the viewport.
   Before this, the detail sat below a 720px canvas -- it rendered correctly
   448px below the fold, which is indistinguishable from a dead click. */
.pa-hub-stage {
	position: relative;
	display: flex;
	flex-direction: column;
	min-width: 0;
}
.pa-hub-plot {
	position: relative;
	flex: 1 1 auto;
	min-height: 0;            /* or the flex item refuses to shrink at all */
}

/* The SVG ring guides sit UNDER the Cytoscape canvas and never take pointer
   events, so hover and drag reach the graph unchanged. */
.pa-hub-rings {
	position: absolute;
	top: 0; left: 0; right: 0; bottom: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
	z-index: 0;
}
/* Height comes from the flex row, not from --pa-net-canvas-height: inside the
   stage the canvas is one of four stacked things, not the whole panel. */
.pa-hub-plot .pa-net-canvas {
	position: relative;
	z-index: 1;
	height: 100%;
	background: transparent;
}
.pa-hub-ring {
	fill: none;
	stroke: var(--hub-ring, #e7e7e4);
	stroke-width: 1;
	stroke-dasharray: 3 4;
}
.pa-hub-ring.is-current {
	stroke: var(--hub-muted, #595959);
	stroke-dasharray: none;
}
.pa-hub-ring-label {
	font-size: 10px;
	fill: var(--hub-muted, #595959);
	opacity: 0.75;
}
.pa-hub-ring-label.is-current { opacity: 1; font-weight: 600; }

.pa-hub-tip {
	position: absolute;
	z-index: 2;
	display: none;
	max-width: 260px;
	padding: 6px 8px;
	font-size: 12px;
	line-height: 1.35;
	color: var(--hub-ink, #18181b);
	background: var(--hub-surface, #ffffff);
	border: 1px solid var(--hub-ring, #e7e7e4);
	border-radius: 4px;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
	pointer-events: none;
}


/* ---- Hub panel: list rail, step chips, node detail ------------------------
   The panel replaced a nine-column sortable grid, so the list has to carry the
   two orderings people actually used -- FDR and DE-neighbour count -- or the
   redesign loses information rather than clarifying it. */
.pa-hub-net h2 {
	margin-bottom: 4px;
}
.pa-hub-intro {
	/* No horizontal margin: `div.contentbox p` in main.css already pads every
	   paragraph in this card by --pa-card-inset, so a margin here inset the
	   intro TWICE and put it 26px right of the h2 it sits under. */
	margin: 0 0 10px;
	font-size: 13px;
	color: var(--pa-ink-muted, #595959);
	max-width: 70ch;
}

/* ---- The metabolite rail ------------------------------------------------ */

/* Anchors, not <li>: the alignment overlay measures list items and would judge
   every row against the page rails. */
.pa-hub-listrail {
	width: 252px;
	flex: 0 0 252px;
	display: flex;
	flex-direction: column;
	padding: 0;
	overflow: hidden;         /* the BODY scrolls; the head stays put */
	border-left: 0;
	border-right: 1px solid var(--pa-border, #e0e0e0);
}
.pa-hub-railhead {
	flex: 0 0 auto;
	padding: 10px 12px 8px;
	border-bottom: 1px solid var(--pa-border, #e0e0e0);
}
.pa-hub-railsort {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: 6px;
}
.pa-hub-railcount {
	/* Same double-inset as the intro: this <p> is inside a 12px-padded rail
	   head, and main.css adds another 26px on top of that. */
	margin: 7px 0 0;
	padding: 0;
	font-size: 11px;
	color: var(--pa-ink-muted, #595959);
	font-variant-numeric: tabular-nums;
}
.pa-hub-railcount:empty { display: none; }
.pa-hub-listbody {
	flex: 1 1 auto;
	overflow-y: auto;
	min-height: 0;
}
.pa-hub-search {
	width: 100%;
	box-sizing: border-box;
	padding: 5px 9px;
	font: inherit;
	font-size: 13px;
	color: var(--pa-ink, #18181b);
	background: var(--pa-surface, #fff);
	border: 1px solid var(--pa-border, #d4d4d8);
	border-radius: 4px;
}
.pa-hub-sortlabel {
	flex: 0 0 auto;
	white-space: nowrap;      /* "Rank by" wrapped to two lines in a 252px rail */
	font-size: 12px;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-sort {
	flex: 1 1 auto;
	min-width: 0;
	padding: 4px 6px;
	font: inherit;
	font-size: 12px;
	color: var(--pa-ink, #18181b);
	background: var(--pa-surface, #fff);
	border: 1px solid var(--pa-border, #d4d4d8);
	border-radius: 4px;
}
.pa-hub-item {
	display: block;
	padding: 7px 12px;
	border-left: 3px solid transparent;
	color: var(--pa-ink, #18181b);
	text-decoration: none;
	border-bottom: 1px solid var(--pa-border-subtle, #ececec);
}
.pa-hub-item:hover {
	background: var(--pa-surface, #fff);
	text-decoration: none;
}
.pa-hub-item.is-current {
	background: var(--pa-surface, #fff);
	border-left-color: var(--hub-ink, #18181b);
}
.pa-hub-item.is-significant { border-left-color: var(--hub-accent, #e34948); }
.pa-hub-item.is-marginal    { border-left-color: var(--hub-absent, #a1a1aa); }
.pa-hub-item-name {
	display: block;
	font-size: 13px;
	line-height: 1.3;
	word-break: break-word;
}
.pa-hub-item-meta {
	display: block;
	margin-top: 2px;
	font-size: 11px;
	color: var(--pa-ink-muted, #595959);
	font-variant-numeric: tabular-nums;
}
.pa-hub-list-empty {
	margin: 12px;
	padding: 0;              /* see .pa-hub-intro */
	font-size: 12px;
	color: var(--pa-ink-muted, #595959);
}

/* ---- Stage head: seed name, step chips, legend -------------------------- */

.pa-hub-stagehead {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 6px 12px;
	padding: 8px 10px 6px;
}
.pa-hub-seedname {
	font-size: 14px;
	font-weight: 600;
	color: var(--hub-ink, #18181b);
}
.pa-hub-steplabel {
	font-size: 12px;
	color: var(--pa-ink-muted, #595959);
}

/* A step whose ring is empty is disabled and says "0". Four identical toggle
   buttons in a bottom toolbar could not say which of them had anything to
   show, and most compounds run out well before radius 4 -- so a pressable
   button that changed nothing was the common case, not the edge case. */
.pa-hub-steps {
	display: inline-flex;
	gap: 4px;
}
.pa-hub-step {
	display: inline-flex;
	align-items: baseline;
	gap: 4px;
	padding: 3px 9px;
	font: inherit;
	font-size: 12px;
	line-height: 1.4;
	color: var(--pa-ink, #18181b);
	background: var(--pa-surface, #fff);
	border: 1px solid var(--pa-border, #d4d4d8);
	border-radius: 12px;
	cursor: pointer;
}
.pa-hub-step em {
	font-style: normal;
	font-size: 10px;
	color: var(--pa-ink-muted, #595959);
	font-variant-numeric: tabular-nums;
}
.pa-hub-step:hover:not(:disabled) { border-color: var(--hub-ink, #18181b); }
.pa-hub-step.is-current {
	color: var(--pa-surface, #fff);
	background: var(--hub-ink, #18181b);
	border-color: var(--hub-ink, #18181b);
}
.pa-hub-step.is-current em { color: inherit; opacity: 0.75; }
.pa-hub-step:disabled {
	opacity: 0.4;
	cursor: default;
}

/* The KEGG id, kept beside the name it was replaced by: an id is what a reader
   carries to another tool, and several compounds share a common name. */
.pa-hub-id {
	font-size: 11px;
	font-weight: 400;
	color: var(--pa-ink-muted, #595959);
	font-variant-numeric: tabular-nums;
}

.pa-hub-tip-hint {
	color: var(--pa-ink-muted, #595959);
	font-size: 11px;
}

/* ---- The node detail card ------------------------------------------------
   Docked to the bottom of the stage rather than placed after it: the canvas is
   720px tall, so anything below it opens off-screen. Height 0 when closed so
   the graph gets the whole stage back. */
/* Sized with flex-basis, and with NO height transition. Both are the same
   measured bug: with `transition: height`, this item resolved to 1px and stayed
   there -- the animation and the flex pass kept restarting each other while
   Cytoscape resized against the same box. flex-basis is what the column flex
   algorithm actually reads, and it settled at the right size immediately. */
.pa-hub-detail {
	position: relative;
	flex: 0 0 0;
	overflow: hidden;
	background: var(--pa-surface, #fff);
}
.pa-hub-detail.is-open {
	/* 264px was sized against an omic figure of `rows * 30 + 100`. The figure
	   is 32px taller now -- the condition names were being clipped at the old
	   height -- and the legend gained a scale row, so the first omic no longer
	   fitted under the card's own title and summary and was cut off at the
	   card's edge on open. The stage gives the 36px up from a 720px canvas. */
	flex-basis: 300px;
	border-top: 1px solid var(--pa-border, #e0e0e0);
}
.pa-hub-detail-body {
	height: calc(100% - 30px);
	overflow-y: auto;
	padding: 0 12px 10px;
}
.pa-hub-detail-close {
	position: absolute;
	top: 4px;
	right: 6px;
	z-index: 2;
	width: 22px;
	height: 22px;
	padding: 0;
	font-size: 17px;
	line-height: 1;
	color: var(--pa-ink-muted, #595959);
	background: transparent;
	border: 0;
	cursor: pointer;
}
.pa-hub-detail-close:hover { color: var(--hub-ink, #18181b); }
/* Scoped to the card so it outranks main.css's `div.contentbox h3`, which is
   (0,1,2) against this class's (0,1,0) and was quietly winning: the title sat
   on the card's edge at margin-left 0 while everything under it -- tabs, rows,
   counts -- sits on the 12px rail. Invisible until the tabs gave it something
   to be misaligned against. */
.pa-hub-detail .pa-hub-detail-title {
	margin: 8px 34px 6px 12px;
	font-size: 14px;
}
.pa-hub-detail-where {
	font-size: 12px;
	font-weight: 400;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-detail-sub {
	margin: 10px 0 4px;
	font-size: 12px;
	font-weight: 600;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-detail-summary {
	margin: 0 0 8px;
	padding: 0;              /* see .pa-hub-intro -- main.css pads every <p> */
	font-size: 12px;
	color: var(--pa-ink-muted, #595959);
}

/* The four rows the removed grid held for this compound -- all of them, about
   one compound, instead of 852 rows needing a step filter to be readable. */
.pa-hub-steptable {
	margin: 0 0 10px;
	border-collapse: collapse;
	font-size: 12px;
	font-variant-numeric: tabular-nums;
}
.pa-hub-steptable th,
.pa-hub-steptable td {
	padding: 3px 12px 3px 0;
	text-align: right;
	border-bottom: 1px solid var(--pa-border-subtle, #ececec);
}
.pa-hub-steptable th:first-child,
.pa-hub-steptable td:first-child { text-align: left; }
.pa-hub-steptable th {
	font-weight: 600;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-steptable tr.is-current { background: var(--pa-surface-subtle, #f5f4ef); }
.pa-hub-steptable tr.is-absent td { color: var(--pa-ink-muted, #595959); }
/* A step that adds no new neighbours repeats the row above it. Greyed,
   because identical numbers on four rows read as a broken control. */
.pa-hub-steptable tr.is-saturated td { color: var(--pa-ink-muted, #595959); }
.pa-hub-steptable td.is-significant { font-weight: 600; }

/* One box per omic. globalExpressionData carries omicsValues[0] only, so this
   is fed by /pa_hub_feature rather than by the job payload. */
.pa-hub-omic {
	margin: 0 0 10px;
	padding: 6px 8px;
}
/* The omic's name and the scale it is painted on, on one line.

   These used to be three inline-blocks in a row - legend, heatmap, plot - so
   the ramp sat in the far left of the figure with a wide empty gutter between
   it and the first cell, and the reader had to cross the whole heatmap to get
   from a colour to its meaning. The legend belongs with the label, and the
   figure below it is then two panes and no dead space. */
.pa-hub-omic-head {
	display: flex;
	/* Centred, not bottom-aligned: the legend is three stacked lines (ramp,
	   numbers, reference) and the heading is one, so flex-end hung the heading
	   off the bottom of it. */
	align-items: center;
	/* Packed to the left, NOT space-between. The box spans the heatmap and the
	   line chart, so pushing the legend to the far edge parked the colour ramp
	   above the chart that has no colours in it. It belongs over the heatmap
	   it describes, which is the left 320px. */
	justify-content: flex-start;
	gap: 20px;
	flex-wrap: wrap;
	margin: 0 0 4px;
}
.pa-hub-omic h4 {
	margin: 0;
	font-size: 12px;
	font-weight: 600;
}
.pa-hub-omic-head .paColorLegend { margin: 0; }

.pa-hub-omic-figure {
	display: flex;
	align-items: flex-start;
	gap: 12px;
}
.pa-hub-omic .PA_step5_heatmapContainer { flex: 0 0 320px; width: 320px; }
.pa-hub-omic .PA_step5_plotContainer { flex: 1 1 auto; min-width: 240px; width: auto; }

.pa-hub-edges {
	margin: 0;
	padding-left: 18px;
	font-size: 12px;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-edge-src { color: var(--pa-ink-subtle, #8a8a8a); }
.pa-hub-warn { color: var(--hub-accent, #e34948); }

/* ------------------------------------------------------------------ *
 * The node inspector: tabs, facets, and a card that admits it scrolls *
 * ------------------------------------------------------------------ */

/* Drag the top edge. The card is a fixed 300px and this job's expression
   figures measure 1046px, so its primary content was showing at 26% before a
   connection list was ever added. */
.pa-hub-grip {
	position: absolute;
	top: -9px;
	left: 50%;
	transform: translateX(-50%);
	display: flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 18px;
	cursor: ns-resize;
	z-index: 6;
}
.pa-hub-grip i {
	display: block;
	width: 30px;
	height: 3px;
	border-radius: 2px;
	background: var(--hub-quiet-ink, #595959);
	opacity: 0.35;
}
.pa-hub-grip:hover i { background: var(--hub-ink, #18181b); opacity: 1; }

.pa-hub-tabs {
	display: flex;
	gap: 2px;
	flex: 0 0 auto;
	padding: 4px 12px 0;
	border-bottom: 1px solid var(--pa-border, #e0e0e0);
}
.pa-hub-tab {
	font: inherit;
	font-size: 12.5px;
	padding: 5px 12px 7px;
	position: relative;
	top: 1px;
	color: var(--pa-ink-muted, #595959);
	background: transparent;
	border: 0;
	border-bottom: 2px solid transparent;
	cursor: pointer;
}
.pa-hub-tab:hover { color: var(--hub-ink, #18181b); }
.pa-hub-tab.is-on {
	font-weight: 600;
	color: var(--hub-ink, #18181b);
	border-bottom-color: var(--hub-ink, #18181b);
}
.pa-hub-tab .n {
	margin-left: 5px;
	font-size: 11px;
	color: var(--pa-ink-subtle, #8a8a8a);
	font-variant-numeric: tabular-nums;
}
.pa-hub-tab.is-on .n { color: var(--pa-ink-muted, #595959); }

/* The scrolling pane lives inside the body so the fade can sit over it. */
.pa-hub-detail-body { position: relative; display: flex; }
.pa-hub-pane {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	padding-bottom: 4px;
}
/* The reported bug was a row sliced through the middle at the card's edge.
   The content was always reachable -- the pane scrolls -- but macOS draws
   overlay scrollbars that stay invisible until dragged, so a severed row was
   the ONLY signal and it reads as a broken render. A permanent track plus a
   fade that clears at the end says "more below" without being touched. */
.pa-hub-pane::-webkit-scrollbar { width: 10px; }
.pa-hub-pane::-webkit-scrollbar-track {
	background: var(--pa-surface-sunken, #f5f4ef);
}
.pa-hub-pane::-webkit-scrollbar-thumb {
	background: var(--hub-quiet-ink, #595959);
	border-radius: 6px;
	border: 3px solid var(--pa-surface-sunken, #f5f4ef);
}
.pa-hub-detail-body::after {
	content: "";
	position: absolute;
	left: 0;
	right: 10px;
	bottom: 0;
	height: 26px;
	background: linear-gradient(to top, var(--pa-surface, #fff), transparent);
	pointer-events: none;
	opacity: 1;
	transition: opacity .15s;
}
.pa-hub-detail-body.at-end::after { opacity: 0; }

.pa-hub-countline {
	margin: 8px 0 9px;
	padding: 0;
	font-size: 12.5px;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-countline b {
	font-size: 15px;
	color: var(--hub-ink, #18181b);
	font-variant-numeric: tabular-nums;
}
.pa-hub-countline span { margin-right: 8px; }

.pa-hub-facets {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0 0 11px;
}
.pa-hub-facet {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	font: inherit;
	font-size: 11.5px;
	padding: 3px 9px 3px 7px;
	border-radius: 999px;
	border: 1px solid var(--pa-border, #e0e0e0);
	background: var(--pa-surface, #fff);
	color: var(--hub-ink, #18181b);
	cursor: pointer;
	font-variant-numeric: tabular-nums;
}
.pa-hub-facet:hover { border-color: var(--hub-ink, #18181b); }
.pa-hub-facet:focus-visible {
	outline: 2px solid var(--hub-ink, #18181b);
	outline-offset: 1px;
}
.pa-hub-facet.is-on {
	background: var(--hub-ink, #18181b);
	border-color: var(--hub-ink, #18181b);
	color: var(--pa-surface, #fff);
}
.pa-hub-facet .n { color: var(--pa-ink-subtle, #8a8a8a); }
.pa-hub-facet.is-on .n { color: inherit; opacity: 0.72; }
.pa-hub-facet.is-on .sw.quiet,
.pa-hub-facet.is-on .sw.absent { border-color: currentColor; }

.pa-hub-filtered {
	margin: -4px 0 8px;
	padding: 0;
	font-size: 11.5px;
	color: var(--pa-ink-subtle, #8a8a8a);
}
.pa-hub-clearfacet {
	font: inherit;
	font-size: 11.5px;
	padding: 0;
	border: 0;
	background: none;
	color: var(--hub-link, #2a78d6);
	text-decoration: underline;
	cursor: pointer;
}

.pa-hub-group {
	display: flex;
	align-items: baseline;
	gap: 7px;
	margin: 11px 0 3px;
	padding: 0 0 3px;
	border-bottom: 1px solid var(--pa-border, #e0e0e0);
	font-size: 11.5px;
	font-weight: 600;
	color: var(--pa-ink-muted, #595959);
}
.pa-hub-group .n {
	margin-left: auto;
	color: var(--pa-ink-subtle, #8a8a8a);
	font-variant-numeric: tabular-nums;
}
.pa-hub-conns {
	list-style: none;
	margin: 0;
	padding: 0;
	font-size: 12.5px;
}
.pa-hub-conns li {
	display: flex;
	align-items: baseline;
	gap: 7px;
	padding: 3px 0;
	border-bottom: 1px solid var(--pa-border, #e0e0e0);
}
/* Which way the edge points, relative to the node whose card this is. Two
   reciprocal ECrel edges are two different facts about the network. */
.pa-hub-dir {
	flex: 0 0 auto;
	width: 9px;
	color: var(--pa-ink-subtle, #8a8a8a);
	font-size: 11px;
}
.pa-hub-conns .nm { font-weight: 600; color: var(--hub-ink, #18181b); }
.pa-hub-conns .rel {
	margin-left: auto;
	font-size: 11.5px;
	color: var(--pa-ink-muted, #595959);
	white-space: nowrap;
}
/* The swatch vocabulary is the graph's, so a row and a node read as the same
   claim -- the state colours are shared up where the legend defines them. */
.pa-hub-conns .sw,
.pa-hub-facet .sw {
	flex: 0 0 auto;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	border: 1.4px solid var(--hub-quiet-ink, #595959);
	background: var(--hub-quiet, #ffffff);
	position: relative;
	top: 1px;
}

@media (max-width: 900px) {
	.pa-hub-listrail {
		width: auto;
		flex: 1 1 auto;
		max-height: 220px;
		border-right: 0;
		border-bottom: 1px solid var(--pa-border, #e0e0e0);
	}
	.pa-hub-detail.is-open { flex-basis: 300px; }
}
