/* The family called 'SourceSansPro' is now served by the real Source Sans Pro
   faces, which have been vendored in this directory all along.

   It used to be served by MyriadPro-Light + MyriadPro-Semibold, and that
   mapping was the single reason this UI read as washed out. `MyriadPro-Light`
   declares `usWeightClass: 300`: it is a Light cut, and it was bound to
   `font-weight: normal`. So every paragraph, label, table cell and form field
   in the product - all of it nominally weight 400 - was actually painted in a
   300 face. Not a colour problem. The strokes themselves were thin.

   Light cuts at 12-14px are a readability failure that the industry spent the
   back half of the 2010s walking away from: a 300 weight is a display face,
   sized for headlines, and it loses stem contrast at body sizes on exactly the
   low-DPI external monitors this kind of software is read on all day.

   Source Sans Pro is what the CSS always claimed to be loading, is by the same
   designer as Myriad (Robert Slimbach) and is the same humanist-sans genre, so
   the product's typographic character carries over - but it ships true 400, 600
   and 700 cuts instead of a Light standing in for regular.

   Real italics, too. Nothing declared an italic face before, so every `em` was
   synthetically obliqued by the browser. That matters here specifically:
   italic on the AI report surface denotes gene symbols, which is semantic, not
   decorative, and a slanted Light face is a poor way to carry meaning.

   `swap` on body text: the fallback renders immediately and is replaced when
   the webfont arrives, so text is never invisible. Icon fonts below take
   `block` instead, for the opposite reason. */
/* Light is declared at 300, where it belongs, rather than standing in for
   normal. The three rules that ask for 300 are all large display figures - the
   34px storage total, the 30px stat values, the 34px pathway count - where a
   light weight is a legitimate treatment and has stroke mass to spare. They
   were written when 300 silently resolved to the Light face, so this keeps
   them rendering as their author saw them.

   Body text asks for 400 and cannot reach this: with 300 and 400 both present,
   a 400 request matches 400 exactly. What the old mapping got wrong was
   binding Light to `normal`, not shipping a Light at all. */
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-Light.ttf') format('truetype');
	font-weight: 300;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-Regular.ttf') format('truetype');
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-Semibold.ttf') format('truetype');
	font-weight: 600;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-Bold.ttf') format('truetype');
	font-weight: 700;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-Italic.ttf') format('truetype');
	font-weight: 400;
	font-style: italic;
	font-display: swap;
}
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-SemiboldItalic.ttf') format('truetype');
	font-weight: 600;
	font-style: italic;
	font-display: swap;
}
@font-face {
	font-family: 'SourceSansPro';
	src: url('fonts/SourceSansPro-BoldItalic.ttf') format('truetype');
	font-weight: 700;
	font-style: italic;
	font-display: swap;
}
/* Icon fonts ------------------------------------------------------------------
   `block`, not `swap`. An icon font's codepoints are in a private use area that
   no fallback font has a glyph for, so swapping to the fallback draws a row of
   empty boxes: on a cold load this page showed tofu in place of every icon in
   the header, the toolbars and the upload cards until the font arrived.
   `block` renders nothing at all for that window, which reads as an icon that
   has not painted yet rather than as one that is broken. */
@font-face {
	font-family: 'CustomIcons';
	src: url('fonts/CustomIcons.ttf') format('truetype');
	font-style: normal;
	font-display: block;
}
/* Re-declared here only to add font-display. font-awesome.min.css is vendored,
   so editing it would lose this on the next upgrade; this file is linked after
   it in index.html, so the later face wins. Same three sources, same order. */
@font-face {
	font-family: 'FontAwesome';
	src: url('../font-awesome/fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'),
	     url('../font-awesome/fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'),
	     url('../font-awesome/fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype');
	font-weight: normal;
	font-style: normal;
	font-display: block;
}

/*BODY STYLES*/

root {
	display: block;
}

:root {
  --main-max-width: 1900px;

  /* Typography ---------------------------------------------------------------
     SourceSansPro is vendored locally via the @font-face rules above; the
     fallback chain keeps metrics close if the webfont fails to load. Use this
     token instead of hardcoding a family so the UI stays typographically
     consistent. */
  --pa-font-sans: 'SourceSansPro', 'Source Sans Pro', 'Segoe UI', system-ui, sans-serif;

  /* Measure -------------------------------------------------------------------
     How wide running text is allowed to get, in characters, independently of
     how wide the thing holding it is. The page column is sized for the Step 3
     enrichment grid, which is far past the width a line of prose can be read
     at, so any paragraph sharing that column needs its own cap.

     Two, because the two kinds of prose in the app are read differently: body
     copy inside a card is read in sequence, and an empty-state note is a
     sentence or two glanced at, which wants the shorter line. */
  --pa-measure: 105ch;      /* running text inside a card */
  --pa-measure-note: 78ch;  /* empty-state notes */

  /* Ink colours --------------------------------------------------------------
     Hue and saturation are preserved from the original palette; only lightness
     moves, so the UI keeps its colour identity.

     Measured against #EEEEEE, the darkest light panel fill in this stylesheet,
     rather than against white. That distinction has already caught this
     palette out twice: tuning against white alone left every token at
     4.13-4.48:1 on the grey panels of Step 3, and a retune against #F3F3F3
     still left them at 4.38-4.41:1 on the #EEEEEE surfaces of Step 4. Tuning
     against the darkest surface makes every lighter one pass by construction.

     What that earlier pass got wrong was the target, not the method. It aimed
     at AA (4.5:1) and stopped, which landed the entire palette in a 5.3-5.4:1
     band on white - every token the same distance above the line, and that
     line is the legal floor for "can be perceived", not a setting for text
     somebody reads for an hour. A palette clustered just above a minimum is
     what "washed out" means in practice, and it is what this one looked like.

     The four tokens that carry actual running text - muted body copy, inline
     `b`, `strong`/infoTip accents, links - now target AAA (7:1) on white,
     which puts them at 6.0-6.4:1 on the #EEEEEE panels. That is roughly where
     the mainstream systems put body ink (Tailwind gray-600 7.5:1, GitHub
     Primer fg.default 15:1 with fg.muted 5.7:1 reserved for genuinely
     secondary text), and it is a full step darker than a floor-hugging 5.3.

     `--pa-accent-orange` and `--pa-accent-green` stay where they are: both are
     used on headings and badges at large sizes, where 3:1 is the threshold and
     darkening them further would cost the palette its warmth for no gain. */
  --pa-link: #0060BA;         /* was #006ACC 5.34:1 -> 6.21:1 */
  --pa-link-hover: #24785D;   /* orig #2E9B78 3.46:1 */
  --pa-accent-blue: #19589E;  /* was #1F6BC1 5.34:1 -> 7.17:1 */
  /* The AI colour. Every AI surface in the application is meant to be the same
     blue -- the mark, the two Step 1 headings that carry it, the consent, the
     conversion controls, the widget -- and until now only some of them were:
     inputformat.css read `var(--pa-ai-blue, #4A90D9)` against a token that was
     declared in dark.css and nowhere else, so in the light theme it fell back
     to a colour no other blue in the app uses, and the marks in the section
     headings took the heading's ink and were not blue at all.

     Declared here it resolves to the accent for the light theme and to
     dark.css's own #4A90D9 in the dark one, which is what that file already
     intended. --pa-ai-action-bg is deliberately NOT redeclared by the theme: it
     is a fill that carries white text, and white on dark.css's lighter blue
     measures 3.0:1. */
  --pa-ai-blue: #19589E;
  --pa-ai-action-bg: #19589E;
  --pa-ai-action-bg-hover: #124676;
  --pa-accent-orange: #AD5022;/* orig rgb(209,96,42) 3.59:1 */
  --pa-ink-muted: #595959;    /* was #6B6B6B 5.33:1 -> 7.00:1 */
  --pa-bold-blue: #37587C;    /* was #446E9B 5.32:1 -> 7.37:1 */
  --pa-accent-green: #247B21; /* orig #288824 3.90:1 on #EEEEEE */

  /* Focus ring. Chosen to clear 3:1 (WCAG 2.4.11) against every surface the
     ring actually lands on: white panels 3.65:1, the #F2F2F2 button toolbar
     3.26:1, and the #222 header bar 4.36:1. It is drawn with outline-offset,
     so it sits on the surface behind a control rather than on the control's
     own fill - which is why the green and red button fills do not constrain
     the choice. */
  --pa-focus-ring: #0A84FF;

  /* Surfaces and controls ----------------------------------------------------
     The app had three corner radii in play - 2px on cards, 3px on form fields,
     4px on buttons - which is the kind of drift that reads as unfinished rather
     than as a decision. Two values now: one for surfaces, one for the controls
     that sit on them.

     Deliberately conservative. This is research software whose subject is the
     data on screen, so the chrome earns nothing by competing with it: no
     gradients, no transforms, no lift-on-hover. The shadow is two very low
     alpha layers, enough to separate a card from the #F3F3F3 page and no more,
     and the border is a touch cooler than the old #E9E9E9 so cards read as
     defined rather than washed out against that grey. */
  /* The page behind the cards. #F3F3F3 was a neutral grey, which is the colour
     a surface gets when nobody chose one: it reads as unpainted rather than as
     a background. This is the same warmth an ivory paper stock has - enough to
     make the white cards on it read as white, which a card on grey never quite
     does, and enough that the page looks lit rather than switched off.

     A light counterpart to the --pa-surface-page dark.css already defines, so
     the two themes now name the same thing. */
  --pa-surface-page: #FAF9F5;

  /* The quiet surface - toolbars, hovers, the compact left menu. This token
     had only ever been declared in dark.css, so every light-mode use of it was
     resolving to its `var(--pa-surface-quiet, #F1F2F4)` fallback, and #F1F2F4
     is cold: its blue channel sits three above its red. Declaring the light
     value here is what the dark sheet has always assumed existed.

     #F3F2ED carries the same +5 red-over-blue as --pa-surface-page above, and
     is within 0.1% relative luminance of the #F1F2F4 it replaces (0.8866 vs
     0.8873), so the contrast figures measured against the old value - the
     8.6:1 noted further down this file among them - all still hold. */
  --pa-surface-quiet: #F3F2ED;

  /* The rest of the surface family, and the two control inks that go with it.
     Same story as --pa-surface-quiet above: dark.css has declared all of these
     for a long time, but the light `:root` never did, so every light-mode
     reader fell through to whatever literal its own `var(..., #fallback)`
     happened to carry. The MORE regulator-target network is written entirely
     against these names, which meant the one panel in the application authored
     against the design system was also the only one not painted by it - it
     rendered in #f5f5f7 / #fafafa / #333, cold greys, on a warm ivory page.

     Values chosen from the warmed literals already used throughout this file
     (#F6F6F6 -> #F6F5F0, #F5F5F5 -> #F5F4EF, #FAFAFA -> #F9F8F4) so nothing
     shifts for anything that was already using those by hand; naming them is
     what lets network-views.css describe both graph surfaces at once.

     --pa-ink-body and --pa-ink-title are deliberately NOT declared here. They
     are read in 22 places in this file with per-site fallbacks that differ on
     purpose, and unifying those is a typographic decision of its own, not a
     side effect of wiring up two network panels. */
  --pa-surface: #FFFFFF;
  --pa-surface-toolbar: #F6F5F0;
  --pa-surface-sunken: #F5F4EF;
  --pa-surface-subtle: #F9F8F4;
  --pa-ink: #18181B;
  --pa-ink-control: #3F3F46;
  --pa-ink-label: #52525B;

  /* A control sitting on a toolbar band: its resting fill, the fill it takes
     under the cursor, and the one it takes while pressed. These exist as their
     own tokens rather than as reuses of --pa-surface because "lights up under
     the cursor" is not the same colour move in the two themes. On the ivory
     band a control brightens to white; on dark.css's #212227 band it has to go
     lighter than the band, and --pa-surface there is #1E1F23 - darker. Written
     as --pa-surface, the hover looked right in light mode and inverted in
     dark. */
  --pa-control-bg: #FFFFFF;
  --pa-control-hover-bg: #FFFFFF;
  --pa-control-active-bg: #EEEDE7;

  /* The quiet panel fills scattered through this file were warmed to match:
     #FAFAFA -> #F9F8F4, #F6F6F6 -> #F6F5F0, #F5F5F5 -> #F5F4EF. Warmed at
     constant lightness, deliberately - every contrast figure recorded in the
     comments below was measured against the old values, and a swap that moves
     hue without moving luminance leaves all of them true (each pair differs by
     under 1% relative luminance). Mixing cold greys into a warm page is what
     makes an interface look assembled from parts rather than designed; it is
     also the tell that the greys were never chosen, only defaulted to.

     They are still literals rather than a token because there are twelve of
     them across nine unrelated components, and turning that into a token is a
     refactor with its own risk, not a colour change. */

  --pa-radius: 8px;         /* cards, panels, dialogs */
  --pa-radius-sm: 6px;      /* inputs, small surfaces */
  /* Buttons -- every one of them, filled or outline, labelled or icon-only.
     They were split between two shapes and nobody had decided which: the header
     bar, the "Load example" chips in the How-it-works cards, the job chip and
     the theme toggle were all fully rounded at 999px, while every other button
     in the application -- the ExtJS ones the framework draws, the form's own
     .button, the toolbar options, the converter's controls, the cookie banner --
     took --pa-radius-sm's 6px. Two radii on one screen is the tell that neither
     was chosen; a pill button is also the shape the rest of the interface's
     small round things already use (.po-job-chip, the omic chips), so the app
     ends up with one answer instead of two.

     Not applied to inputs, which keep --pa-radius-sm: a rounder text field
     reads as a search box, and most of these are not. Nor to the segmented
     two-option toggles, which are a group of one shape rather than two
     buttons.

     8px, not the 999px the header was using. Fully round was the first attempt
     at making them one shape and the owner's read was that it is too much: at
     this size a pill is a chip, and these are buttons. 8px is the radius the
     cards and panels already take, so a button now agrees with the surface it
     sits on instead of introducing a third curve. */
  --pa-radius-btn: 8px;     /* buttons */
  /* An alpha of the ink rather than a fixed grey. The same hairline then sits
     correctly on a white card, on the ivory page and on the #EEEEEE panels of
     Step 4, instead of being tuned for one of them and looking cold on the
     other two. Resolves to about #E7E7E8 on white, which is where #E4E4E7
     already was, so nothing shifts - it just stops being wrong elsewhere. */
  --pa-border: rgba(24, 24, 27, 0.10);
  /* How far a card's contents stand off its own border. Body copy used to sit
     about 5px from the edge while the card's heading sat at 20, so text ran
     into the frame around it and the two never lined up. One value for both. */
  --pa-card-inset: 26px;
  --pa-shadow: 0 1px 3px rgba(24, 24, 27, 0.08), 0 1px 2px -1px rgba(24, 24, 27, 0.06);
  --pa-shadow-inset: inset 0 1px 2px rgba(24, 24, 27, 0.14);

  /* Kept short enough to feel like feedback rather than animation. Collapsed
     to 0.01ms by the prefers-reduced-motion block near the end of this file. */
  --pa-transition: 150ms ease;

  /* Page column ---------------------------------------------------------------
     The application was pinned to the left edge. On a 1867px viewport the
     content measured 1300px wide starting at x=20, leaving 547px of dead space
     down the right - not a wide layout, a narrow one shoved into a corner.

     One column, centred, that the header contents and the content boxes both
     align to. 1400px is the width of the Step 3 enrichment grid, the widest
     thing in the application, so the column holds it without reintroducing the
     horizontal scrolling that removing the rail just cured.

     It was 1560px, which is wider than the laptop screens most of this is read
     on: with the old 20px floor the max() below always chose that floor on a
     1440px display and the whole rule did nothing, leaving the results within
     40px of the right edge. The cap engages at 1480px rather than 1600px now,
     and the floor is a margin you can actually see rather than a hairline.

     The gutter is `max()` so nothing changes below the cap - this only does
     work on displays wide enough to have the problem. */
  --pa-page-max: 1400px;

  /* The rail is sized so the results column's visible edge lands on the
     header's "Job view" pill, the same way the TOC's width is the wordmark's:
     the header is the ruler for everything under it. The pill starts 166px
     right of the gutter (147px of wordmark + 19px of menu margin, both
     constants), and the ExtJS step containers add 20px of their own inset
     between the column edge and a card's border, so 146 + 20 puts the card
     exactly under the pill at every viewport width. Was 206, which left the
     cards 60px right of anything else in the header - a rail nothing above
     it shared. */
  --pa-rail: 146px;

  /* The gutter was `max(40px, (100vw - 1400px) / 2)`, which on the 1440-1512px
     laptops this is mostly read on resolved to the 40px floor or a little over
     it - 56px at 1512. That is a hairline by the standards of any page meant to
     be read rather than operated, and it is the specific thing that made the
     application feel like it was pressed against the sides of the window.

     Measured on anthropic.com at a 1512px viewport for comparison: content
     1279px wide inside a 116.5px gutter, or 7.71% of the viewport each side.
     Not a floor it happens to land on - a proportion it holds.

     So the floor becomes proportional too. 7.5vw tracks that reference within
     a few pixels, the 24px minimum keeps a phone from losing the text to its
     own bezel, and the 160px cap stops the proportion running away on a very
     wide display - past which the `(100vw - --pa-page-max) / 2` term takes over
     again and the column caps at 1400px exactly as before.

     Between roughly 1280 and 1600px the proportional term now wins, so the
     column there IS narrower than it was - 1286px rather than 1400px at 1512.
     That is the trade, and it is deliberate: those widths were previously
     spending the difference on a 40-56px margin, which is not a column.

     It costs the enrichment grid nothing, because the results view does not
     read this token. It is `#mainViewCenterPanel.pa-has-toc`, and the media
     block near the end of this file pads it from --pa-gutter-railed instead,
     which is unchanged. Note also that the old rule never actually delivered
     1400px on a 1440px laptop either - max(40, 20) is 40, for a 1360px column -
     so the width the grid was sized against was only ever reached at 1480px and
     up, where the second term still wins today.

       1280 ->  96 (was 40)   1440 -> 108 (was 40)   1512 -> 113 (was 56)
       1680 -> 140 (was 140)  1920 -> 260 (was 260)  2560 -> 580 (unchanged) */
  --pa-gutter: max(clamp(24px, 7.5vw, 160px),
                   calc((100vw - var(--pa-page-max)) / 2));

  /* The contents sidebar is chrome, not content, so the results column is
     centred in what is left after it - not in the viewport. Padding the left by
     rail + gutter while padding the right by gutter alone would push the column
     off-centre by the width of the rail. */
  --pa-gutter-railed: max(40px, calc((100vw - var(--pa-rail) - var(--pa-page-max)) / 2));
}

/* Keyboard focus ------------------------------------------------------------
   The vendored ExtJS Neptune theme ships `.x-webkit :focus { outline: none }`,
   which strips the focus ring from every focusable element in the app -
   buttons, links, grid rows, form fields alike. Keyboard and screen-magnifier
   users get no indication of where they are (WCAG 2.4.7 Focus Visible).

   Restored here rather than in the theme file, because that file is vendored
   and any change to it would be lost on an ExtJS upgrade.

   Three details that matter:
   - `!important` is unavoidable. Neptune's rule is `outline: none !important`,
     so neither a later source position nor higher specificity can beat it.
     Verified in the browser: literal colours, longhand properties and higher
     specificity all still computed to `outline-style: none`; only `!important`
     took effect.
   - `:focus-visible`, not `:focus`. The ring appears for keyboard navigation
     but not when a control is clicked, which is the visual noise that usually
     motivates suppressing focus rings in the first place.
   - The `.x-webkit` prefix on the first selector matches Neptune's own
     specificity; the bare selector below it covers browsers where ExtJS does
     not add that class and no suppression exists.

   `outline` is painted outside the border box and takes no space, so this
   cannot shift any ExtJS layout. */
.x-webkit :focus-visible,
:focus-visible {
	outline: 3px solid var(--pa-focus-ring) !important;
	outline-offset: 2px;
}

body {
	background-color: #000;
	font-family: var(--pa-font-sans) !important;
	/* Left-aligned rather than justified: the app renders text in narrow ExtJS
	   panels and table cells, where justification without hyphenation opens
	   large inter-word "rivers" and hurts readability. */
	text-align: left;
	font-size: 14px !important;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}
span.helpTip {
	color: var(--pa-ink-muted);
}
span.helpTip:hover {
	color: var(--pa-link);
}
span.helpTip:before {
	content: "\f059";
	font: normal normal normal 14px/1 FontAwesome;
	padding-left: 8px;
}
span.infoTip {
	color: var(--pa-accent-blue);
	margin-left: 15px;
	display: block;
	font-size: 12px;
}
span.infoTip:before {
	content: "\f05a";
	font: normal normal normal 12px/1 FontAwesome;
	color: var(--pa-accent-blue);
	margin-left: -15px;
	margin-right: 4px;
}
span.commentTip {
	color: var(--pa-ink-muted);
	display: block;
	font-style: italic;
	font-size: 12px;
	margin-bottom: 4px;
}

.x-body {
	background-color: #000
}
b {color: var(--pa-bold-blue);}

/*TITLES STYLES*/

/* `font-weight: lighter` is a *relative* keyword, not an absolute one: it steps
   down from the inherited weight, so on a 400 body it resolved to 100. Every
   heading in the product was asking for Thin. Measured in Chrome, `h4` on the
   landing page computed to `font-weight: 100`.

   A heading's job is to outrank the paragraph beneath it, and weight is the
   cheapest way to say so. 600 - Semibold, a real cut in the family above - is
   the standard choice for UI headings; it separates from 400 body copy without
   the blockiness 700 brings at these small heading sizes. */
/* The scale below h2 used to be colour-coded rather than sized: h3 teal
   (#007383), h4 green, h5 a third teal (#056E66). Three hues, no relationship
   between them, and each one a step *down* the document outline while reading
   as a step up in emphasis.

   That was invisible at the top level, because the landing page and the results
   cards override h2 and h3 locally (`div.contentbox h2` / `h3` below). It was
   not invisible one level in. Everything that lives inside a panel - the
   network's Tools and Details columns, the pathway view's information strip,
   the cluster panels, the omic upload boxes - titles its sections with bare h4
   and h5, so a settings column read as green heading, teal sub-heading, green
   heading, teal sub-heading down its whole length, with the actual controls in
   near-black underneath. Colour was marking nothing: every one of those
   headings is the same kind of thing.

   One ink, three sizes, one weight. Hierarchy is carried by size and by the
   space around a heading, which is what the level-1 surfaces already do; colour
   goes back to meaning links, significance tints and omic type-coding.

   Sizes come down with the colour. 20px for an h3 and 16px for an h4 were
   display sizes inside 260px-wide panels, where they wrapped a two-word label
   onto two lines; the body copy they title is 13px. */
h1, h2, h3, h4 {font-weight: 600;margin: 0;}
h2 {color: var(--pa-accent-orange);font-size: 23px;}
h3 {font-size: 16px;color: #27272A;	margin: 10px 0px;}
h4 {font-size: 14px;color: #27272A; margin: 10px 0px;}
h5 {font-size: 12.5px; margin: 10px 0px; color: #3F3F46; font-weight: 600;}
a {text-decoration: none;color: var(--pa-link);}
a:hover {color: var(--pa-link-hover);}

/*****************************************************************************
**** STYLES FOR HEADER AND TOOLBARS **********************************************
*****************************************************************************/

.toolbar {
	border-width: 0;
	padding: 10px 20px;
	height: 50px;
	/*z-index: 100;*/
}
/* The session bar's avatar. It was a 30x30 PNG that is in neither the
   repository nor the deployed image; it is a FontAwesome glyph now, which
   needs a size because nothing ever sized the <img> either. Muted rather than
   full ink: it labels the account control beside it and should not outweigh
   it.

   Nothing renders this today - MainView constructs SessionInfoBar and never
   adds it to a container, under a TODO saying as much - so the rule is here
   for whenever that widget is wired up, not because a broken image is on
   screen now. */
.sessionAvatar {
	font-size: 24px;
	line-height: 30px;
	color: var(--pa-ink-muted);
}
/* Chrome ----------------------------------------------------------------------
   Successive passes lightened this bar from #222222 to #33343A and it still read
   as heavy, because the weight was never really the shade - it was that two
   solid dark masses, the header band and the 65px rail, framed the screen on two
   sides and between them held every piece of navigation in the application.

   So the rail is gone: its markup now renders inside this bar (see MainView.js),
   and the bar itself goes white with a hairline under it. Navigation stops being
   a coloured region of the layout and becomes a row of controls, which is the
   pattern the reference site uses and the reason its chrome disappears behind
   its data.

   The bar keeps its 50px height on purpose: .secondTopToolbar below is pinned
   with `top: 50px`, so the height is load-bearing, not cosmetic. */
.mainTopToolbar {
	background: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
	display: flex;
	align-items: center;
	gap: 16px;
	/* The bar stays full-bleed - a centred white strip on a white page would be
	   invisible - but its contents sit on the page column, so the wordmark lines
	   up with the left edge of the content beneath it. */
	padding-left: var(--pa-gutter);
	padding-right: var(--pa-gutter);
}

/* One control system for the header row ---------------------------------------
   Folding the step actions into this row exposed that they had never been
   designed to sit beside each other. Measured, the bar held:

     Sign in / Log in   #5b5a59 charcoal fill, 10px type, 27px tall
     Reset              #D43E3A solid red,     14px type, 32px tall
     Run PaintOmics     solid green,           14px type, 32px tall
     Load example       solid teal,            14px type, 32px tall
     Sharing options    white,                 14px type, 32px tall
     the nav pills      light grey,          12.5px type, 999px radius

   Two heights, two type sizes, four fills and two corner radii, in one row.
   That is what read as "very different visual effect" - the controls were not
   disagreeing about hierarchy, they were disagreeing about what a control is.

   So: one shape for everything in this row - the nav's pill, 30px tall, 12.5px
   type - and hierarchy expressed by weight instead of by everything shouting at
   once. Exactly one control is filled, the primary action for the step; the
   rest are ghosts, and destructive intent is carried by red ink rather than a
   red field. A Reset button does not need to be the loudest thing on screen.

   The border is #878787 on every ghost, not a tinted red for the destructive
   one: a red pale enough to read as a hint (#D9A6A2, #C08A86) measures 2.12 and
   2.91 against white and misses the 3:1 that WCAG 1.4.11 wants of a control's
   boundary, while the neutral makes 3.59. Ink #24252A is 15.29 and the
   destructive #B3261E is 6.54. */
.mainTopToolbar .button,
.secondTopToolbar .button,
.paToolbarMiniature .button {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	height: 30px;
	padding: 0 13px;
	border: 1px solid #878787;
	border-radius: var(--pa-radius-btn);
	background-color: #FFFFFF;
	color: #24252A;
	font-size: 12.5px;
	line-height: 1;
	white-space: nowrap;
}
.mainTopToolbar .button:hover,
.secondTopToolbar .button:hover {
	background-color: var(--pa-surface-quiet);
	color: #111111;
	border-color: #767676;
}
/* The step's primary action - Run PaintOmics, Next step - is the one thing that
   stays filled, so the eye still lands on it immediately. */
.secondTopToolbar .button.btn-success {
	background-color: #3A843A;   /* white label 4.62:1 */
	border-color: transparent;
	color: #FFFFFF;
}
.secondTopToolbar .button.btn-success:hover {
	background-color: #31702F;
	border-color: transparent;
	color: #FFFFFF;
}
.mainTopToolbar .button.btn-danger,
.secondTopToolbar .button.btn-danger,
.paToolbarMiniature .button.btn-danger {
	background-color: #FFFFFF;
	color: #B3261E;
	border-color: #878787;
}
.mainTopToolbar .button.btn-danger:hover,
.secondTopToolbar .button.btn-danger:hover {
	background-color: #FCF2F1;
	color: #8F1E17;
	border-color: #767676;
}
/* .btn-sm set 10px, which is where the account button's odd size came from. */
.mainTopToolbar .button.btn-sm {
	font-size: 12.5px;
}

/* The Sign in / Log out button sat on a dark bar and needed a #858585 border to
   reach the 3:1 that WCAG 1.4.11 asks of the information identifying a control.
   On white its own #5b5a59 fill is 6.88:1 against the bar, so the fill now
   carries the boundary by itself and the border goes back to transparent -
   the earlier fix is not reverted so much as made unnecessary by the surface
   underneath it changing.

   The `margin-left: auto` that pins the corner to the right belongs to neither
   this button nor the switch any more: it is on .headerUtilities below, the
   wrapper that holds both. Giving it to the switch alone - the arrangement
   before this - meant the switch absorbed all the free space in the row and the
   account button merely followed it, which is exactly how a bare moon ended up
   floating on its own between the step actions and a labelled pill. */
.mainTopToolbar .button {
	/* The account control is the design system's HeaderButton tone="account" - a
	   filled charcoal pill, not one of the step ghosts - so its fill has to be
	   restated here. The base `.button` above does carry #5b5a59, but at (0,1,0)
	   it loses to the shared `.mainTopToolbar .button, .secondTopToolbar .button`
	   rule that paints every header control white, and with the transparent
	   border below that left this one at 1.00:1 against the white bar: WCAG
	   1.4.11 had nothing to measure. The shape set by that rule - 30px pill,
	   12.5px type, 999px radius - is unchanged; only the fill and its label ink
	   return. */
	background-color: #5B5A59;   /* --pa-btn-neutral-bg, 6.88:1 on the white bar */
	color: #E2E2E2;              /* --pa-btn-neutral-ink, 5.31:1 on the fill */
	border-color: transparent;
	float: none;
	margin-left: 0;
	margin-right: 0;
}
/* (0,3,0), which is what it takes to beat the ghost hover declared above. */
.mainTopToolbar .button:hover {
	background-color: #117EA9;   /* --pa-btn-neutral-bg-hover, white label 4.58:1 */
	color: #FFFFFF;
	border-color: transparent;
}
/* One bar, not two -----------------------------------------------------------
   The step actions - Load example / Run PaintOmics / Reset on Step 1, Go back /
   Next step on Step 2, the Step 4 view controls - lived in their own 50px band
   pinned directly under the header. Two stacked bars for what is one row of
   chrome, and because this band is `position: fixed` it was not occupying
   layout space but *covering* the first 50px of the content underneath.

   It now sits in the header row itself, right-aligned and clear of the account
   button. Nothing about the markup changes - the band is still the same fixed
   element, it just moves up and shrinks to its contents - so every step keeps
   building its own buttons exactly as before. The content below gains back the
   50px it was losing to the overlay.

   `row-reverse` preserves the original visual order: these buttons were laid out
   with `float: right`, so the first in the DOM sat rightmost, and a plain flex
   row would have reversed them. */
/* `!important` throughout, and it is load-bearing rather than lazy. On Step 1
   this band is a plain box, but on Step 4 it is an ExtJS container whose layout
   the framework manages, and the framework writes `left: 0; width: 1440px; top:
   0` as *inline* styles. A stylesheet declaration cannot beat an inline one
   without !important, so without these the Step 4 toolbar stayed full-width,
   wrapped its five buttons onto two rows and sat on top of the pathway. The
   inner span is ExtJS's box wrapper and carries the same inline width, which is
   why it needs the override too.

   `position` was the one declaration in this block that did not carry it, and
   that is the whole reason Step 4's toolbar did not line up with the other
   three steps. On Step 4 the band is a child of a vbox, so ExtJS gives it
   `.x-box-item`, and Neptune declares

       .x-box-item { position: absolute !important; }

   which beats a plain `position: fixed` no matter how specific this selector
   is. So the band computed to `absolute`, and every other declaration here
   then resolved against the wrong containing block: `top: 0` meant the top of
   #pathwaysPanelsWrapper rather than of the viewport, which is 50px down, and
   the inline `left: 0` -- never overridden, because a fixed element does not
   need it -- stretched the box from the panel's left edge to `right`. Measured:
   position absolute, top 50, left 109, the buttons packed to the right of a
   973px box and reading as a centred second row under the header, where every
   other step has them in the header itself.

   `left: auto` is the other half. Without it the inline `left: 0px` survives
   the switch to fixed and pins the band to the viewport's left edge, so it is
   the pair that has to be stated, not just the position. */
.secondTopToolbar {
	position: fixed !important;
	left: auto !important;
	top: 0 !important;
	/* --pa-header-utilities is the distance from the right edge of the viewport
	   to the left edge of .headerUtilities, written by fitHeaderNav() on every
	   resize and every view change. It replaces a literal `var(--pa-gutter) +
	   178px`, which was 132 of account button plus 30 of switch plus a 16 gap,
	   counted by hand and true only while all three kept those exact sizes. It
	   was already wrong in a state the app reaches by itself: signing out removes
	   the account button, and the band went on reserving room for it.

	   The fallback is the old number, so a browser that somehow renders this
	   before the first measurement lands where it used to rather than on top of
	   the corner. The +16 matches the divider's own 16px margin on the far side,
	   which is what centres that hairline between the two groups. */
	right: calc(var(--pa-header-utilities, calc(var(--pa-gutter) + 178px)) + 16px) !important;
	width: auto !important;
	height: 50px;
	padding: 0;
	display: flex;
	flex-direction: row-reverse;
	align-items: center;
	gap: 8px;
	background: transparent;
	border-bottom: 0;
	box-shadow: none;
	z-index: 201;
}
/* The buttons were floated; inside a flex row the float is ignored but the
   margins are not, and they would fight the gap above. */
.secondTopToolbar > span,
.secondTopToolbar > span > div {
	width: auto !important;
	display: flex !important;
	align-items: center;
	gap: 8px;
}
.secondTopToolbar .button {
	float: none;
	margin: 0;
}

/* The corner that is not about the analysis --------------------------------
   The theme switch and the account button do the same kind of job - neither
   belongs to the step on screen - and they now say so by sitting together in
   one group at the end of the row, behind a divider.

   That divider is the point of the wrapper. The header carries three different
   kinds of control: navigation, the actions for this step, and these two. With
   nothing marking the boundary, an icon-only switch in a run of labelled pills
   reads as something left behind rather than as a member of a group, which is
   the complaint this answers. A hairline costs one pixel and does the whole
   job. */
.mainTopToolbar .headerUtilities {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	gap: 8px;
	margin-left: auto;
}
/* Hidden with opacity rather than display so its 9px stays in the group's
   measured width. fitHeaderNav() reserves space for that width, and a divider
   that came and went would move the step actions sideways every time a view
   mounted its toolbar.

   Shown by :has(), not by a class from JavaScript. The first version asked
   fitHeaderNav() to write `.has-step-actions`, which meant a decorative hairline
   depended on that function's scheduling - and that scheduling is known to go
   stale (see the note on watchHeaderFit). Measured on the results page it did:
   the divider was present on one load and absent on the next, from nothing but
   which run happened to be last. The stylesheet can answer "is a step toolbar on
   screen" by itself, and a selector cannot be out of date. */
.mainTopToolbar .headerUtilities::before {
	content: '';
	flex: 0 0 auto;
	width: 1px;
	height: 20px;
	/* 16, matching the gap .secondTopToolbar leaves on the other side, so the
	   hairline sits centred between the two zones. Measured at 8 it was closer to
	   the switch than to Reset, which made it read as an ornament belonging to the
	   switch instead of as the boundary between two groups - the opposite of the
	   job. The 8px inside the group stays 8, so the switch and the account button
	   are still visibly tighter to each other than either is to this line. */
	margin-right: 16px;
	/* --pa-border is a 0.10 alpha, correct for a panel edge sitting against a
	   filled surface but too faint to read as a boundary in open space. This one
	   has 16px of white either side of it and one pixel to make its case. */
	background: rgba(24, 24, 27, 0.18);
	opacity: 0;
	transition: opacity var(--pa-transition);
}
body:has(.secondTopToolbar) .mainTopToolbar .headerUtilities::before {
	opacity: 1;
}
#header {
	float: none;
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	height: 32px;
	/* No `margin-left: -5px`. It pulled the mark five pixels outside the
	   toolbar's own gutter, so the wordmark did not start on the page rail -
	   and once the cards below were brought onto that rail, five pixels of
	   overhang is exactly the kind of miss that reads as sloppy rather than as
	   optical correction. The mark is a square SVG with no side bearing to
	   compensate for. */
	cursor: pointer;
}
#header img {
	height: 28px;
	float: none;
}
/* Dark ink now that the bar is white. The wordmark also stops being the largest
   thing in the interface - at 26px it outsized every heading in the analysis
   below it, which is the same complaint as the omic cards. */
#header h1 {
	font-size: 19px;
	margin: 0 0 0 9px;
	font-weight: 400;
	color: #1A1A1A;
	white-space: nowrap;
}
/* No rule for .appVersion any more: MainView.js stopped emitting the chip. The
   header is the row where the nav pills and the step actions grow towards each
   other, and fitHeaderNav() compacts the pills and then strips their labels to
   keep them apart; a build number between the wordmark and the first pill spent
   about 50px of exactly that contested space. Kept as a note rather than as a
   dead rule, so the next person to look for the version in the header finds out
   where it went instead of finding styling for an element that is not there. */
#footer {
	max-width: 65px;
}

/***********************************************************
***STYLES FOR LATERAL MENU** *******************************
***********************************************************/

/* Header navigation -----------------------------------------------------------
   This was a 65px dark rail down the left edge holding six stacked items. It now
   lives in the header as a horizontal pill group, which is what finally takes
   the weight out: the navigation stops occupying a region of the layout and the
   analysis gets the full width of the window.

   The markup is unchanged - same <ul class="lateralMenu-body">, same
   .menuOption items, same .submenu children, so the jQuery in MainView.js that
   binds clicks and hover still finds everything. Only the mount point and these
   rules changed. */
/* The nav sat centred while the header held only a wordmark and an account
   button. Now that the step actions share this row it moves left, next to the
   wordmark, so the whole right half is free for them - Step 4 alone contributes
   five buttons. */
.mainNav {
	flex: 0 1 auto;
	display: flex;
	justify-content: flex-start;
	min-width: 0;
}
ul.lateralMenu-body {
	display: flex;
	align-items: center;
	gap: 2px;
	margin: 0;
	padding: 3px;
	list-style: none;
	background: var(--pa-surface-quiet);
	/* The track is a surface holding controls, so it takes the surface radius
	   the cards and panels take. It was fully round, which was the last piece of
	   the header still saying "pill" after every button in the application came
	   down to 8px. */
	border-radius: var(--pa-radius);
	max-width: 100%;
}
ul.submenu {
	margin: 0;
	padding: 0;
}
/* Top-level items. `position: relative` is what the dropdown below anchors to. */
.lateralMenu-body > .menuOption {
	position: relative;
	display: flex;
	align-items: center;
	gap: 5px;
	/* Tightened from 6px 13px when the step actions joined this row - the nav had
	   to give back roughly 90px so five Step 4 buttons and the account control
	   could share the header without colliding. */
	padding: 6px 10px;
	border: 0;
	/* One step inside the track's 8px, because the track insets these by 3px and
	   a nested corner has to be the outer radius minus the padding or it reads
	   as loose inside it. Only ever visible on hover and on the selected item,
	   which are the two states that paint a background. */
	border-radius: var(--pa-radius-sm);
	font-size: 12.5px;
	line-height: 1;
	color: #3A3B42;          /* 8.6:1 on #F1F2F4 */
	white-space: nowrap;
	cursor: pointer;
	transition: background-color var(--pa-transition), color var(--pa-transition);
}
.lateralMenu-body > .menuOption:hover,
.lateralMenu-body > .menuOption.selected {
	background: #FFFFFF;
	color: #111111;
}
/* Making room for the step actions ------------------------------------------
   The step actions are `position: fixed` (see .secondTopToolbar), so they take
   part in no layout and the nav has no way to stop before them - it ran
   underneath, which is what put the AI Interpret button on top of "Contact".

   MainView.js measures the two and adds these in order, only as far as it has
   to. `is-compact` buys about 50px by tightening the pills; `is-iconly` gives
   up the words and keeps the icons, which is worth roughly 400px. Both keep
   every item present, hoverable and clickable, and the title attribute set in
   navHTML names the item while its label is hidden. That is the reason the nav
   compacts instead of clipping: `overflow: hidden` here would also cut off the
   dropdowns, which are absolutely positioned children of these items. */
.lateralMenu-body.is-compact > .menuOption {
	padding: 6px 7px;
	gap: 4px;
}
.lateralMenu-body.is-iconly > .menuOption {
	padding: 6px 9px;
	gap: 0;
}
.lateralMenu-body.is-iconly > .menuOption > .menuLabel {
	display: none;
}
/* The icon sat above the label in the rail and has to sit beside it here. */
.lateralMenu-body > .menuOption > i.fa {
	display: inline-block;
	font-size: 13px;
	margin: 0;
	transition: color var(--pa-transition);
}
/* The per-section accents survive the move: they were on the rail's left border,
   then on the icon, and stay on the icon here. */
.lateralMenu-body > .menuOption:hover > i.fa,
.lateralMenu-body > .menuOption.selected > i.fa { color: #B02A5B; }
.lateralMenu-body > .menuOption:nth-child(2):hover > i.fa,
.lateralMenu-body > .menuOption:nth-child(2).selected > i.fa { color: #8A7A18; }
.lateralMenu-body > .menuOption:nth-child(3):hover > i.fa,
.lateralMenu-body > .menuOption:nth-child(3).selected > i.fa { color: #2F6FA8; }
.lateralMenu-body > .menuOption:nth-child(4):hover > i.fa,
.lateralMenu-body > .menuOption:nth-child(4).selected > i.fa { color: #2E7D3A; }

/* Dropdowns. The rail flew these out sideways at `left: 65px` with a negative
   top margin; from a header they drop underneath the item that owns them.
   jQuery toggles display via fadeIn/fadeOut, so `display: none` stays the
   resting state and nothing about the behaviour changes. */
.lateralMenu-body ul.submenu {
	display: none;
	position: absolute;
	top: 100%;
	left: 0;
	margin-top: 8px;
	width: max-content;
	min-width: 240px;
	max-width: 380px;
	background: #FFFFFF;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
	box-shadow: 0 12px 28px -8px rgba(24, 24, 27, 0.22), 0 6px 12px -6px rgba(24, 24, 27, 0.14);
	padding: 6px;
	z-index: 300;
	font-size: 13px;
	text-align: left;
	white-space: normal;
}
/* The 8px offset above is a dead zone, and it made every dropdown option
   unreachable. jQuery binds mouseenter/mouseleave on the top-level item and
   hides the menu with fadeOut(0); an absolutely positioned child keeps the
   parent "hovered" while the pointer is over it, but the 8px between the pill's
   bottom edge and the card's top edge belongs to neither, so moving the mouse
   down to the menu fired mouseleave and the menu vanished before it could be
   clicked. Teleporting a cursor straight onto the card hides the bug, which is
   why it survived the first round of checks - it only appears when the pointer
   actually travels through the gap.

   This bridges it: a transparent strip, part of the submenu and therefore part
   of the item's hover subtree, covering exactly the offset. The gap stays
   visible, it just stops being a hole. */
.lateralMenu-body ul.submenu::before {
	content: '';
	position: absolute;
	left: 0;
	right: 0;
	top: -8px;
	height: 8px;
}

/* No right-anchoring rule any more. `:nth-last-child(-n+2)` used to hang the
   last two dropdowns from the right of their item, because with six pills those
   two sat far enough along the bar to run off the window. With four they do not:
   the whole nav now ends around 450px from the left, so every dropdown fits
   hanging left - and the rule had become actively wrong, since right-anchoring a
   380px card to a pill at x=340 would have started it 40px off the left edge of
   the screen. */
ul.submenu li {
	list-style-type: none;
	padding: 0;
}
ul.submenu li.menuOption {
	display: block;
	padding: 8px 10px;
	border: 0 !important;
	border-radius: 6px;
	background-color: transparent;
	color: #3A3B42;
	text-align: left;
	line-height: 1.45;
	transition: background-color var(--pa-transition);
}
ul.submenu li.menuOption:hover {
	background-color: var(--pa-surface-quiet);
}
ul.submenu a {
	color: var(--pa-link);
}
ul.submenu a:hover {
	color: var(--pa-link-hover);
	text-decoration: underline;
}
/* No rule for `li.menuOption > div` any more. It existed to overrule
   `style="color: white"` written inline on the citation headings, back when they
   flew out of a dark rail; the citations are structured markup now and no header
   element carries a colour in its style attribute, so there is nothing left to
   beat with !important. */
ul.submenu i.fa {
	display: inline-block;
	margin-right: 8px;
	font-size: 13px;
	width: 14px;
	text-align: center;
	color: var(--pa-ink-muted);
}

/* The More panel --------------------------------------------------------------
   Resources, the citations and Contact behind one pill. Two columns because the
   two lists have genuinely different shapes - five short links against four
   references - and stacking them made a dropdown taller than most laptop
   viewports.

   The grid lives on .navPanel-inner, an <li>, and not on the <ul> that is the
   card. jQuery's fadeIn() writes `display: block` as an inline style on the
   element it reveals, and an inline declaration beats any `display: grid` a
   stylesheet puts on the same element. Putting the grid one level in keeps the
   framework's show/hide and the layout out of each other's way. */
.lateralMenu-body ul.submenu.navPanel {
	min-width: 0;
	max-width: min(600px, calc(100vw - 48px));
	padding: 14px 16px 12px;
}
.navPanel-inner {
	display: grid;
	grid-template-columns: minmax(180px, auto) minmax(230px, auto);
	column-gap: 28px;
	row-gap: 10px;
	align-items: start;
}
/* Contact is neither a resource nor a citation, so it spans the foot with a rule
   above it rather than being filed under a heading it does not belong to. */
.navPanel-foot {
	grid-column: 1 / -1;
	border-top: 1px solid var(--pa-border);
	padding-top: 8px;
}
.navPanel-list {
	margin: 0;
	padding: 0;
	list-style: none;
}
/* 11px and tracked out: these name the columns, they are not read as content,
   and at body size two of them would compete with the links underneath.

   No left margin: the 10px it carried parked each title between its list's
   icon edge and its text start - on neither. The list items hang their icons
   at the column's own edge, and that edge is the one rail the column has. */
.navPanel-title {
	margin: 0 0 6px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--pa-ink-muted);
}
/* The citation row: paper on the left, BibTeX on the right, both clickable. */
.navPanel-cite {
	display: flex !important;   /* beats `ul.submenu li.menuOption { display: block }` */
	align-items: baseline;
	gap: 10px;
}
.navPanel-cite-ref {
	flex: 1 1 auto;
	min-width: 0;
	display: block;
}
.navPanel-cite-name {
	display: block;
	color: var(--pa-ink-strong, #1A1A1A);
	font-weight: 600;
}
.navPanel-cite:hover .navPanel-cite-name {
	color: var(--pa-link-hover);
}
.navPanel-cite-meta {
	display: block;
	margin-top: 1px;
	font-size: 11.5px;
	color: var(--pa-ink-muted);
}
/* A chip, so the second link in the row is obviously a separate target from the
   first rather than a word that happens to be underlined next to it. */
.navPanel-cite-bib {
	flex: 0 0 auto;
	align-self: center;
	padding: 3px 8px;
	border: 1px solid var(--pa-border);
	border-radius: 999px;
	font-size: 11px;
	line-height: 1.4;
	white-space: nowrap;
}
.navPanel-cite-bib:hover {
	background: var(--pa-surface-quiet);
	text-decoration: none !important;
}
/* Two columns need about 600px of dropdown. Below that the panel would be wider
   than the room to the right of the More pill, so it becomes one column and
   grows downwards instead. */
@media (max-width: 940px) {
	.navPanel-inner {
		grid-template-columns: minmax(0, 1fr);
	}
}
ul.menu li.loggedOption, ul.menu span.loggedOption {
	display: none
}

/*****************************************************************************
**** BUTTONS AND LINKS STYLES ************************************************
*****************************************************************************/

.button {
	background-color: #5b5a59;
	cursor: pointer;
	color: #E2E2E2;
	font-family: var(--pa-font-sans);
	font-size: 14px;
	padding: 6px 10px;
	text-decoration: none;
	float: right;
	margin: 0px 5px;
	white-space: nowrap;
	vertical-align: middle;
	border: 1px solid transparent;
	border-radius: var(--pa-radius-btn);
	/* Named properties rather than `all`: these buttons sit inside ExtJS panels
	   whose width and height are animated by the layout engine, and
	   transitioning those would make every relayout visibly lag. */
	transition: background-color var(--pa-transition), border-color var(--pa-transition), color var(--pa-transition), box-shadow var(--pa-transition);
}

/* Pressed state. The app had none - a button looked identical mid-click and
   after, so a slow request read as a click that had not registered. Rendered as
   an inset shadow rather than a transform: nudging a control down is a gesture
   this kind of software does not need, and it moves the click target under the
   cursor. */
.button:active {
	box-shadow: var(--pa-shadow-inset);
}

/* Disabled. Also absent before, so a disabled action was indistinguishable
   from a live one until clicking it did nothing. `filter: grayscale` drains the
   meaning out of the coloured variants - green for go, red for destructive -
   without needing a separate disabled fill for each of the nine button classes,
   and the reduced opacity keeps it clearly recessive. Pointer events stay on so
   the cursor still reports the state. */
.button.disabled,
.button[disabled],
.button[aria-disabled="true"] {
	opacity: 0.55;
	filter: grayscale(0.65);
	cursor: not-allowed;
	box-shadow: none;
}
/* The variant hover fills below are declared at (0,2,0); this selector is
   (0,3,0), so the greyscale survives the hover rather than the hover repainting
   a disabled control in full colour. The fill underneath still swaps, but
   drained to grey it reads as inert, which is the point. */
.button.disabled:hover,
.button[disabled]:hover,
.button[aria-disabled="true"]:hover {
	opacity: 0.55;
	filter: grayscale(0.65);
	box-shadow: none;
}

.button:hover {
	/* Was #50c0ed, which put white on pale cyan at 2.08:1. The base .button is
	   dark grey with a light label, so hovering used to *lose* contrast - the
	   label got harder to read at exactly the moment the control was being
	   pointed at. Same hue, 4.58:1. */
	background: #117EA9;
	color: #ffffff;
}
.button.btn-sm {
	font-size: 10px;
}
.button.btn-no-float {
	float:none; /*TODO: REPLACE BY btn-float*/
}
.btn-right {
	float:right; /*TODO: REPLACE BY btn-float*/
}
.btn-left {
	float:left; /*TODO: REPLACE BY btn-float*/
}
.btn-inline {
	float: none;
}

/* The action button of a dialog form - Sign in, Sign me up!, Reset password.
   Every one of these used to carry its own inline geometry: `width: 195px;
   margin: 10px 54px` on the sign-in link, `10px 35px` on the reset link,
   `width: 100%` on the sign-up one. Those numbers were arithmetic against a
   fixed dialog width - 195 + 54 + 54 = 303, tuned to the ~310px column of a
   700px window - so they centred nothing and broke the moment a dialog was
   resized or a label translated.

   The design system's own login pattern is auto-width, centred, one filled
   primary; `inline-flex` plus a centred parent gives that at any width. The
   6px gap is --pa-btn-gap, which is what puts the icon next to the label
   without the `&nbsp;`-and-hope this file used before.

   `float: none` is the load-bearing declaration. `.button` floats right, and a
   float ignores `margin: auto`, so centring is impossible until the float goes.
   Dropping it also means the button now contributes its own height to the box
   - which the fixed dialog heights in UserController.js have to absorb. */
.button.btn-form-action {
	float: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--pa-btn-gap, 6px);
	width: auto;
	margin: 10px 0;
}
.formActionRow {
	text-align: center;
	margin: 0;
}

/* The validation line above those buttons. It was written as
   `<div style="color: #D22; font-size: 16px;" id="..." style="display:none">`
   - two `style` attributes on one element, of which the HTML parser keeps the
   first and drops the second, so `display:none` never applied. The div is
   empty until jQuery fills it, so nothing showed and the bug stayed invisible.
   Hiding it here, in one place, is what the second attribute was reaching for.

   #D22 on white is 4.51:1; it is stated as a token-adjacent literal rather
   than inline so that dark.css can reach it, which an inline style cannot. */
.formMessage {
	display: none;
	color: #D22;
	font-size: 16px;
}

/* The rule between the sign-in form and the no-account column. It was an
   inline `border-left: 1px solid #E7E7E7` repeated on three divs, which put it
   out of reach of dark.css - so a near-white hairline stayed on the dark
   panel. Stated once here, it takes the border token and can be themed. */
.signInColumnDivider {
	padding: 0 30px;
	border-left: 1px solid var(--pa-border);
	/* The rule is drawn by this div's own left border, so it was only ever as
	   tall as the words inside it - it stopped level with the last line of the
	   right-hand column and left the two halves joined by a stub. The parent
	   box already stretches; this is what passes that height down to the
	   element actually carrying the border. */
	height: 100%;
	box-sizing: border-box;
}
.signInColumnDivider .formActionRow {
	margin-top: 28px;
}

/* The account dialog ----------------------------------------------------------
   Sign in, Create account, Reset password all render into one ExtJS window, and
   it was carrying the framework's defaults rather than the design system's
   surface. Measured on screen: an empty 60px header band (removed in
   UserController - the window has no title), the form column sitting on
   ExtJS's #f2f2f2 panel fill against a white right column, so a dialog with
   nothing asymmetric in it looked like two pasted-together halves, and field
   labels at the framework's bold near-black rather than the 13px
   --pa-ink-body every other form in the product uses.

   The design system's Dialog is one surface: a hairline, the shared 6px
   corner, real elevation, and the title inside the body rather than in chrome.
   These rules bring the account window to that, without touching the ExtJS
   window elsewhere. */
.userViewsDialog,
.userViewsDialog .x-window-body {
	background-color: var(--pa-surface, #FFFFFF);
	border-radius: var(--pa-radius);
}
/* ExtJS makes the window element itself focusable and focuses it on open, so
   the global focus ring drew a 3px blue rectangle around the whole dialog. A
   focus indicator belongs on the thing you are about to type into, and this
   div is not it - SignInPanel now focuses the email field instead, which is
   where the ring shows up. Suppressed on the container only; every control
   inside keeps its own :focus-visible outline.

   `!important` because the global :focus-visible rule carries it too - without
   it this loses the cascade and the ring comes straight back, which is what
   happened on the first attempt. */
.userViewsDialog:focus,
.userViewsDialog:focus-visible {
	outline: none !important;
}
.userViewsDialog .x-window-body {
	border: 0;
}
/* The form column's fill. `.x-panel-body-default` is where the grey came from;
   it is stated at (0,2,0) so this needs the dialog class to win. */
.userViewsDialog .x-panel-body,
.userViewsDialog .x-panel-body-default,
.userViewsDialog .x-form,
.userViewsDialog .x-container {
	background-color: transparent;
}
/* The heading of an account form. `h2` globally is 23px `font-weight: lighter`
   in --pa-accent-orange, which resolved to weight 100 terracotta: the title of
   the dialog was the palest text in it. A form's title is the one thing that
   should be unambiguous, so it takes real weight and title ink. */
.userViewsDialog h2 {
	margin: 0;
	font-size: 22px;
	font-weight: 600;
	line-height: 1.2;
	letter-spacing: -0.015em;
	color: var(--pa-ink-title, #18181B);
}

/* The lockup above it: product mark, title, and a line saying what the form is
   for. Four different forms open into this one window and none of them used to
   say which product the credentials belonged to. */
.po-auth-head {
	margin-bottom: 22px;
}
.po-auth-brand {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-bottom: 18px;
}
.po-auth-brand img {
	height: 22px;
	width: auto;
	display: block;
}
.po-auth-brand span {
	font-size: 13px;
	font-weight: 600;
	letter-spacing: -0.005em;
	color: var(--pa-ink-title, #27272A);
}
.po-auth-sub {
	margin: 7px 0 0;
	font-size: 13px;
	line-height: 1.5;
	color: var(--pa-ink-muted);
}

/* The confirmation states - "Check your inbox", "Password changed". */
.po-auth-notice {
	margin-bottom: 14px;
}
/* The sentence under the heading. Replaces a `style='font-size: 20px'` written
   inline by showCongratzPanel, which set the confirmation copy larger than the
   heading above it and put it out of reach of dark.css. */
.po-auth-notice-body {
	margin: 0;
	font-size: 14px;
	line-height: 1.55;
	color: var(--pa-ink-body, #555);
}
.po-auth-notice-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	margin-bottom: 14px;
	border-radius: 50%;
	font-size: 17px;
	color: #1F6B4A;
	background: rgba(36, 123, 33, 0.10);
}

/* The gutter between the sign-in form and the "No account" column beside it.
   Only there: the other three forms in this window are a single column, and a
   padding on `.po-auth-form` alone put 26px of dead space down the right of
   Create an account, Reset password and Change password as well.

   Descendant, not `>`. An ExtJS hbox wraps its items in `.x-box-inner` and
   `.x-box-target`, so the form is a grandchild of the container carrying
   `.po-auth-cols` and the child combinator matched nothing - measured 0px of
   padding and a 0px gap, with the sign-in inputs ending flush against the
   second column's border. */
.po-auth-cols .po-auth-form {
	padding-right: 26px;
}

/* Field rhythm. ExtJS stacks these with no gap at all, so five inputs read as
   one striped block rather than five questions. */
.userViewsDialog .x-form-item {
	margin-bottom: 14px;
}
/* The framework renders labels bold and nearly black, which made "Email
   Address:" heavier than the heading above it. Medium weight and a step down
   in size: a label names its field, it does not compete with the title. */
.userViewsDialog .x-form-item-label,
.userViewsDialog .x-form-item-label-inner {
	font-size: 12.5px;
	font-weight: 600;
	color: #3F3F46;
	padding-bottom: 6px;
}

/* The inputs. Neptune draws a 22px box with a 1px #B5B8C8 border and square
   corners - the single most dated element in the dialog. These take the
   design system's own control geometry: the shared small radius, the border
   token every card uses, room to actually type in, and a real focus state
   rather than the framework's 1px colour shift.

   `height` is set rather than padding because ExtJS writes an explicit
   `height` onto the input element inline when it lays the field out; matching
   it here keeps the box the size the layout thinks it is. */
.userViewsDialog .x-form-text {
	font-size: 13.5px;
	height: 38px;
	padding: 0 12px;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	background-image: none;
	background-color: var(--pa-surface, #FFFFFF);
	color: var(--pa-ink-title, #27272A);
	box-shadow: none;
	transition: border-color var(--pa-transition), box-shadow var(--pa-transition);
}
.userViewsDialog .x-form-text:hover {
	border-color: #C4C6CC;
}
/* `!important` on the outline: the global :focus-visible rule near the top of
   this file restores a 3px ring on every focusable element, which on a field
   that is already drawing its own ring doubles it. The ring below is the same
   colour and the same job, drawn inside the control's own corner radius. */
.userViewsDialog .x-form-text:focus {
	outline: none !important;
	border-color: var(--pa-focus-ring);
	box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.20);
}
.userViewsDialog .x-form-text::placeholder {
	color: #A1A1AA;
}
/* Neptune marks an invalid field with a red wavy background image tiled along
   the bottom edge, which at this border radius is clipped into a smear. A
   border and a ring say the same thing and survive the corner. */
.userViewsDialog .x-form-invalid-field,
.userViewsDialog .x-form-invalid-field-default {
	background-image: none;
	border-color: #D22;
	box-shadow: 0 0 0 3px rgba(221, 34, 34, 0.14);
}

/* Show/hide password. See poAttachPasswordReveal in UserViews.js - the button
   is appended to the field's own cell, which this makes a positioning
   context. */
.userViewsDialog .po-pass-wrap {
	position: relative;
}
.userViewsDialog .po-pass-wrap .x-form-text {
	padding-right: 40px;
}
.po-pass-toggle {
	position: absolute;
	/* Anchored to the bottom of the cell, not centred in it. With
	   `labelAlign: top` the cell this sits in is 65px tall because it also
	   spans the label row above, so centring on it put the button 27px high -
	   floating between the label and the field rather than in the field.
	   The field is 38px and the button 28, so 5px off the bottom edge centres
	   it on the input itself. */
	bottom: 5px;
	right: 5px;
	width: 28px;
	height: 28px;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: 0;
	border-radius: var(--pa-radius-sm);
	background: transparent;
	color: var(--pa-ink-muted);
	font-size: 13px;
	cursor: pointer;
	transition: color var(--pa-transition), background-color var(--pa-transition);
}
.po-pass-toggle:hover {
	color: #3F3F46;
	background: var(--pa-surface-quiet, #F1F2F4);
}

/* The primary action fills its column. It was `min-width: 190px` centred,
   which on the 300px form column left an asymmetric 55px of air either side of
   the one thing the reader came here to press. A full-width action is also
   what makes the two columns of the sign-in dialog read as equal offers. */
.userViewsDialog .button.btn-form-action {
	min-width: 0;
	width: 100%;
	height: 40px;
	padding: 0 16px;
	font-size: 13.5px;
	font-weight: 600;
	border-radius: var(--pa-radius-btn);
	margin: 0;
}
.userViewsDialog .formActionRow {
	margin-top: 20px;
}
/* Quiet the secondary links under the primary: they were the same weight and
   size as the action itself. */
.userViewsDialog .po-auth-links {
	text-align: center;
	margin: 14px 0 0;
}
.userViewsDialog .po-auth-alt {
	text-align: center;
	margin: 8px 0 0;
	font-size: 13px;
	color: var(--pa-ink-muted);
}
.userViewsDialog .po-auth-links a,
.userViewsDialog .po-auth-alt a,
.userViewsDialog #forgotPassLink,
.userViewsDialog .signUpLink {
	font-size: 13px;
}

/* The second column of the sign-in dialog. It was a bare `border-left`
   hairline, which left the two halves reading as one form that had been split
   rather than as two ways in. A quiet panel says "this is a different offer"
   without any more ink than the rule used, and centring it vertically keeps
   its heading level with the form's now that the form has a lockup above
   its own.

   Scoped to .po-auth-aside so the same .signInColumnDivider markup in the
   guest and no-login panels keeps the plain rule it was written for. */
.po-auth-aside .signInColumnDivider {
	display: flex;
	flex-direction: column;
	justify-content: center;
	border-left: 0;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
	background: var(--pa-surface-quiet, #F7F8FA);
	padding: 24px;
}
.po-auth-aside .signInColumnDivider h2 {
	font-size: 16px;
	margin-bottom: 8px;
}
.po-auth-aside .signInColumnDivider p {
	font-size: 13px;
	line-height: 1.55;
	color: var(--pa-ink-body, #555);
	margin: 0;
}
.po-auth-aside .signInColumnDivider .formActionRow {
	margin-top: 20px;
}

/* Create an account ----------------------------------------------------------
   The affiliation hint, and the data-protection notice above the consent tick.
   That notice was a bare 80px scrolling iframe with no label and no frame, so
   it read as a rendering fault rather than as a document. */
.po-auth-hint {
	margin: -8px 0 14px;
	font-size: 12px;
	line-height: 1.45;
	color: var(--pa-ink-muted);
}
.po-auth-terms {
	margin-bottom: 14px;
}
.po-auth-terms-label {
	display: block;
	margin-bottom: 6px;
	font-size: 12.5px;
	font-weight: 600;
	color: #3F3F46;
}
.po-auth-terms #dataProtection {
	display: block;
	height: 112px;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	background: var(--pa-surface-quiet, #F7F8FA);
}
/* The consent row. ExtJS drops the label under the box; the same flex fix the
   AI consent checkbox below uses, so the tick and the sentence it applies to
   read as one thing. */
.po-auth-consent .x-form-cb-wrap {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}
.po-auth-consent .x-form-cb-wrap > .x-form-cb-label {
	width: auto !important;
	flex: 1 1 auto;
	margin: 0;
	font-size: 13px;
	line-height: 1.45;
	color: var(--pa-ink-body, #555);
}
.po-auth-consent .x-form-cb-wrap > .x-form-checkbox {
	flex: 0 0 auto;
	margin-top: 1px;
}

/* The AI consent checkbox ------------------------------------------------------
   The box sat on its own line with its label dropped underneath it, so the
   control and the sentence it applies to read as two separate things. Measured:
   ExtJS anchors the field to 100% and writes an explicit `width: 721px` onto
   the label, inside a 725px cell. Add the 15px box and its margins and the pair
   needs 740px, so the label could never share the line and wrapped below.

   The cell becomes a flex row and the label gives up its computed width, which
   also buys the wrapped second line a hanging indent - continuation text lines
   up with the first line rather than sliding back under the checkbox. Scoped to
   the AI section so no other checkbox in the form is touched. */
.po-ai-section-body .x-form-cb-wrap {
	display: flex;
	align-items: flex-start;
	gap: 8px;
}
.po-ai-section-body .x-form-cb-wrap > .x-form-cb-label {
	/* ExtJS writes the width inline, so this needs the weight to beat it. */
	width: auto !important;
	flex: 1 1 auto;
	margin: 0;
	line-height: 1.45;
}
.po-ai-section-body .x-form-cb-wrap > .x-form-checkbox {
	flex: 0 0 auto;
	margin-top: 1px;
}

/* The consent box's label keeps the application's checkbox-label type --
   13px regular, the same as "KEGG (required)" on the Databases row above --
   so the one control on this panel reads as a control, not a heading. A
   title-sized label (15px, then 14px) was tried and read as too big. */

/* The upload form's three section headings. div.contentbox h3 is 13.5px for
   every card in the application; on this form the panels under the headings
   carry 14px titles and 14px copy, so at 13.5px the headings were the smallest
   type in their own sections. 16px is the design system's h3; the extra top
   margin is the air each section needs to read as a block. Scoped to the
   form so no other card moves. */
.paStep1Form div.contentbox h3,
div.contentbox .paStep1Form h3 {
	font-size: 16px;
	font-weight: 600;
	color: var(--pa-ink-title, #1A1A18);
	margin: 20px 0 6px;
}
/* The explanation, under the switch: a step larger than the 13px the AI
   widget uses for chat, because this is a panel on a form, not a bubble. */
.po-ai-section-body p.ai-intro-copy {
	font-size: 14px;
	line-height: 1.55;
	margin: 10px 0 0;
	max-width: var(--pa-measure-note, 78ch);
}
/* Short items listed where a sentence would enumerate them -- the formats the
   converter handles under Section 3, the three facts about the interpretation
   beside its consent box. One rule for both so they are one shape.

   They were pills: each item in a rounded outlined box. Twelve of those on one
   screen -- three in the interpretation panel, nine under the upload one -- and
   the owner's reading was that they looked like tags on something rather than
   like a list of what a thing does. Boxing a two-word noun does not make it
   easier to read; it makes nine nouns into nine objects. Set as one line of
   text with a separator between items, they are what they always were: a list,
   quiet, under the sentence that introduces them. */
.po-spec-list {
	list-style: none;
	margin: 0;
	padding: 0;
	/* Flex, not inline text. As inline <li>s the markup gave the line no break
	   opportunity at all -- there is no whitespace between </li><li> and the
	   separator is generated content -- so the list ran straight off the panel:
	   "Reads your uploaded val" and "MetaboLights" were both cut by the card
	   edge. As flex items they wrap between items and never inside one. */
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	font-size: 12.5px;
	line-height: 1.8;
	color: var(--pa-ink-muted);
}
div.contentbox .po-spec-list li,
.po-spec-list li {
	margin: 0;
	padding: 0;
	max-width: none;
	color: inherit;
	font-size: inherit;
	line-height: inherit;
	/* An item never breaks inside itself: "DIA-NN" was wrapping at its own
	   hyphen and "CSV / TSV" at its slash, so nine names read as eleven. */
	white-space: nowrap;
}
.po-spec-list li:not(:last-child)::after {
	/* A middot, not a comma: several of these names contain a space
	   ("MetaboLights MAF", "Excel workbooks") or a slash ("CSV / TSV"), which a
	   comma list would not separate clearly. On the item rather than before the
	   next one, so a wrapped line ends with the separator instead of starting
	   with it. */
	content: "\00B7";
	margin: 0 8px;
	color: var(--pa-ink-muted);
	opacity: 0.55;
}

/* The decision column's stack: one line, how it is made, the privacy link. */
.po-ai-section-body p.po-ai-oneliner { margin: 12px 0 0; }

/* The privacy notice's link, in the notes that carry one. Quiet: the link is
   the label of a heading, not a control. */
.ai-gdpr-info-link {
	color: var(--pa-ink-muted);
	font-size: 12.5px;
	white-space: nowrap;
	text-decoration: none;
}
.ai-gdpr-info-link:hover {
	color: var(--pa-accent-blue);
	text-decoration: underline;
}


/* The experiment-design field on step 1 ---------------------------------------
   Free text describing an experiment, so it is sized to be written in rather
   than filled in: the label sits on top (see the note in PA_Step1Views.js) and
   the box keeps a comfortable measure and stays resizable vertically. */
.po-exp-design textarea {
	font-family: var(--pa-font-sans);
	font-size: 13px;
	line-height: 1.5;
	padding: 8px 10px;
	resize: vertical;
}
/* The field's caption. It used to be its own full-width box above the button;
   it is now a span inside .po-exp-design-draft, sharing that row with the
   button, so it takes no margin of its own and flexes into whatever the button
   leaves. The basis is a measure, not a width: below it the row wraps and the
   caption takes its own line, which is the right behaviour in the narrow
   column rather than a hyphenated two-word ribbon beside the button. */
.po-exp-design-hint {
	font-size: 12px;
	line-height: 1.45;
	color: var(--pa-ink-muted);
	flex: 1 1 220px;
	min-width: 0;
}

/* "Draft this for me" and the line under it that reports what was sent.
   The row wraps because the note grows to two lines once it carries the
   column count and the Undo link, and a nowrap row pushed Undo off the
   card's right edge at the narrow end of the two-column split. */
.po-exp-design-draft {
	margin-top: 10px;
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 8px;
}
.po-exp-design-draft-btn {
	/* The AI mark is an inline SVG, so it needs a baseline the button's own
	   line-height does not give it. */
	display: inline-flex;
	align-items: center;
	gap: 6px;
	flex: none;
}
/* "Draft this for me" asks the model to write the experiment design, which
   makes it the only AI action on this form -- and it was .btn-secondary's
   green, the colour this application uses for "go" (Run PaintOmics, Next step).
   Two meanings on one colour, and the AI action was the one wearing the wrong
   one. White on this fill is 7.17:1. */
.po-exp-design-draft-btn.button.btn-secondary {
	background-color: var(--pa-ai-action-bg);
	border-color: var(--pa-ai-action-bg);
	color: #FFFFFF;
}
.po-exp-design-draft-btn.button.btn-secondary:hover {
	background-color: var(--pa-ai-action-bg-hover);
	border-color: var(--pa-ai-action-bg-hover);
}
/* Disabled until consent is ticked, which is most of the time this form is on
   screen -- so it has to read as "not yet", not as "broken". Kept at the
   secondary button's shape with the ink dropped to the muted token, and the
   cursor says the control knows it was clicked. `title` carries the reason. */
.po-exp-design-draft-btn:disabled,
.po-exp-design-draft-btn[disabled] {
	opacity: 0.5;
	cursor: not-allowed;
}
.po-exp-design-draft-note {
	font-size: 12px;
	line-height: 1.45;
	color: var(--pa-ink-muted);
	flex: 1 1 100%;
}
.po-exp-design-draft-note:empty {
	display: none;
}
.po-exp-design-draft-note.is-error {
	color: var(--pa-ai-consent-warn, #C44500);
}
.po-exp-design-undo {
	font-weight: 600;
	white-space: nowrap;
}
/* The outlined secondary button - "Sharing options", "Show mapping info", the
   dialog's Close. Its label was never the problem (#333 on white is 12.63:1);
   its *boundary* was. A white fill on the #F2F2F2 toolbar is nearly the same
   colour as the toolbar, so the 1px border is the only thing saying "this is a
   control", and at #ccc it measured 1.43:1 against that toolbar. WCAG 1.4.11
   asks for 3:1 on exactly this - the visual information required to identify a
   component.

   Both values are picked against the *pair* of surfaces each state touches, not
   just one, which is what the old #adadad hover got wrong: it was chosen to sit
   on white and then used on an #e6e6e6 fill.

     base   #ccc     1.61 on the white fill / 1.43 on the toolbar
            #878787  3.59 / 3.21
     hover  #adadad  1.94 on the #e6e6e6 fill / 1.74 on the toolbar
            #767676  3.64 / 4.06

   Kept to the minimum that clears the bar with a little headroom. A darker
   outline would pass more comfortably and would also make every secondary
   button shout, which is not what this application needs. */
.btn-default {
	color: #333;
	background-color: #fff;
	border-color: #878787;
}
.btn-default:hover {
	color: #333;
	background-color: #e6e6e6;
	border-color: #767676;
}
.btn-primary {
	color: #fff;
	background-color: #337ab7;
	border-color: #2e6da4;
}
.btn-primary:hover {
	color: #fff;
	background-color: #286090;
	border-color: #204d74;
}
/* The button palette below is Bootstrap 3's, whose white-on-colour variants all
   fall short of WCAG AA. Fills are darkened along the same hue so each button
   keeps its meaning (green = go, red = destructive), except .btn-info and
   .btn-warning: light cyan and amber cannot carry white text without turning
   into unrecognisable teal and brown, so those keep their fill and take dark
   ink instead - the approach Bootstrap itself adopted in v4. */
.btn-secondary {
	color: #fff;
	background-color: #2A8368; /* was #55c9a6 - 2.04:1 */
	border-color: #226B55;
}
.btn-secondary:hover {
	color: #fff;
	background-color: #327358; /* was #59b790 - 2.44:1 */
	border-color: #295E48;
}
.btn-small {
	font-size: 12px;
	padding: 4px 6px;
}
/* The type-coding argument for the pale cyan was true when this class dressed
   the message dialog's dismiss button, where the fill said "this dialog is an
   info". It does not any more: Util.js gives that button `btn-default` in every
   case now, because a button that closes a dialog does the same thing whatever
   the dialog says, and the type is already carried by the accent along the
   dialog's top edge and by its heading.

   What is left wearing this class is three real actions - Search in this
   pathway, Search for features, Show Features - and Step 3's AI Interpret. Not
   one of them is an "info". They are the primary control of the panel they sit
   in, and they were a #5bc0de fill with #333 ink and a #1F7F9B border: a pale
   Bootstrap-3 cyan carrying dark text inside a dark teal outline, i.e. three
   colours on one small button and none of them the application's.

   One accent, filled, white ink, no third colour. --pa-accent-blue is 7.17:1
   against white, so the label clears AAA, and the fill is 3:1 against the
   panels these sit on without needing a separate border to prove it is a
   control. */
.btn-info{
	color: #FFFFFF;
	background-color: var(--pa-accent-blue);
	border-color: var(--pa-accent-blue);
}
.btn-info:hover {
	color: #FFFFFF;
	background-color: #124477;
	border-color: #124477;
}
.btn-success {
	color: #fff;
	background-color: #3A843A; /* was #5cb85c - 2.48:1 */
	border-color: #306C30;
}
.btn-success:hover {
	color: #fff;
	background-color: #337533; /* was #449d44 - 3.41:1 */
	border-color: #2A602A;
}
.btn-warning {
	color: #333; /* dark ink on the amber fill - 6.49:1 */
	background-color: #f0ad4e;
	border-color: #eea236;
}
.btn-warning:hover {
	color: #333; /* 5.42:1 */
	background-color: #ec971f;
	border-color: #d58512;
}
.btn-danger {
	color: #fff;
	background-color: #D43E3A; /* was #d9534f - 3.96:1 */
	border-color: #B62B28;
}
.btn-danger:hover {
	color: #fff;
	background-color: #C32F2B;
	border-color: #A02723;
}
.button.acceptButton {
	background: #0F8827; /* was #12a22f - 3.37:1 */
	color: #ffffff;
}
.acceptButton:hover {
	/* Was #23D046, which brightened on hover; no green light enough to read as
	   "brighter" can carry white text at AA, so this darkens instead, matching
	   the hover direction of every other button in this file. */
	background: #0C7020;
	color: #ffffff;
}
.cancelButton:hover {
	background: #D14242; /* was #E08181 - 2.75:1 */
	color: #ffffff;
}
.warningButton:hover {
	background: #9A6C33; /* was #C28B46 - 2.97:1 */
	color: #ffffff;
}
ul.ui-menu.ui-widget.ui-widget-content {
	position: absolute !important;
}
.x-tip-body-default a {
	color: white
}
.extButton {
	margin: 2px 5px 2px 0px;
	border: 1px solid rgb(187, 187, 187);
	display: inline-block;
	padding: 6px 10px;
}
.dropdown .dropdown-menu {
	visibility: hidden;
	margin: 0;
	list-style: none;
	width: 100%;
	padding: 0;
}
.dropdown:hover .dropdown-menu {
	visibility: visible;
}
.dropdown-menu a {
	display: block;
	padding: 0.2em 0.8em;
	text-decoration: none;
	background: #CCCCCC;
	color: #333333;
}

/** List Item Hover Styles **/

.dropdown-menu a:hover {
	background: #BBBBBB;
}
.container {
	margin: auto;
	border: solid 1px var(--pa-border);
	border-radius: var(--pa-radius);
	padding: 5px 20px;
	background-color: #ffffff;
}
.container .expanded {
	width: 95%;
}
.x-panel-body-default {
	font-size: 14px;
	background: rgb(243, 243, 243);
}
.contentbox {
	border: 1px solid var(--pa-border);
	background: #fff;
	border-radius: var(--pa-radius);
	overflow: hidden;
	/* `5px 10px` before. The horizontal 10 was a second gutter stacked on the
	   one --pa-gutter already applies to #mainViewCenterPanel, so a card's edge
	   never coincided with anything else measured from the page column - most
	   visibly the header wordmark, which sat 20px to the left of the card
	   directly below it. Vertical margin stays; that one separates cards from
	   each other, which is what a margin here is for. */
	margin: 5px 0;
	box-shadow: var(--pa-shadow);
}
/* See the comment on the form in PA_Step1Views: the inset is applied through the
   panel's own margin config, which ExtJS measures, so the body must not add it
   again. Scoped to this one panel - every other .contentbox is an element that
   ExtJS does not size itself, where the margin is correct. */
.paStep1Form > .x-panel-body.contentbox,
.paToolForm > .x-panel-body.contentbox {
	margin-left: 0;
	margin-right: 0;
}
/* The one panel in each supporting-tool form. It is laid out by the form's
   vbox, which reads this margin off the computed style and takes it out of the
   stretched width, so the panel starts and ends on the card inset like the
   "Data uploading" heading above it. */
.paToolForm .omicbox {
	margin-left: var(--pa-card-inset);
	margin-right: var(--pa-card-inset);
}
/* Card headings ---------------------------------------------------------------
   Every block on the results page - Pathways selection, Pathways summary,
   Multiple databases used, the classification and network panels, both
   metabolite analyses, Pathway enrichment - was titled in 23px terracotta at
   `font-weight: lighter`, which resolves to 100, on a grey band. Thin weight at
   display size is the most dated thing left on the page: it reads washed out
   rather than quiet, and at seven repetitions down a 2900px page the colour was
   doing the shouting instead of the structure.

   A card heading is a label, not a display line. So: near-black at a real
   weight, sized to sit above its content rather than compete with it, and the
   grey band replaced by a hairline - the card already has an edge, it does not
   need a second fill inside it.

   The accent is not lost, only moved to where it carries meaning: links, the
   contents marker, the significance tints. Put the colour back here by setting
   `color: var(--pa-accent-orange)` if it is wanted. */
div.contentbox h2, #clusternumber_box h2, #threshold_box h2 {
	background-color: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
	border-radius: var(--pa-radius) var(--pa-radius) 0 0;
	/* A warm near-black rather than #27272A. On an ivory page a cold grey-black
	   heading reads as a different material from the paper under it. */
	color: #1A1A18;
	/* 19, not 15. Body copy in these cards is 13px, so a 15px heading was a
	   1.15 step - close enough to the text it titles that the page had no
	   typographic hierarchy at all, which is most of why it read as flat. Every
	   card looked like an undifferentiated block of text with a slightly bolder
	   first line. 19px is a 1.46 step: unmistakably a heading, still nowhere
	   near shouting, and it moves nothing horizontally. */
	font-size: 19px;
	font-weight: 600;
	letter-spacing: -0.012em;
	padding: 15px var(--pa-card-inset);
	margin: 0;
}
/* The inset for body copy lives on `div.contentbox p, div.contentbox li` further
   down this file, which is the rule that already owned that padding. */
/* A step down again: these sit inside a card that already has an h2.

   The horizontal padding is not decoration: at `6px 0` a sub-heading sat hard
   against the card's own border while the h2 above it and every paragraph below
   it were inset by --pa-card-inset. On the data-upload card that put three
   headings ("1. Organism selection", "AI-Powered Pathway Interpretation",
   "2. Choose the files to upload") on a left edge no other element shared, so
   the card read as having no margin at all on the side the eye scans first.
   Every heading in a card now starts where its body copy starts. Blocks that
   supply their own inset (.po-step-card, .repDetectionCard) opt out below. */
div.contentbox h3 {
	background-color: transparent;
	border-color: var(--pa-border);
	color: #3F3F46;
	font-size: 13.5px;
	font-weight: 600;
	padding: 6px var(--pa-card-inset);
	margin: 12px 0 4px;
}
/* The AI mark that leads "2. AI-powered pathway interpretation" in the upload
   form hangs in the card's inset instead of sitting in the text flow.

   The three headings of that form are a numbered sequence - 1. Organism
   selection, 2. AI-powered..., 3. Choose the files to upload - and only the
   middle one carries a mark. In flow it pushed that heading's number 17px right
   of the other two: an ordered list whose numbers do not line up, which is the
   one thing a numbered list has to get right.

   Pulled by its own width plus the space that follows it, so the digit lands on
   the same rail as its siblings and the mark sits in the 26px inset the card
   already holds open. Measured after the change: 1., 2. and 3. all start at
   x=150. Still inline, so it keeps the vertical alignment its own
   `vertical-align:-0.15em` gives it - absolute positioning would have needed
   that centring rebuilt by hand. */
/* The mark is the AI colour wherever it is drawn as a symbol beside words: the
   two Step 1 section headings, the consent label, the converter's offer under
   each omic. It is an inline SVG painting with currentColor, so left alone it
   took whatever ink surrounded it -- which made the marks in the headings the
   same near-black as the heading, and the one thing on the form that is
   supposed to say "this is the AI part" said nothing.

   The exception is a mark inside a filled control or on the widget's own
   coloured header, where the surrounding ink is already chosen for that surface
   and the mark has to follow it. */
svg.po-ai-mark {
	color: var(--pa-ai-blue);
}
.button svg.po-ai-mark,
.x-btn svg.po-ai-mark,
button svg.po-ai-mark,
.ai-widget-header-title svg.po-ai-mark,
.ai-widget-fab svg.po-ai-mark {
	color: inherit;
}

div.contentbox h3 > svg.po-ai-mark:first-child {
	margin-left: calc(-1em - 0.2em);
}
div.contentbox h3 span {
	font-size: small;
	padding-left: 10px;
}
div.contentbox p, div.contentbox li {
	/* 5px put body copy hard against the card's border while the card's own
	   heading sat at 20 - the text was framed by an edge it was touching, and
	   the two never shared a left margin. Both now use --pa-card-inset. */
	padding: 2px var(--pa-card-inset);
	/* See the note on `body`: justified text in these narrow columns produces
	   distracting inter-word gaps, so keep a ragged right edge. */
	text-align: left;
}
/* Lists carry their own marker indent on top of the inset. */
div.contentbox li {
	padding-left: 4px;
}
/* Blocks that already own their spacing, and would otherwise be inset twice.

   Every selector here is written `div.<class> p`, not `.<class> p`. That is not
   style: `div.contentbox p` above scores (0,1,2) and a bare `.<class> p` scores
   (0,1,1), so the exceptions lost to the rule they exist to override wherever
   the block sits inside a card - which is exactly where they are needed. The
   AI callout on the data-upload page is the case that showed it: its prose was
   inset 26px while the consent checkbox directly beneath it was not, so the two
   lines of the same block started on different left edges. `div.` ties the
   specificity and these rules, being later in the file, win. */
div.po-ai-section-body p,
div.paEmptyNote p,
div.step4HistoryBox p,
div.x-tip p,
div.sigma-tooltip p,
/* The MORE network's rail is a 216px column inside a card, so a 26px inset on
   each side would leave its note 164px wide - two words a line. */
div.more-net-side p,
/* This card insets its own inner div, so its paragraphs would otherwise sit at
   52px while the heading above them sat at 26. */
#dbs_message p {
	padding-left: 0;
	padding-right: 0;
}

/* A flat segmented control: a sunken track holding two borderless options,
   the active one raised on its own chip. Replaces the old beveled grey/blue
   pill (still visible in git blame) with the same token set every other
   control in the app already draws from, so it themes for free instead of
   needing its own dark.css entry. Shared by this tooltip's Genes/Metagenes
   and Heatmap/Line-chart pickers, and by the Step 3 pathway-info rail panel's
   identical toggle. */
div.twoOptionsButtonWrapper {
	display: inline-flex;
	gap: 2px;
	padding: 3px;
	margin: 10px 0;
	background-color: var(--pa-surface-sunken);
	border-radius: var(--pa-radius-sm);
}
a.button.twoOptionsButton {
	float: none;
	font-size: 11px;
	font-weight: 600;
	margin: 0;
	padding: 5px 12px;
	background-color: transparent;
	border: none;
	border-radius: var(--pa-radius-sm);
	color: var(--pa-ink-label);
	transition: background-color var(--pa-transition), color var(--pa-transition), box-shadow var(--pa-transition);
}
a.button.twoOptionsButton:hover:not(.selected) {
	color: var(--pa-ink);
}
a.button.twoOptionsButton.selected {
	background-color: var(--pa-surface);
	color: var(--pa-ink);
	box-shadow: var(--pa-shadow-inset);
}

/*****************************************************************************
**** FORMULARY STYLES ********************************************************
*****************************************************************************/

.fieldBox {
	background-color: #fff;
	box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .1), inset 0 -1px 0 rgba(0, 0, 0, .07);
	margin-bottom: 20px;
	border-bottom: 1px solid #ccc;
	border-top: 4px #FFB0B0 solid !important;
}
.fieldBox:nth-child(2n) {
	border-top: 4px #F1D460 solid !important;
}
.fieldBox:nth-child(3n) {
	border-top: 4px #C6E05C solid !important;
}
input, textarea {
	border-radius: var(--pa-radius-sm);
	transition: border-color var(--pa-transition), box-shadow var(--pa-transition);
}

/* The focus rule that used to live here drew a 5px pale-cyan glow bleeding
   evenly in all directions - a 2010 affordance, and at #A5D8F8 barely visible
   against white anyway. It is deliberately removed rather than restyled.

   Two replacements were tried and both were wrong. A tight `box-shadow` ring in
   the accent colour gave *two* concentric blue halos, because browsers match
   `:focus-visible` on text fields even for mouse focus, so the --pa-focus-ring
   outline is already drawn around every focused input. Falling back to tinting
   `border-color` did nothing at all: Neptune's `.x-form-text` is more specific
   than a bare `input:focus`, so the declaration never applied - it just looked
   like it had, because the outline was doing the work.

   So the outline is the focus indicator for text fields, on its own. It is the
   one that was measured against the 3:1 requirement, and one indicator is the
   entire point of an indicator. */
.x-grid-row-editor {
	background: rgb(222, 236, 245);
}
.invalid-cell {
	background-color: #F7E9ED !important;
	border: 1px solid #EC9292 !important;
}

/********************************************************************************
**Message dialog styles
*********************************************************************************/
/* #powerTip is gone with it. jQuery PowerTip creates that element itself, so
   the rule was correct in principle - but index.html never loads
   js/libs/powertip/jquery.powertip.min.js, and nothing else references it, so
   the plugin never runs and the element is never made. Tooltips in this
   application come from ExtJS qtips and from tooltipster. */

.fieldSet {
	display: inline-flex;
	height: 45px;
	margin-bottom: 8px;
}
.fieldSet .fileInput::-webkit-file-upload-button {
	visibility: hidden;
	width: 0px;
}
.fieldSet .fileInput {
	border: solid 1px #D8D8D8;
	border-radius: 4px;
	box-shadow: 0px 0px 6px #B1B1B1;
	font-size: 14px;
	padding: 8px;
	/* Was #707070 (4.95:1). This is a field showing the filename the user
	   picked - their own data echoed back - which is content, not chrome. */
	color: #3F3F46;
	width: 500px;
	margin-left: 10px;
}
select {
	border: solid 1px #D8D8D8;
	border-radius: 4px;
	box-shadow: 0px 0px 6px #B1B1B1;
	font-size: 14px;
	padding: 8px;
	/* Was #707070. It reads as 4.95:1 if you assume a white field, which is
	   what an earlier pass in this branch did - but this rule sets its own
	   #E7E7E7 fill two lines down, where the real figure is 4.00:1. #676767
	   cleared that at 4.58:1, which is the floor again; the selected option is
	   the user's own choice of organism or database, so it reads as content.
	   #3F3F46 is 8.45:1 against the same #E7E7E7 fill. */
	color: #3F3F46;
	width: 250px;
	background: rgb(231, 231, 231);
	margin-left: 15px;
}
/* The message dialog's own layout --------------------------------------------
   `text-align: center` here was doing the work of a design decision that was
   never made. It centred the title, the body, the progress meta and the button
   row alike, so a dialog carrying three bullet points of prose ("About this
   example") centred them; #messageDialogBody had to opt back out with its own
   `text-align: left`, which is the tell.

   Centring is right for the button row and for the spinner, and wrong for
   everything that is read rather than glanced at. Both of those state it
   themselves - the button row inline, the spinner in `#messageDialogSpin` - so
   removing it here costs them nothing.

   The title also stops being 16px semibold *and* coloured *and* centred, which
   is three ways of saying "this is the heading" where a dialog needs one. It
   keeps the size and weight; the colour stays, because on this one surface a
   heading's hue is the message type - error, warning, info, success - and those
   four rules are further up this file. */
#messageDialogPanel {
	padding: 22px 24px 18px;
	text-align: left;
}
#messageDialogPanel > h4 {
	font-size: 16px;
	line-height: 1.35;
	margin: 0 0 8px;
}

/* The modal scrim, and why the dialog looked the way it did ------------------
   ExtJS masks the page with white at 0.7 opacity. On an app whose page is
   already #F3F3F3 that does not dim anything - it washes the background out to
   near-white, leaving the dialog with nothing to sit against. The 5px saturated
   frame below was compensating for that: it was the only thing separating the
   dialog from the page.

   Dimming instead of bleaching lets the frame stop shouting. Scoped to
   `body > .x-mask`, which is where ExtJS puts the modal scrim; panel-level
   loading masks live inside their own panel and keep the light treatment,
   because there the point is to grey out content in place, not to push it back.

   0.45 is dark enough to read as modal and light enough to leave the analysis
   behind it legible, which matters when the dialog is reporting on it. */
body > .x-mask {
	background-color: rgba(24, 24, 27, 0.45);
	opacity: 1;
}

/* With a working scrim the dialog can be a surface rather than a frame: a hair
   line border, the shared corner, and real elevation. The type colour moves to
   a 4px bar along the top, so error / warning / info / success still announce
   themselves at a glance without a saturated rectangle around the message. The
   shadow is deeper than the card shadow because a dialog genuinely is above
   everything else - it is the one place in this UI where that is true. */
#messageDialog.x-window-default {
	border-width: 1px;
	border-top-width: 4px;
	border-style: solid;
	border-color: var(--pa-border);
	border-radius: var(--pa-radius);
	box-shadow: 0 12px 28px -8px rgba(24, 24, 27, 0.22), 0 6px 12px -6px rgba(24, 24, 27, 0.14);
}

/* Every other ExtJS window - the Yes/No confirmations, the contact form, the
   sign-in panel, the upload dialogs - is a different window from #messageDialog
   above and so missed that fix entirely: it still had the
   5px saturated frame, square-ish corners and no shadow. Same treatment, minus
   the top accent bar, because this window has a real header bar which already
   does that job.

   The header keeps a fill rather than going neutral, so the white close tool
   stays visible; it just moves from Neptune's #3892d3, where the white title
   sat at 3.38:1, to the #287AB6 the buttons already use. One blue in the app
   instead of two, and the title clears AA at 4.62:1. */
.x-window-default {
	border-width: 1px;
	border-color: var(--pa-border);
	border-radius: var(--pa-radius);
	box-shadow: 0 12px 28px -8px rgba(24, 24, 27, 0.22), 0 6px 12px -6px rgba(24, 24, 27, 0.14);
}
/* The first attempt at this made the header *heavier*: moving Neptune's #3892d3
   to the darker #287AB6 fixed the white title's contrast but a saturated blue
   band is exactly the weight that needed removing. So the band goes neutral
   instead - the same #F6F6F6 the section headers inside .contentbox already
   use, with dark text on it, which is both lighter and consistent with the rest
   of the application. Contrast stops being a problem rather than being
   negotiated: #222 on #F6F6F6 is 15.9:1. */
/* The header carries its own 5px border on all four sides, separate from the
   window's - which is why a heavy edge survived hugging the top corners after
   the window frame was already down to a hairline.

   !important on the width, and only on the width. Neptune writes
   `.x-window-header-default { border-width: 5px !important }`, so the zeroing
   here was losing to it on every dialog in the application while this rule's
   own border-color went on winning - which is exactly why the band was still
   there and still looked like a deliberate 5px frame in --pa-border. No amount
   of added specificity fixes that; an important declaration is only beaten by
   another one. Measured before the fix: 5px/5px/5px/5px on a header whose rule
   said `0 0 1px`. */
.x-window-default > .x-window-header {
	background-color: #F6F5F0;
	background-image: none;
	border-width: 0 0 1px !important;
	border-style: solid;
	border-color: var(--pa-border);
	border-radius: var(--pa-radius) var(--pa-radius) 0 0;
}
/* Neptune's window title is white, for the dark blue band it used to sit on.
   The band is #F6F6F6 now, so every window title has to come with it - scoping
   this to .x-message-box left the contact form, the sign-in panel and the
   upload dialogs with a white title on a near-white bar. */
.x-window-default > .x-window-header .x-header-text {
	color: #222;
}

/* The close control is an <img> whose glyph comes from a sprite laid over a
   background *colour* - Neptune fills it #3892d3, which was invisible against
   the old blue header and is a saturated dot against the new light one.

   Only the fill changes. Blanking the sprite instead was tried and is wrong
   twice over: it leaves a bare coloured square, and an <img> is a replaced
   element so it cannot carry the ::before/::after that would redraw the ✕.
   A muted grey keeps the knocked-out glyph legible and stops the control from
   being the brightest thing in a header whose whole point is now to be quiet. */
.x-window .x-tool-img.x-tool-close,
.x-window .x-tool-img.x-tool-pin,
.x-window .x-tool-img.x-tool-unpin,
.x-window .x-tool-img.x-tool-plus,
.x-window .x-tool-img.x-tool-minus {
	background-color: #8A8B92;
	border-radius: 3px;
	transition: background-color var(--pa-transition);
}
.x-window .x-tool-img.x-tool-close:hover,
.x-window .x-tool-img.x-tool-pin:hover,
.x-window .x-tool-img.x-tool-unpin:hover,
.x-window .x-tool-img.x-tool-plus:hover,
.x-window .x-tool-img.x-tool-minus:hover {
	background-color: #5A5B62;
}

/* Panel headers get the window header's treatment, for the same reason and in
   the same colours. Neptune paints `.x-panel-header-default` #157FCC with a
   white title, and nothing in this file ever overrode it, so every framed
   panel in the light theme - the dataset cards in the example picker most
   visibly - wore a saturated blue band.

   That blue was never a decision. It is not in the token block at the top of
   this file, and it is not even the blue the application uses elsewhere:
   --pa-accent-blue is #1F6BC1. It is simply what the framework ships.

   The give-away that this was an oversight rather than a choice is dark.css,
   which has neutralised `.x-panel-header-default` since the dark theme landed.
   The two themes disagreed about what a panel header looks like, and the light
   one was disagreeing with itself as well - its own window headers had already
   gone neutral a few rules above. This closes that gap rather than opening a
   new question.

   Both title class names, as in the window-header rules and in dark.css:
   `-title` is the ExtJS 6 name and `-text` the 4.2.1 one this app runs. */
.x-panel-header-default {
	background-color: #F6F5F0;
	background-image: none;
	border-color: var(--pa-border);
}
.x-panel-header-default .x-panel-header-title,
.x-panel-header-default .x-panel-header-text,
.x-panel-header-default .x-header-text {
	color: #222;
}
/* The tools are <img> sprites knocked out of a background *colour*, which
   Neptune fills with the same #157FCC. Left alone they are saturated dots on a
   near-white band; muted, exactly like the window's close control above. */
.x-panel-header-default .x-tool-img {
	background-color: #8A8B92;
	border-radius: 3px;
	transition: background-color var(--pa-transition);
}
.x-panel-header-default .x-tool-img:hover {
	background-color: #5A5B62;
}

/* A window carries *two* frames: its own border, and a second one on
   `.x-window-body` inside it. Neptune paints that inner one 1px #3892d3, so
   every dialog in the app has a blue rectangle drawn just inside its edge -
   which, stacked with the outer border, is what read as a single heavy blue
   frame and sent the first attempt at this chasing the wrong element.

   The outer border is the edge now, so the inner one has nothing left to do.
   Made transparent rather than zero-width on purpose: the border box still
   occupies its 1px, so the body's content area does not change size and no
   dialog relaying-out on open. */
.x-window-body-default {
	border-color: transparent;
}

/* Each of the four below sets `border-top-color` only. They must stay after the
   rule above: same specificity, so source order is what lets the type colour
   win the top edge while the other three sides keep the neutral border. */
#messageDialog.errorDialog {
	border-top-color: rgb(211, 96, 68);
}
#messageDialog.errorDialog h4 {
	color: #C84D2F; /* was rgb(211,96,68) - 3.80:1 on the dialog's white fill */
}
#messageDialog.warningDialog {
	border-top-color: #CB7505;
}
#messageDialog.warningDialog h4 {
	color: #AC6304; /* was #CB7505 - 3.45:1 */
}
#messageDialog.infoDialog {
	border-top-color: rgb(0, 121, 220);
}
#messageDialog.infoDialog h4 {
	color: #0076D6; /* was rgb(0,121,220) - 4.40:1 */
}
#messageDialog.successDialog {
	border-top-color: #288824;
}
#messageDialog.successDialog h4 {
	/* This was "the same green as the global h4", back when the global h4 was
	   green. It is not any more - see the heading-scale note near the top of
	   this file - so the green is stated here on its own account: these four
	   rules are the one place in the application where a heading's colour is
	   the message type, and success is the only one of the four that would
	   otherwise have gone neutral. 4.53:1 on the dialog's white fill; the
	   border above keeps #288824, which clears the 3:1 non-text bar. */
	color: var(--pa-accent-green);
}
#messageDialogBody {
	text-align: left;
	/*max-height: 125px;*/
	overflow-y: auto;
}

#messageDialogSpin {
	text-align: center;
	display: none;
}
#messageDialogSpin img {
	width: 100px;
}
#messageDialog .button{float: none;}

/* The dialog's button row. It was a centred inline run of floated buttons,
   which wrapped into a ragged 2x2 block the moment a third action was added:
   format-panel.js puts the AI offer on a file error, and it landed on its own
   line under "Report error", aligned to nothing. A flex row keeps every action
   on one line while there is room and wraps them evenly when there is not, and
   DOM order is display order, so the offer can go first. */
#messageDialog .messageDialogActions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 8px;
	margin-top: 14px;
}
#messageDialog .messageDialogActions .button {
	margin: 0;
}
#messageDialog .messageDialogActions .po-ai-mark {
	margin-right: 4px;
	vertical-align: -0.15em;
}

/** SIGMA TOOLTIP OPTIONS **/
.sigma-tooltip {
	width: 200px;
	height: 180px;
	background-color: #FFF;
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
	border-radius: 4px;
}
.sigma-tooltip-header, .sigma-tooltip-footer {
	border-bottom: 1px solid #D4D4D4;
	padding: 4px;
	text-align: center;
	color: var(--pa-link); /* was #0076E2 - 4.48:1 on the tooltip's white fill */
}
.sigma-tooltip > .arrow {
	border-width: 10px;
	position: absolute;
	display: block;
	width: 0;
	height: 0;
	border-color: transparent;
	border-style: solid;
}
.sigma-tooltip.top {
	margin-top: -12px;
}
.sigma-tooltip.top > .arrow {
	left: 50%;
	bottom: -10px;
	margin-left: -10px;
	border-top-color: #FFF;
	border-bottom-width: 0;
}
.sigma-tooltip.bottom {
	margin-top: 20px;
}
.sigma-tooltip.bottom > .arrow {
	left: 50%;
	top: -10px;
	margin-left: -10px;
	border-bottom-color: #FFF;
	border-top-width: 0;
}
.sigma-tooltip.left {
	margin-left: -12px;
}
.sigma-tooltip.left > .arrow {
	top: 50%;
	right: -10px;
	margin-top: -10px;
	border-left-color: #FFF;
	border-right-width: 0;
}
.sigma-tooltip.right {
	margin-left: 12px;
}
.sigma-tooltip.right > .arrow {
	top: 50%;
	left: -10px;
	margin-top: -10px;
	border-right-color: #FFF;
	border-left-width: 0;
}

/********************************************************************************
**STYLES FOR ALL STEPS
*********************************************************************************/
td.whiteBackground {
	border-color: none;
	background: #FFF;
}

span.stepIndicatorItem {
	width: 25%;
	display: inline-block;
	text-align: center;
	color: #fff;
	height: 30px;
	font-size: 15px;
}
span.currentStep {
	background: #fff;
	color: #000;
}
.highcharts-tooltip {
	z-index: 9999 !important
}
.highcharts-tooltip > span {
	background: #fff;
}
i.fa.fa-paint-brush-o:before {
	font-family: "CustomIcons" !important;
	content: "\e600";
}

/* Checkboxes and radios ------------------------------------------------------
   These were drawn as Font Awesome glyphs: `\f096` / `\f046` for a checkbox and
   `\f10c` / `\f05d` for a radio. Two things were wrong with that, one cosmetic
   and one not.

   The cosmetic one: a glyph is a letterform, so the "box" was a hairline square
   at whatever weight the icon font happened to draw it, and the checked state
   was a *tick sitting outside* the square rather than a filled control. At 13px
   in the network's settings column that is a row of faint outlines, which is
   most of why that panel read as older than the rest of the application.

   The one that is not cosmetic: `\f05d` - the radio's checked glyph - is a
   check mark in a circle. A tick means "this is on", independently of the
   others; a dot means "this is the one of the group". The control was drawn
   saying the wrong thing about its own semantics, in the one place
   (Node coloring, Choose what edges represent) where the distinction is the
   whole point of using a radio.

   Drawn in CSS instead: a real 16px box with a real fill, a stroked tick for
   the checkbox and an inset dot for the radio. 16px of control plus a 7px gap
   is the 23px the glyph rule reserved, so nothing moves horizontally.

   The fill is the app's interaction blue rather than the old #3C8E49. Green in
   this UI means *go* - it is Apply, Run PaintOmics, Next step - and a checkbox
   is not an action; blue is what already marks selection and focus here. It is
   --pa-accent-blue, so a checked control and a link agree.

   The input itself moves from `display: none` to being visually hidden but
   still in the accessibility tree and still focusable. `display: none` removes
   an element from tab order entirely, so none of these controls could be
   reached or toggled from the keyboard at all - the Tools panel's ten
   checkboxes, the two radio groups, the Step 4 view toggles. Absolutely
   positioned, so it takes no space and no layout changes. */
/* A handful of checkboxes in this app are bare `<input type="checkbox">` with
   no wrapper of any kind - Step 3's "Choose the pathways below and Paint!" is
   the visible one - so neither the rule below nor the framework one further
   down can reach them and they render in the platform's own blue. One line puts
   them in the app's accent without taking over their drawing. */
input[type="checkbox"],
input[type="radio"] {
	accent-color: var(--pa-accent-blue);
}
div.radio input[type=radio], div.checkbox input[type='checkbox'] {
	position: absolute;
	width: 16px;
	height: 16px;
	margin: 0;
	opacity: 0;
	pointer-events: none;
}
/* So the hidden input above resolves against its own wrapper rather than
   against whatever ancestor happens to be positioned. */
div.radio, div.checkbox {
	position: relative;
}
div.radio input[type=radio] + label {
	display: block;
	padding-left: 23px;
}
div.checkbox:not(.viewbox):not(.nobox) input[type='checkbox'] + label:before,
div.radio input[type='radio'] + label:before {
	content: "";
	display: inline-block;
	box-sizing: border-box;
	width: 16px;
	height: 16px;
	margin-right: 7px;
	/* Optical, not arithmetic: pulls the box onto the label's baseline so the
	   two read as one line rather than as a box with text beside it. */
	vertical-align: -3px;
	border: 1.5px solid #8A8A91;
	background-color: #FFFFFF;
	background-repeat: no-repeat;
	background-position: center;
	background-size: 100% 100%;
	transition: background-color var(--pa-transition), border-color var(--pa-transition), box-shadow var(--pa-transition);
}
div.checkbox:not(.viewbox):not(.nobox) input[type='checkbox'] + label:before {
	border-radius: 4px;
}
div.radio input[type='radio'] + label:before {
	border-radius: 50%;
}
/* The tick is a stroked path in a data URI rather than a glyph, so it scales
   with the box and cannot be affected by an icon font failing to load. */
div.checkbox:not(.viewbox):not(.nobox) input[type="checkbox"]:checked + label:before {
	background-color: var(--pa-accent-blue);
	border-color: var(--pa-accent-blue);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M3.6 8.4l2.9 2.9 5.9-6.3'/%3E%3C/svg%3E");
}
/* An inset white ring is what leaves a dot: the fill covers the whole circle
   and the ring eats everything but the middle. One declaration, no second
   pseudo-element to position. */
div.radio input[type=radio]:checked + label:before {
	background-color: var(--pa-accent-blue);
	border-color: var(--pa-accent-blue);
	box-shadow: inset 0 0 0 3.5px #FFFFFF;
}
div.checkbox:not(.viewbox):not(.nobox):hover input[type="checkbox"]:not(:checked) + label:before,
div.radio:hover input[type=radio]:not(:checked) + label:before {
	border-color: #52525B;
}
/* The ring the rest of the application uses, moved onto the drawn box because
   the input it belongs to is invisible. */
div.checkbox input[type="checkbox"]:focus-visible + label:before,
div.radio input[type=radio]:focus-visible + label:before {
	outline: 3px solid var(--pa-focus-ring);
	outline-offset: 2px;
}
/* The two variants that are deliberately not boxes. `viewbox` is Step 4's
   show/hide toggle, where the glyph is an eye and the state is what the eye is
   doing; `nobox` drops the mark entirely and colours the label instead. Both
   keep the Font Awesome technique, so it is restated for them here rather than
   inherited from a base rule that now draws a box. */
div.checkbox.viewbox input[type='checkbox'] + label:before,
div.checkbox.nobox input[type='checkbox'] + label:before {
	display: inline-block;
	font-family: FontAwesome;
	font-style: normal;
	font-weight: normal;
	line-height: 1;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	padding-right: 0px;
	width: 23px;
}
div.checkbox.viewbox input[type="checkbox"] + label:before {
	content: "\f070";
	/* Checkbox Unchecked */
	color: #D63636;
}
div.checkbox.viewbox input[type="checkbox"]:checked + label:before {
	content: "\f06e";
	/* Checkbox Checked */
	color: #3C8E49;
}
div.checkbox.nobox input[type="checkbox"] + label:before {
	content: none;
}
div.checkbox.nobox input[type="checkbox"]:not(:checked) + label {
	color: #969696;
}
div.checkbox.nobox input[type="checkbox"]:not(:checked) + label > i {
	color: #969696 !important;
	border-color: #969696 !important;
}
div.checkbox label, div.radio label {
	padding-left: 0;
}

/* One rail for both controls, and a hanging indent for either ----------------
   Two faults, one shape, and they were most visible in the network's Tools
   column where radios and checkboxes are stacked in the same panel.

   The first is that they did not share a left edge. The drawn box is an inline
   `:before` at the start of the label's first line, so where the control lands
   is whatever padding the label carries: 23px for a radio (the rule further up)
   and 0 for a checkbox (the rule directly above). In the KEGG Tools panel that
   put "Classification" and "Gene expression" 23px right of "Show all node
   labels" and of every `h5` heading they sit under - a stagger with nothing
   choosing it, in a column 214px wide.

   The second is what happens when a label wraps. With the box in the first
   line's flow, every line after the first starts at the label's content edge -
   which is exactly where the box is - so "Linked biological / processes" put
   "processes" underneath the radio, and "Calculate layout on / background",
   "Always use combined p- / value" and "Shared biological / features" all did
   the same. A wrapped line running under its own control is the one alignment
   error a reader cannot read past, because it breaks the column that says which
   words belong to which box.

   `padding-left` plus a negative `text-indent` of the same size fixes both at
   once: the first line is pulled back so the box sits on the container's rail,
   and every line - first or wrapped - starts at the content edge just past it.
   23px is the box (16px) and its gap (7px), from the `:before` rule above.

   Deliberately not `display: flex` on the label, which would give the same two
   columns without arithmetic: these labels also hold inline `<b>`/`<i>` runs,
   and under flex each becomes its own flex item, which breaks the sentence into
   separately-wrapping boxes. Deliberately not `position: absolute` on the box
   either - inline is what keeps it on the first line's baseline through the
   `vertical-align: -3px` above, at whatever font size the call site uses. */
div.radio input[type='radio'] + label,
div.checkbox:not(.nobox) input[type='checkbox'] + label {
	display: block;
	padding-left: 23px;
	text-indent: -23px;
}
/* `viewbox` draws an eye glyph 23px wide and keeps the 7px gap, so its hang is
   30px, not 23. Sharing the number would have left its wrapped lines 7px shy of
   its first one - a smaller version of the same fault. */
div.checkbox.viewbox input[type='checkbox'] + label {
	padding-left: 30px;
	text-indent: -30px;
}
/* `text-indent` is inherited and applies to the first line of every block box
   inside, so a label's own block children would each be pulled 23px left. Three
   of the checkboxes in the Tools panel carry their hint that way ("Disable the
   auto-layout for network."), and without this reset the hint gained the
   outdent that belongs to the control. Inline children need no reset - the
   property does nothing to them. */
div.radio > label > *,
div.checkbox > label > * {
	text-indent: 0;
}
/* The drawn box is itself an inline-block, so it inherits the outdent and
   applies it to its own content. The plain checkbox and radio draw an empty
   box and do not notice; `viewbox` draws a Font Awesome eye, which the outdent
   would have carried 30px out of the control it belongs to. */
div.checkbox input[type='checkbox'] + label:before,
div.radio input[type='radio'] + label:before {
	text-indent: 0;
}

/* Sliders ---------------------------------------------------------------------
   The seven sliders in the network's settings column, and the cluster-count one
   beside them, were jQuery UI's stock 2009 widget: a 1px #ddd frame around an
   #eee gradient GIF for the track, and a bold-bordered #f6f6f6 square with a
   second gradient GIF for the thumb. Two raster images per slider, for a
   control that is four rounded rectangles.

   The track also said nothing. Without `range`, jQuery UI draws no filled
   portion, so a slider at 10% and a slider at 90% were the same picture except
   for where the thumb sat - which in a column of four stacked sliders is the
   difference between reading the filter settings at a glance and reading each
   one. `range: "min"` is passed at every call site now, which is what the
   `.ui-slider-range` rule below paints.

   `!important` on the track height only, matching what stood here: jQuery UI's
   own `.ui-slider-horizontal { height: .8em }` is a single class, so it loses
   on specificity, but ExtJS re-applies inline heights to some of these
   containers. Everything else wins on specificity alone. */
.slider-ui.ui-slider,
.ui-slider-horizontal {
	height: 4px !important;
	background: #E1E1E4;
	background-image: none;
	border: 0;
	border-radius: 999px;
	/* The thumb overhangs the track by 6px top and bottom, so the widget needs
	   room around it or the row above clips it. */
	margin-top: 14px;
	margin-bottom: 14px;
}
.slider-ui.ui-slider .ui-slider-range,
.ui-slider-horizontal .ui-slider-range {
	background: var(--pa-accent-blue);
	background-image: none;
	border-radius: 999px;
}
.slider-ui.ui-slider .ui-slider-handle.ui-state-default,
.ui-slider-horizontal .ui-slider-handle,
.ui-widget-content .ui-slider-handle.ui-state-default {
	width: 16px;
	height: 16px;
	top: -6px;
	margin-left: -8px;
	border: 1.5px solid var(--pa-accent-blue);
	border-radius: 50%;
	background: #FFFFFF;
	background-image: none;
	box-shadow: 0 1px 3px rgba(24, 24, 27, 0.20);
	cursor: grab;
	transition: box-shadow var(--pa-transition), background-color var(--pa-transition);
}
/* A ring rather than a size change: growing the thumb on hover moves the target
   out from under the cursor mid-drag, which is the one thing a slider must not
   do. */
.slider-ui.ui-slider .ui-slider-handle.ui-state-hover,
.slider-ui.ui-slider .ui-slider-handle.ui-state-focus,
.ui-widget-content .ui-slider-handle.ui-state-hover,
.ui-widget-content .ui-slider-handle.ui-state-focus {
	background: #FFFFFF;
	background-image: none;
	box-shadow: 0 1px 3px rgba(24, 24, 27, 0.20), 0 0 0 4px rgba(25, 88, 158, 0.16);
}
.slider-ui.ui-slider .ui-slider-handle.ui-state-active,
.ui-widget-content .ui-slider-handle.ui-state-active {
	background: var(--pa-accent-blue);
	background-image: none;
	cursor: grabbing;
	box-shadow: 0 1px 3px rgba(24, 24, 27, 0.20), 0 0 0 4px rgba(25, 88, 158, 0.22);
}

/********************************************************************************
**STYLES FOR STEP 1
*********************************************************************************/

/* The old shadow here was `0 0 10px #dfdfdf` - a fully opaque grey spreading
   evenly in every direction, so the panel glowed rather than sat above the
   page. The token shadow has a direction and an alpha, which is what makes it
   read as height instead of as a halo. */
.omicSubmittingPanel {
	border: solid 1px var(--pa-border);
	box-shadow: var(--pa-shadow);
	border-radius: var(--pa-radius);
}
.step1LateralMenu {
	padding-right: 20px;
	border-right: dotted 1px #E4E4E4;
}
.step1LateralMenu p {
	padding: 0;
	margin: 3px 0px;
}
/* `overflow: hidden` is what makes the coloured omic header clip to the card's
   top corners, so the header needs no radius of its own. */
.omicbox {
	border: 1px solid var(--pa-border);
	background: #fff;
	border-radius: var(--pa-radius);
	overflow: hidden;
	margin: 5px 10px;
	box-shadow: var(--pa-shadow);
}
/* Same weight problem as the rail, and the same answer. A 50px band of
   saturated colour carrying 30px type made the omic name the loudest thing on
   the page, louder than the "2. Choose the files to upload" heading that owns
   the section and far louder than the fields the card actually exists to
   collect. Nothing about an omic's name needs to be twice the size of the
   step heading.

   The band came down to 40px and the name to 20px, which was the same size as
   the "Available omics" / "Selected omics" panel titles it sits between. That
   fixed the size and left the shape: a full-width band of saturated colour is
   how a framed panel was drawn in 2012, and it is the last thing in the upload
   form still drawn that way. It is also the loudest object on a page whose
   other cards - the AI section, the example note, the help panel - are all
   white surfaces separated by hairlines.

   So the colour stops being the surface and becomes a marker on it. The header
   is a white band with the card's own hairline under it, like every other card
   header in the application, and the omic's hue is a 5px bar down its leading
   edge. Nothing is lost by that: the bar is the same colour at full strength,
   in the position an eye scanning a column of cards actually reads first, and
   the drag chips in "Available omics" - where the colour has no other carrier -
   keep their solid fill.

   The six hues are the app's type-coding: users identify an omic by its colour
   here, in the legends and in the pathway diagrams, so every value below is
   exactly the one it has always been. They move from `background` to a custom
   property because they now have two jobs - the chip's fill and the card's bar -
   and a colour used twice should be written once. */
.omicboxTitle,
.availableOmicsBox {
	--pa-omic-color: rgb(85, 201, 166);
}
.omicboxTitle.metabolomicsFileBox, .availableOmicsBox[title=metabolomics] {
	--pa-omic-color: rgb(121, 176, 236);
}
.omicboxTitle.proteomicsFileBox, .availableOmicsBox[title=proteomics] {
	--pa-omic-color: rgb(180, 161, 221);
}
.omicboxTitle.otherFileBox, .availableOmicsBox[title=otheromic] {
	--pa-omic-color: rgb(214, 127, 107);
}
.omicboxTitle.bedbasedFileBox, .availableOmicsBox[title=bedbasedomic] {
	--pa-omic-color: #738B9D;
}
.omicboxTitle.miRNAbasedFileBox, .omicboxTitle.moreBasedFileBox, .availableOmicsBox[title=regulatoryomic] {
	--pa-omic-color: rgb(154, 150, 78);
}
.omicboxTitle {
	display: flex;
	align-items: center;
	min-height: 44px;
	background: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
	/* Inset rather than a border: `.omicbox` clips to its own radius, so a
	   5px inset shadow follows the rounded top-left corner instead of being
	   squared off by a border box that predates it. It also takes no space, so
	   the fields below stay on the same left rail as the title. */
	box-shadow: inset 5px 0 0 var(--pa-omic-color);
	padding-left: 20px;
}
/* Near-black ink. The header is white now, so the contrast note that used to
   live here - white-on-pastel ran 2.04:1 to 3.55:1, near-black cleared AA on
   all six - applies to the drag chips below, which still carry the fill. */
div.omicboxTitle h4 {
	font-size: 15px;
	color: #1A1A18;
	margin: 0;
	/* The delete control is `float: right` inside this heading, and a flex item
	   shrink-wraps its content - so without this the trash icon sat hard against
	   the last letter of the omic's name instead of at the far end of the bar.
	   Filling the row gives the float an edge to reach. */
	flex: 1 1 auto;
}
.omicboxTitle a, .availableOmicsBox a {
	color: #1A1A1A;
}
.availableOmicsBox {
	cursor: move;
	/* The chip carries the omic's hue the way the card it becomes carries it: a
	   5px bar down the edge, and a body tinted rather than filled.

	   Three passes are on the record here and the answer is the third. It began
	   as a flat block of --pa-omic-color, which made this column four saturated
	   slabs against four white cards beside it. It then became .omicboxTitle
	   exactly -- white body, hue as a bar -- and the owner's read was that on a
	   30px strip a bar on white is a stripe on a mostly-empty box. Then a
	   gradient, saturated at the left and lightening across, which is still a
	   filled chip and is what this replaces.

	   Tinting the body at 18% is what the white version was missing: the chip
	   still reads as its colour at a glance -- these four are told apart by hue
	   and nothing else, so it has to -- while the saturated hue is spent on the
	   bar, which is the mark the card uses. The two columns now say the same
	   thing in the same way at two sizes.

	   The base and the strength are custom properties because the answer is not
	   the same in both themes. An earlier version mixed toward #FFFFFF in both,
	   on the reasoning that the chip's near-black ink needs a light body: the
	   result was four near-white slabs down the left edge of a dark page, which
	   the owner read as "terrible" and which is what a light-theme value looks
	   like when it is carried into a dark one unchanged. dark.css re-bases the
	   mix on the theme's own surface and takes the ink with it. The flat first
	   declaration is the fallback for a browser without color-mix(). */
	background-color: #FFFFFF;
	background-color: color-mix(in srgb,
		var(--pa-omic-color) var(--pa-omic-tint, 18%),
		var(--pa-omic-tint-base, #FFFFFF));
	background-image: none;
	/* No bar. The card these become carries one, and on a 30px chip it read as
	   a stripe stuck to the edge rather than as the card's mark -- which is the
	   same objection the white-bodied version drew, and the owner's again when
	   the bar came back on the tinted one. The tint alone says which omic this
	   is; that is all the chip has to say. */
	padding: 7px 12px;
	/* No horizontal margin: the column these sit in is already on the card's
	   inset, and 10px here put the first thing the eye meets under
	   "2. Choose the files to upload" on a left edge of its own. */
	margin: 10px 0;
	/* These are drag sources for the cards below, so they take the same corner
	   as the card they become. */
	border-radius: var(--pa-radius);
	transition: box-shadow var(--pa-transition), transform var(--pa-transition);
}
/* They are drag sources, and the lift is what says so. */
.availableOmicsBox:hover {
	box-shadow: var(--pa-shadow);
}
/* The drag sources match the cards they become, so they come down with them. */
div.availableOmicsBox h4 {
	font-size: 14px;
	color: #1A1A1A;
	margin: 0;
}
.dragHerePanel {
	display: none;
	text-align: center !important;
	color: #6D7075; /* was #8B8E93 - 3.04:1 on this panel's own #F6F6F6 fill */
	font-size: 22px;
	background: #F6F5F0;
	padding: 10px;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
}

#additionalInfoContainer {
	margin: 10px auto;
}

/* The information column's note, in Sections 1 and 2.

   It is the Help panel below, in the two sections that did not have one: same
   surface, same border, same small uppercase label with its ⓘ, same prose. The
   declarations live on the Help panel's own rules, which this class joins, so
   the three cannot drift; only the margin differs, because the Help panel is
   vertically centred in a 400px row and these two sit at the top of theirs. */
.po-info-note {
	margin: 0 !important;
}
div.contentbox .po-info-note p:last-child,
.po-info-note p:last-child {
	margin-bottom: 0;
}
/* The one-liner and the facts keep their own type inside the note; only their
   spacing is the note's. */
.po-info-note p.ai-intro-copy {
	font-size: 13px;
	line-height: 1.55;
}
/* How the draft is made, a step under the sentence that says what it is.

   It was three <li>s -- "PubMed & Europe PMC", "Every citation verified",
   "Reads your uploaded values" -- in the inline middot list Section 3 uses for
   its formats. That list holds nine short names on one wide line; in a 302px
   column it wrapped after every item, so the separators never rendered and the
   note ended in three unpunctuated fragments stacked under a paragraph. Two
   passes tried to rescue it (unwrapping the separators, then stacking one per
   line on purpose) and the owner's read on the second was the same as on the
   first. A sentence says all three things, punctuated, in the voice of the
   paragraph above it. */
.po-info-note p.po-ai-method {
	margin: 8px 0 0;
	font-size: 12.5px;
	line-height: 1.5;
	color: var(--pa-ink-muted);
}
/* "Where your data goes", and the sentence that answers it.

   The same heading-and-paragraph pair as "What you get" at the top of the note,
   so the column reads as two labelled blocks rather than as a paragraph with a
   link stuck to the end of it. The (!) is what the owner asked for -- a mark
   that says there is something here to read before the feature runs -- and it
   is amber rather than the red an earlier version used beside the consent box,
   where it read as an error on the form. The blue ⓘ above it is information;
   this one is a caution, and the two colours are the difference.

   It is a heading and not an inline icon before a sentence for a measurable
   reason: inline, the icon put the words 15.8px right of the column's text rail
   and everything else in the note started 15.8px left of them. As a flex item
   in an h5 the icon takes its own slot and the text starts where the rail is. */
.po-info-note h5.po-ai-privacy-head {
	margin: 14px 0 5px;
}
.po-info-note h5.po-ai-privacy-head i {
	color: var(--pa-status-warning, #B26A00);
}
/* The link is the heading: it takes the heading's type rather than the muted
   12.5px the same link wears when it ends a paragraph. */
.po-info-note h5.po-ai-privacy-head a {
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	text-decoration: none;
}
.po-info-note h5.po-ai-privacy-head a:hover {
	color: var(--pa-accent-blue);
	text-decoration: underline;
}
/* The answer, written by fillAIProvenance once /ai_provider replies -- which is
   after this column has been measured, so see the updateLayout it calls. */
.po-info-note p.po-ai-provider-inline {
	margin: 0;
	font-size: 12.5px;
	line-height: 1.5;
	color: var(--pa-ink-muted);
}

/* The upload form's help panel ------------------------------------------------
   A 50px blue ⓘ over a 20px centred "Help", both stacked above three short
   paragraphs of left-aligned prose. The icon was the largest single glyph
   anywhere in the application - larger than the product name in the header -
   and it was announcing three sentences of instructions.

   It is a note, so it is dressed as one: the quiet surface the rest of the
   app uses for secondary panels, a small label with the icon inline at the
   size of the text beside it, and the prose set to the note measure. The
   heading stops being centred, because nothing under it is.

   `text-align` and the flex centring both go rather than being overridden -
   a centred heading over ragged-left body copy is the arrangement that made
   this read as a widget dropped onto the page instead of part of it. */
/* The three section notes and the Help panel: a column of quiet text, not a
   filled card.

   They were bordered, rounded, filled boxes -- and on a form that already draws
   a panel per section, that made every section a box inside a box, three of them
   stacked down one edge of the page. In the dark theme they were worse: three
   grey slabs, the heaviest things on the screen, holding the lightest content on
   it. The owner's read was "too terrible", and it is the same read as the pill
   tags and the chip boxes before it -- this interface does not want more frames.

   A hairline down the left instead. It marks the column as an aside, groups the
   label with its prose, and draws one line where the box drew four. */
#additionalInfoContainer div.content,
.po-info-note {
	color: var(--pa-ink-body, #222);
	height: auto;
	border: 0;
	border-radius: 0;
	background: none;
	margin: 24px auto;
	padding: 0;
}
/* The aside column keeps its distance and nothing else. It had a hairline down
   its left edge to mark it as an aside; the owner's read was that the two
   columns of a panel do not need a line between them, and they do not -- the
   label above the note ("WHAT YOU GET", small, uppercase, with its blue ⓘ)
   already says this column is not the form. A rule there is a third vertical
   edge inside a panel that already has two. */
.po-note-col {
	padding-left: 18px;
}

#additionalInfoContainer div.content h5,
.po-info-note h5 {
	display: flex;
	align-items: center;
	gap: 8px;
	color: #27272A;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	margin: 0 0 10px;
}

#additionalInfoContainer div.content h5 i,
.po-info-note h5 i {
	/* The inline `font-size: 50px` this used to fight with is gone from
	   PA_Step1Views.js; the icon is now sized here, with the label. */
	font-size: 14px;
	color: var(--pa-accent-blue);
}

#additionalInfoContainer div.content p,
div.contentbox .po-info-note p,
.po-info-note p {
	margin: 0 0 8px;
	font-size: 13px;
	line-height: 1.55;
	max-width: var(--pa-measure-note);
	/* Inset twice: `div.contentbox p` gives every paragraph the card inset,
	   and `div.content` above has already inset this one. The help text
	   therefore started 26px right of its own "Help" heading, which takes no
	   padding at all - the heading's icon on one edge and the prose it
	   introduces on another, inside a 200px-wide card. */
	padding-left: 0;
	padding-right: 0;
}
#additionalInfoContainer div.content p:last-child {
	margin-bottom: 0;
}

/********************************************************************************
**STYLES FOR STEP 2
*********************************************************************************/
.showOtherCompoundsButton {
	margin-left: 15px
}
.showOtherCompoundsButton.visible {
	color: #ff6666
}
.omicSummaryContainer {
	max-width: var(--main-max-width);
	margin: 5px 10px;
}
.contentbox.metaboliteBox {
	margin: 5px 0;
}
/* The action link that closes a card — Step 3's "Choose the pathways below and
   Paint!", Step 2's "Download ID/Name mapping results".

   Three things were wrong with `display: block; text-align: center; 18px`.

   Size: 18px against 14px body copy is a 1.29 step, and the card's own h2 is
   19px. A link inside the card was therefore within a pixel of the heading that
   titles the card - so the card had two things claiming to be its most
   important line, and the heading lost. 15px is a step above body copy without
   entering heading territory.

   Rail: every other line in these cards - h2, p, li - starts at
   --pa-card-inset. `margin: 10px` plus centring put this one line on neither
   that rail nor any other, so it floated free under a left-aligned paragraph.
   It is the same defect the note on `#clusternumber_box .divForm` describes on
   Step 2, and it has the same fix: put it where the prose starts.

   Icon alignment: as a block with an inline `<i>` before the text, the glyph
   sat on the text baseline and its own line-height decided how high - which is
   the vertical misalignment visible in the screenshots. inline-flex with
   `align-items: center` centres the two against each other and needs no magic
   number, so it stays right if either the icon or the label changes size. */
#download_mapping_file {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin: 6px var(--pa-card-inset) 16px;
	font-size: 15px;
	font-weight: 600;
	line-height: 1.4;
}
/* One step down and unbolded: the glyph is a cue for the label, not a second
   voice at the same weight. `line-height: 1` because Font Awesome's own is
   what would otherwise push it off the centre the flex box just established. */
#download_mapping_file > i {
	font-size: 14px;
	font-weight: 400;
	line-height: 1;
}
/* Two call sites, two nestings. On Step 3 this link is a direct child of the
   card's own div and the inset above is what puts it on the rail; on Step 2 it
   is written inside the paragraph that introduces it, and `div.contentbox p`
   has already applied --pa-card-inset to that paragraph - so the margin landed
   on top of it and the link sat 52px in, 26px right of every other line in the
   card. Measured: x=182.4 against a rail at 156.4.

   #paint_link is no longer in these rules: it is a real .button in the summary
   band's heading row now (see "The summary band" further down). */
div.contentbox p > #download_mapping_file {
	margin-left: 0;
	margin-right: 0;
}
/* The mapping-statistics section, revealed by "Show mapping" on Step 3.

   Three faults in one card, all measured against its own contents: the ExtJS
   vbox honors a 10px side margin every sibling section carries and this one
   lacked, so its border sat 10px left of every other card on the page; and
   its title and download link took --pa-card-inset (26px) while the omic
   summary cards below them sit at the .omicbox margin (11px with the border).
   A title over a stack of cards pads to the cards' edge - the same answer
   "Selected omics" gives on the upload form - so the title and the link come
   to the cards rather than four cards moving 15px for one line of type. */
#statsViewContainer {
	margin-left: 10px;
	margin-right: 10px;
}
#statsViewContainer h2 {
	padding-left: 11px;
	padding-right: 11px;
}
#statsViewContainer > div > #download_mapping_file {
	margin-left: 11px;
	margin-right: 11px;
}
#dbs_message, #clusternumber_box, #threshold_box {
	margin: 10px 0;
	width: 100% !important;
}
#dbs_message div {
	padding: 0 var(--pa-card-inset) 10px;
}
#clusternumber_box div, #clusternumber_box span, #threshold_box div, #threshold_box span {
	border: 0 !important;
	background-color: white !important;
	width: 100% !important;
}
/* `margin: 0 auto` centred this form in its card, which put the one control
   the card exists to offer 395px right of the heading and the paragraph that
   introduce it - the only block on Step 2 not starting on the card's own rail.
   Centring a single left-labelled form row aligns it with nothing: the row has
   a label edge and a field edge, and neither lands under the prose. Left on the
   card inset, the label sits under the first word of the paragraph above it. */
#clusternumber_box .divForm, #threshold_box .divForm {
	width: 60% !important;
	margin: 0 0 0 var(--pa-card-inset) !important;
}
/* Vertical only. The horizontal 20px was applied on top of the
   --pa-card-inset these paragraphs already get from `div.contentbox p`, so the
   prose in these two cards sat 20px to the right of their own h2 and of every
   other paragraph on the page. */
#clusternumber_box p, #threshold_box p {
	/* 14px, not 20: the sibling "Multiple databases used" card leaves its
	   first paragraph on the browser's default 1em margin, and the two
	   cards' headings end on exactly the same line - so the 6px extra here
	   was the whole reason these cards' opening lines sat on different
	   baselines across one row. */
	margin: 14px 0;
}
/* Each database gets a name, a paragraph about it, and the table of what
   matched in it. The table is the answer the card exists to give, so it is
   centred under the prose that introduces it rather than left hanging between
   two unequal margins, and it carries the same treatment as every other table
   on the results pages - it was the one place left with a pure-black 1px
   border and cells with no vertical padding. */
#dbs_message dt {
	font-weight: 600;
	font-size: 13px;
	color: #27272A;
	/* Flush with the prose and the heading; 5px left the database names five
	   pixels adrift of every other line in the card. */
	padding-left: 0;
	margin-top: 20px;
}
#dbs_message dd {
	margin: 6px 0 0;
}
/* Same as the results-page card: the prose uses the card's width rather than a
   reading measure, so it does not sit as a column with empty space beside it. */
#dbs_message p {
	max-width: none;
}
#dbs_message dd > div[id^="matching_table_"] {
	display: flex;
	justify-content: center;
	padding: 18px 0 6px;
}
#dbs_message table {
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
	border-collapse: separate;
	border-spacing: 0;
	overflow: hidden;
	background-color: #FFFFFF;
	min-width: 340px;
	/* `div[id^=matching_table] table` further down this file centres these
	   tables, and that inherits into the cells. The <th> below then sets itself
	   back to `left` while the <td> did not, so "Omic" and "Gene expression"
	   sat in one column on two different edges - 27px apart here, and a
	   different 27px in the Reactome table next to it, because centred text
	   moves with the column width and the two tables hold different numbers.
	   Set the alignment once on the table; the last-child rule below still
	   takes the counts back to the right. */
	text-align: left;
}
#dbs_message table th {
	padding: 9px 18px;
	border-bottom: 1px solid var(--pa-border);
	background-color: #F9F8F4;
	color: #52525B;
	font-size: 11.5px;
	font-weight: 600;
	text-align: left;
	white-space: nowrap;
}
#dbs_message table td {
	padding: 9px 18px;
	border-top: 1px solid #F1F1F3;
	font-size: 12.5px;
	color: #27272A;
}
/* The first row is the <th> header, so the first data row is the second one -
   without this it draws a second rule directly under the header's. */
#dbs_message table tr:nth-child(2) td {
	border-top: 0;
}
/* Counts read as a column of magnitudes, so they line up digit for digit. */
#dbs_message table th:last-child,
#dbs_message table td:last-child {
	text-align: right;
	font-variant-numeric: tabular-nums;
}

/********************************************************************************
**STYLES FOR STEP 3
*********************************************************************************/
#jobName {
	margin-left: 7px;
}
.contingencyTable {
	border-collapse: collapse;
	text-align: center;
	font-size: 8px;
	margin: 2px 15px;
}
.contingencyTable tbody tr {
	border-top: 1px solid #9c9c9c;
}
.contingencyTable td, .contingencyTable th {
	border-right: 1px solid #9c9c9c;
}
.contingencyTable td:last-child, .contingencyTable th:last-child {
	border: none;
}
.pathwaysTable {
	width: 100%;
	text-align: center;
}

/* `div[id^="pathwayNetworkBox_"].fullscreen` stood here and had never once
   applied: nothing in the application adds a `fullscreen` class, and the old
   full-screen path went through the native API, which sets a `:fullscreen`
   pseudo-class instead. The rules that do the work now are `.paNetExpanded`,
   further down this file with the rest of the network layout. */

/* Column headers used to be rotated - 45 degrees for the p-value columns and a
   full 90 for the narrow count columns - because at the old width there was no
   room to set them straight. Deleting the 65px navigation rail bought that room
   back, so they read horizontally now and wrap onto two or three short lines
   instead. Rotated text is the single hardest thing to scan in a table, and it
   was being used on the two columns this table is sorted by.

   `white-space: normal` is what allows the wrap; ExtJS otherwise keeps header
   text on one line. */
.header-45deg .x-column-header-text,
.header-90deg .x-column-header-text {
	-webkit-transform: none;
	-moz-transform: none;
	-o-transform: none;
	transform: none;
	display: block;
	white-space: normal;
	/* Wrap at spaces only. `break-word` was tried and shredded the two narrow
	   count columns into "Uniq / ue / gene / s" - at 50px wide they need to
	   break between "Unique" and "genes", never inside a word. */
	overflow-wrap: normal;
	word-break: normal;
	hyphens: none;
	line-height: 1.25;
	text-align: center;
	font-size: 11px;
	/*padding: 0px;*/
	/*width: 40px;*/
}
/* 25px of top padding existed to clear the rotated text above. With the headers
   set straight it just pushed them off centre, so it comes back to a normal
   cell padding. */
.x-group-sub-header .x-column-header-inner {
	padding-top: 6px !important;
}
/*.sigma-scene, .sigma-labels, .sigma-mouse {
max-width: 1300px;
max-height: 600px;
}*/
/* "Building network…", over the diagram while it lays out.
   40px terracotta with a spinning cog beside it, which made a routine wait the
   single largest and most saturated thing on the results page - three times the
   size of the card heading above it, and in a colour this UI otherwise uses for
   the numbered step markers. A wait message is not news. Body-sized, muted, and
   centred properly rather than by a hard-coded 350px of top padding, which only
   ever landed in the middle of a network box that happened to be 775px tall. */
div[id ^= "pathwayNetworkWaitBox_"] {
	position: absolute;
	inset: 0;
	height: 100%;
	width: 100%;
	background-color: rgba(255, 255, 255, 0.76);
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	font-size: 15px;
	color: var(--pa-ink-muted);
}
/* `.resumeLayout` used to paint its controls green with `!important`, which is
   why three of the five items in the network toolbar were a different colour
   from the other two.

   The class is not a colour, though - it is a state flag, and only on one
   control. PA_Step3Views adds it to the layout button while the layout is
   paused and removes it while the layout runs, swapping the icon and the label
   at the same time (`▶ Resume layout` / `⏸ Stop layout`). The other two
   controls that carry it - Save Node Positions, Toggle tooltips - never change
   state at all; they were green because they happened to be written with the
   same class.

   A play/pause pair that already swaps both its glyph and its wording does not
   also need a hue, and paying for it here cost the toolbar its consistency. So
   the colour goes and the state stays: the flag still does its job in the click
   handler, and every control in the strip takes the one neutral treatment
   `.pa-net-tool` gives it in network-views.css. The class is now carried by the
   layout button alone - Save positions and Toggle tooltips never had a state
   for it to flag. */
div[id ^= "pathwayClassificationPlot2Box_"] {
	font-size: 15px;
	padding-top: 24px;
}



span.networkClusterImage{width: 30%;display: inline-block;margin: 3px; cursor: pointer;}
span.networkClusterImage img {width: 100%;}
span.networkClusterImage p {margin: 0;text-align: center; font-size: 10px;}
span.networkClusterImage > i {
	display: none;
	position: relative;
	bottom: -97px;
	margin-top: -97px;
	color: #DA643D;
	height: 97px;
	background-color: rgba(255, 255, 255, 0.54);
	line-height: 75px;
}
span.networkClusterImage.disabled > i {display:block;}
/* AI pathway clusters (network colouring from the AI report): one legend
   row per cluster instead of the metagene thumbnail grid. A hidden cluster
   dims and strikes through rather than showing the eye overlay, which sized
   itself for a 97px image. */
span.networkClusterImage.networkAICluster {display: block; width: auto; margin: 2px 0; padding: 2px 4px; border-radius: 3px;}
span.networkClusterImage.networkAICluster > i {display: none !important;}
span.networkClusterImage.networkAICluster p {text-align: left; font-size: 11px; line-height: 1.35;}
span.networkClusterImage.networkAICluster.disabled p {opacity: 0.45; text-decoration: line-through;}
span.networkClusterImage.networkAICluster:hover {background-color: rgba(0, 0, 0, 0.05);}
.mainInfoPanel {font-size: 12px;}
.pathwayPlotwrappers h4{color: #B14C2D;/* was #D16949 - 3.60:1 */font-size: 13px;margin: 0;margin-top: 5px;}
/* Vertical margin only. The horizontal 10px put the "Pathways network" card
   10px right of the "Pathways classification" card stacked directly above it,
   and of every other card on Step 3. It was already being zeroed further down
   this file - but only under `div.onedatabase`, so the fault was invisible on a
   single-database organism and present on every KEGG+Reactome job, which is
   what mmu and most of the example datasets produce. */
div[id ^= "networkview_"] { max-width:1800px; margin: 5px 0; }
div[id ^= "networkClustersContainer_"], div[id ^= "patwaysDetailsWrapper_"]{overflow-y:auto; overflow-x: hidden; padding: 2px 10px;}

/* The pathway explorer card ---------------------------------------------------
   One card per database, holding what used to be four: the classification
   panel, the network panel, and the network's "Tools" and "Details" panels. See
   the note in PA_Step3JobView.initComponent for why they are one activity.

   Nothing here draws a surface - the .contentbox the card also carries does
   that. These rules only place the two blocks inside it and give the fold
   control on the first one somewhere to sit. */
.paExploreCard > .paExploreOverview {
	/* Contains the two floated columns of the classification block, so that the
	   network below starts under them rather than beside them. */
	display: block;
}
.paExploreCard > .paExploreOverview::after,
.paExploreFoldable::after {
	content: "";
	display: block;
	clear: both;
}
/* The band that names the foldable block and carries its control. It is not an
   h3 with a floated link, because a float would take the link out of the row's
   height and the two would only line up by accident. */
.paExploreBand {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	padding-right: var(--pa-card-inset);
}
.paExploreBand > h3 {
	margin-bottom: 0;
}
a.paExploreFold {
	flex: 0 0 auto;
	font-size: 12px;
	font-weight: 600;
	color: var(--pa-ink-muted, #6B6B66);
	text-decoration: none;
	white-space: nowrap;
}
a.paExploreFold:hover {
	color: var(--pa-accent-orange, #AD5022);
	text-decoration: none;
}
a.paExploreFold i {
	margin-right: 5px;
}
/* jQuery's slideToggle writes `display` inline, so the folded state is not
   expressed here - only the clearfix above, which has to survive it. */
.paExploreFoldable {
	border-bottom: 1px solid var(--pa-border);
	padding-bottom: 8px;
	margin-bottom: 4px;
}

/* The pathways network and its rail ------------------------------------------
   Two floated 216px columns before, "Tools" and "Details", standing side by
   side and shown or hidden independently. Three things were wrong with that and
   all three are structural rather than cosmetic:

     - the two are never both being read. Details holds the colour legend and
       whatever pathway was last clicked; Tools holds every control. Showing
       them together spent 462px of a 1114px card on annotation and left the
       diagram it annotates with 662px;
     - a float cannot take its height from the element beside it, so the panels
       carried `height: 876px` as a literal and the resizer had to keep three
       heights in step by hand. It did not - see the note on that resizer;
     - "Configure" opened a *second* column rather than switching one, so how
       wide the graph was depended on which panels happened to be open.

   One grid: the graph in column 1 spanning both rows, the rail's tab strip and
   whichever pane is showing in column 2. The rail is one 236px column whatever
   is in it, the graph gets the rest, and neither has to be told a height. */
/* No gutter, and no margin on the area. The graph panel runs to the card's own
   left edge because the toolbar inside it derives its inset from
   --pa-card-inset - so that the first tool's icon lands on the same rail as the
   card's heading - and an inset on this container would charge that twice. The
   rail is separated by its own left border instead of by a gap, and docks
   against the card's right edge for the same reason: a bordered column floating
   10px inside a bordered card is two frames where the eye expects one. */
.paNetArea {
	margin: 0;
}
/* The grid is declared two elements below the container, and it has to be.
   ExtJS 4's auto layout renders a `display: table` span and a
   `display: table-cell` div between a container and its items - both generated,
   both carrying that display as an inline style, and neither reachable by the
   `cls` config that named .paNetArea. So the row is declared on the inner
   wrapper, addressed by the id ExtJS derives from the container's own, and the
   outer one is unwrapped to a block so it stops shrink-wrapping the row.

   `!important` beats the inline display and nothing else. Written without it
   the container measured 948px against a 1184px card and both panes sat at full
   width under the graph, which is the shape this replaced. */
.paNetArea > [id$="-outerCt"] {
	display: block !important;
	width: 100%;
}
.paNetArea > [id$="-outerCt"] > [id$="-innerCt"] {
	display: grid !important;
	grid-template-columns: minmax(0, 1fr) 236px;
	grid-template-rows: auto minmax(0, 1fr);
	gap: 0;
	align-items: stretch;
	width: 100%;
}
.paNetArea .paNetMain {
	grid-column: 1;
	grid-row: 1 / span 2;
	min-width: 0;
}
.paNetArea .paNetRailTabs {
	grid-column: 2;
	grid-row: 1;
}
.paNetArea .paNetRailPane {
	grid-column: 2;
	grid-row: 2;
	margin: 0;
	padding-bottom: 16px;
	overflow-y: auto;
	overflow-x: hidden;
	background: var(--pa-surface, #fff);
	border: none;
	border-left: 1px solid var(--pa-border);
	border-radius: 0;
}
/* With the rail hidden the graph takes the card. The canvas has to be told when
   that happens - a sigma renderer sizes itself once - which is why every caller
   of this class also calls resizeNetwork(). */
.paNetArea.is-railHidden > [id$="-outerCt"] > [id$="-innerCt"] {
	grid-template-columns: minmax(0, 1fr);
}
.paNetArea.is-railHidden .paNetRailTabs,
.paNetArea.is-railHidden .paNetRailPane {
	display: none !important;
}

/* Below the width where a 236px column and a graph can share a row, the rail
   goes under the graph instead of squeezing it. 900px is the breakpoint
   network-views.css already uses to shorten the canvas, so the two changes
   happen together rather than at two widths a reader would have to discover
   separately. */
@media (max-width: 900px) {
	.paNetArea > [id$="-outerCt"] > [id$="-innerCt"] {
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: auto auto auto;
	}
	.paNetArea .paNetMain {
		grid-column: 1;
		grid-row: 1;
	}
	.paNetArea .paNetRailTabs {
		grid-column: 1;
		grid-row: 2;
		border-left: none;
		border-top: 1px solid var(--pa-border);
	}
	.paNetArea .paNetRailPane {
		grid-column: 1;
		grid-row: 3;
		border-left: none;
		max-height: 420px;
	}
}

/* The rail's tabs. Segmented, sitting on top of the pane and sharing its
   border, so the strip and the pane below read as one object rather than as a
   control that happens to be above a box. */
.paNetRailTabs {
	display: flex;
	align-items: stretch;
	gap: 0;
	background: var(--pa-surface-sunken, #F5F4EF);
	border: none;
	border-left: 1px solid var(--pa-border);
	border-radius: 0;
	overflow: hidden;
}
a.paNetRailTab {
	flex: 1 1 0;
	min-width: 0;
	padding: 9px 6px;
	font-size: 12px;
	font-weight: 600;
	line-height: 1.2;
	text-align: center;
	color: var(--pa-ink-muted, #6B6B66);
	background: transparent;
	border: none;
	border-bottom: 1px solid var(--pa-border);
	text-decoration: none;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
a.paNetRailTab:hover {
	color: var(--pa-ink-title, #1A1A18);
	background: var(--pa-control-hover-bg, #FFFFFF);
	text-decoration: none;
}
/* The active tab loses the bottom border so it runs into the pane below it -
   the same join the database tabs make with the card. */
a.paNetRailTab.is-active {
	color: var(--pa-ink-title, #1A1A18);
	background: var(--pa-surface, #fff);
	border-bottom-color: transparent;
}
a.paNetRailTab i {
	margin-right: 5px;
	opacity: 0.75;
}
a.paNetRailHide {
	flex: 0 0 auto;
	padding: 9px 10px;
	font-size: 12px;
	color: var(--pa-ink-muted, #6B6B66);
	border-bottom: 1px solid var(--pa-border);
	text-decoration: none;
}
a.paNetRailHide:hover {
	color: var(--pa-accent-orange, #AD5022);
	background: var(--pa-control-hover-bg, #FFFFFF);
	text-decoration: none;
}

/* Full screen ----------------------------------------------------------------
   The class does the covering, not the Fullscreen API, so the panel is expanded
   even where the native request is refused - and the toolbar inside it, holding
   the button that comes back, is expanded with it. See setFullScreenNetwork in
   PA_Step3Views for why that is the whole point.

   `:fullscreen` is matched as well so that the same rules apply when the browser
   *does* grant the request and paints its own black backdrop behind the
   element. */
.paNetArea.paNetExpanded,
.paNetArea:fullscreen {
	/* Three `!important`s, all against inline styles ExtJS writes onto this
	   container itself: `position: relative` for the resizer that is pinned to
	   it, and a measured width and height from the layout run. Without the first
	   the class applies, the z-index resolves to 9500, and the panel does not
	   move - which is the state this looked like on the first attempt. */
	position: fixed !important;
	inset: 0;
	z-index: 9500;
	margin: 0;
	width: auto !important;
	height: auto !important;
	max-width: none !important;
	background: var(--pa-surface, #fff);
	overflow: hidden;
}
/* Both generated wrappers have to be given the height, or the grid inside them
   shrink-wraps its content and the graph stays 720px tall on a screen-high
   panel - which is what full screen looked like before. */
.paNetArea.paNetExpanded > [id$="-outerCt"],
.paNetArea:fullscreen > [id$="-outerCt"],
.paNetArea.paNetExpanded > [id$="-outerCt"] > [id$="-innerCt"],
.paNetArea:fullscreen > [id$="-outerCt"] > [id$="-innerCt"] {
	height: 100% !important;
}
.paNetArea.paNetExpanded .paNetMain,
.paNetArea:fullscreen .paNetMain {
	display: flex;
	flex-direction: column;
	min-height: 0;
}
/* The canvas takes the room the title, the toolbar and the status line leave. */
.paNetArea.paNetExpanded .pa-net-canvas,
.paNetArea:fullscreen .pa-net-canvas {
	flex: 1 1 auto;
	height: auto;
	min-height: 0;
}
/* The resize handle is for a panel sitting in a page. Full screen has no page
   under it to make room in. */
.paNetArea.paNetExpanded > .x-resizable-handle,
.paNetArea:fullscreen > .x-resizable-handle {
	display: none;
}

/* Classification / database badge. The fill and the letter colour are set
   inline by classificationBadgeStyle() in Util.js - see the comment there for
   why the palette colour is the chip fill rather than the ink.

   The inset ring gives the chip an edge on white. It only does visible work for
   the pale end of the palette (#ffff00, #deeaee, #99ffcc and friends), which
   would otherwise dissolve into the page; on the dark end it is imperceptible,
   which is why one flat rgba value covers all 36 colours. */
/* The page column ------------------------------------------------------------
   Centring `.contentbox` was not enough: Step 3 - the results page - builds its
   analyses inside plain ExtJS containers rather than contentboxes, and those
   measured 1847px at a 1867px viewport. Full bleed, no margin, exactly the
   "uses all the space and leaves no space to the side" complaint.

   The column is enforced here instead, on the centre panel, because that is the
   one ancestor every step shares. Padding rather than max-width: ExtJS writes
   the inner container's width itself and ignores a max-width on it, but it
   measures the space *inside* the panel's padding, so padding is the one lever
   that the layout engine actually respects. Verified - it reflowed the Step 3
   containers from 1847px to 1547px, centred.

   `.contentbox` no longer caps itself, so surfaces fill the column and every
   block on the page shares one left and right edge. */
#mainViewCenterPanel {
	padding-left: var(--pa-gutter);
	padding-right: var(--pa-gutter);
	box-sizing: border-box;
	/* Stated here rather than left to the ExtJS theme's grey. This is the one
	   ancestor every step shares, so it is the only place the page colour can
	   be set once. dark.css already claims this selector with !important, and
	   is loaded after this file, so the dark page still wins.

	   !important because ExtJS writes `background-color: rgb(243,243,243)`
	   into this element's own style attribute from JavaScript - which is also
	   the reason the dark rule needs it. A stylesheet cannot outrank an inline
	   style by being more specific, only by being important. */
	background-color: var(--pa-surface-page) !important;
}
div.contentbox,
.contentbox,
.omicbox,
.po-hero-section {
	max-width: none;
}
/* Running text does not want the full page column even if the tables do. */
div.contentbox p,
div.contentbox li {
	max-width: var(--pa-measure);
}

i.classificationNameBox {
	margin-right: 10px;
	border-radius: 50%;
	line-height: 24px;
	display: inline-block;
	border: 2px solid;
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.22);
	font-style: normal;
	width: 24px;
	height: 24px;
	text-align: center;
	font-weight: bold;
	margin-bottom: 3px;
	/* An inline-block is a block container, so it inherits `text-indent` and
	   applies it to its own first line - and its own first line is the single
	   letter this badge exists to centre. The hanging indent added below for the
	   legend rows promptly pulled every letter 34px out of its circle and left a
	   row of empty discs with a stray capital beside each. The badge's content is
	   positioned by `text-align: center` and by nothing else. */
	text-indent: 0;
}
/* The network's Details legend, one row per classification: a round badge and
   the classification's name beside it. The badge is inline and the name is a
   bare text node after it, so a name too long for the column wrapped back to
   the row's left edge and its second line ran underneath the badge - "Genetic
   Information / Processing" and "Environmental Information / Processing" both
   did, in a panel 224px wide where they always will. The badge column and the
   label column are then not columns at all.

   Same hanging indent as the checkbox labels, and the same reasoning: the badge
   keeps its place in the first line's flow, and every line starts past it.
   34px is the badge (24px, border-box) and its 10px gap, from the rule above. */
[id^="networkClustersContainer_"] > div > div {
	padding-left: 34px;
	text-indent: -34px;
}
div[id ^= "pathwayClassificationContainer"] {overflow-y: auto; overflow-x: hidden;}
.step3ClassificationsWrapper{display: block;}
.step3ClassificationsChildrenContainer{display: none; margin-left: 30px;border-left: 1px dotted #B1B1B1;padding-left: 10px;}
.step3ClassificationsTitle{cursor: pointer;}
.step3ClassificationsTitle.disabled {color: #BBBBBB;text-decoration: line-through;}
/* Disabled badge: a flat grey chip, matching the filled shape of the enabled
   one. The old rule set only the ink and the border, which on a filled chip
   left the palette colour showing through. #595959 on #E8E8E8 is 5.72:1, so the
   letter stays readable while the chip clearly reads as switched off. */
.step3ClassificationsTitle.disabled >  i.classificationNameBox{color: #595959 !important; background-color: #E8E8E8 !important; border-color: #E8E8E8 !important;}
.step3ClassificationsOptions{display: none; margin-left: 10px;font-size: 11px;}
.step3ClassificationsTitle:hover > .step3ClassificationsOptions{display: inline-block;}
.step3ClassificationsOptions a {margin: 0px 2px;}
.step3ClassificationsOptions a {color: #A7A7A7;margin: 0px 2px; display: inline-block;}
.step3ClassificationsOptions a.hideOption.selected, .step3ClassificationsOptions a.hideOption:hover{color: #E3643D;}
.step3ClassificationsOptions a.showOption.selected, .step3ClassificationsOptions a.showOption:hover{color: #23AE89;}
.step3ClassificationsOptions a.customOption.selected, .step3ClassificationsOptions a.customOption:hover{color: #BD7E39;}
.step3ClassificationsOptions a.selected{float: left;font-weight: bold;}
/* The pathways network toolbar ------------------------------------------------
   The band itself, its status line and the shape of its controls are now
   described once in network-views.css, together with the MORE network's
   toolbar. What survives here is what only this bar has: the three hover
   menus, the reorder popover, and the transient "Saved" / "Tooltips enabled"
   message that floats over the diagram.

   Three things went, and they are worth naming because each of them was
   visible:

   `margin-bottom: -20px` pulled the strip 20px down over the canvas, so the
   toolbar and the top of the diagram overlapped. `float: left !important` on
   every `a` in the bar beat the inline `float: right` that Save Node Positions,
   Toggle tooltips and the layout button each carried - which is why a bar
   written as two groups rendered as one undifferentiated row. And the clearfix
   existed only to contain those floats; a flex container contains its own
   children.

   The download links for PNG and SVG were not in this bar at all. They were in
   a `.lateralOptionsPanel-toolbar` floated above the card's own `<h2>`, so the
   two controls that act on the network sat outside the network's chrome and on
   top of its title. They are in the band now, in a group of their own. */
p[id ^= "step3-network-toolbar-message_"]{
	display: none;
	position: absolute;
	left: 50%;
	top: 84px;
	transform: translateX(-50%);
	z-index: 4;
	padding: 9px 18px;
	margin: 0;
	font-size: 14px;
	line-height: 1.3;
	text-align: center;
	white-space: nowrap;
	color: var(--pa-accent-blue);
	background: var(--pa-surface, #FFFFFF);
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	box-shadow: var(--pa-shadow);
}
p[id ^= "step3-network-toolbar-message_"].successMessage{
	color: var(--pa-accent-green);
	border-color: var(--pa-accent-green);
}
div[id ^= "step3-network-toolbar-message_"].successMessage{color: #12A22F !important;border: 1px solid #12A22F !important;}

div[id ^= "step3-network-toolbar_"] div.menu { position: relative; }
div[id ^= "step3-network-toolbar_"] div.menuBody {
	position: absolute;
	left: 0;
	top: calc(100% + 4px);
	width: 205px;
	padding: 4px;
	display: none;
	z-index: 5;
	background-color: var(--pa-surface, #FFFFFF);
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	box-shadow: var(--pa-shadow);
}
/* Menu items are full-width rows rather than inline chips: a dropdown whose
   entries are centred pills reads as a toolbar that escaped its bar. */
div[id ^= "step3-network-toolbar_"] div.menuBody .pa-net-tool {
	display: flex;
	width: 100%;
	justify-content: flex-start;
}

div[id ^= "step3-network-toolbar_"] div[id ^= "reorderOptions"]{
		position: absolute; width: 200px !important;
		left: 10px; top: calc(100% + 4px);
	 	height: 80px !important;
	 	border: 1px solid var(--pa-border) !important;
	 	border-radius: var(--pa-radius) !important;
	 	background: var(--pa-surface, #FFFFFF) !important;
		box-shadow: var(--pa-shadow);
		z-index: 5;
		min-height: auto !important;
 }

/* The pathway details panel (Step 3 details card, Step 4 sidebar) ------------
   Replaces the blue zebra table (#e2eff8 on even rows) and the bulleted
   classification list. Zebra striping earned nothing here - three rows do not
   need tracking help - and once the hidden per-condition rows toggle open the
   stripes landed on arbitrary rows anyway. Hairline separators carry the
   structure; numbers right-align in tabular figures so magnitudes line up. */
.pa-details-label {
	display: block;
	font-size: 10px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	color: var(--pa-ink-muted, #595959);
	margin: 6px 0 4px;
}
.pa-details-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 4px;
}
.pa-details-chip {
	font-size: 11px;
	line-height: 1.4;
	padding: 2px 9px;
	border-radius: 999px;
	background: var(--pa-surface-quiet, #F3F2ED);
	border: 1px solid var(--pa-border, rgba(24, 24, 27, 0.10));
	color: var(--pa-ink-control, #3F3F46);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 100%;
}
.pa-details-table {
	width: 100%;
	border-collapse: collapse;
	margin: 8px 0 4px;
	font-size: 12px;
}
.pa-details-table th {
	font-size: 10px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pa-ink-muted, #595959);
	text-align: right;
	padding: 3px 6px;
	border-bottom: 1px solid var(--pa-border, rgba(24, 24, 27, 0.10));
}
.pa-details-table th:first-child {
	text-align: left;
}
.pa-details-table td {
	padding: 5px 6px;
	border-bottom: 1px solid var(--pa-border, rgba(24, 24, 27, 0.10));
	vertical-align: middle;
}
.pa-details-table tbody tr:last-child td {
	border-bottom: none;
}
.pa-details-omic {
	text-align: left;
	font-weight: 600;
	color: var(--pa-ink, #18181B);
}
.pa-details-num {
	text-align: right;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
	color: var(--pa-ink-control, #3F3F46);
}
.pa-details-actions {
	text-align: right;
	white-space: nowrap;
}
.pa-details-actions i {
	cursor: pointer;
	padding: 4px 5px;
	border-radius: var(--pa-radius-sm, 6px);
	color: var(--pa-ink-muted, #595959);
}
.pa-details-actions i:hover {
	background: var(--pa-surface-quiet, #F3F2ED);
	color: var(--pa-ink, #18181B);
}
.pa-details-table tr.condition-row td {
	border-bottom: none;
	background: var(--pa-surface-subtle, #F9F8F4);
	font-size: 11px;
	color: var(--pa-ink-muted, #595959);
}
.pa-details-table tr.condition-row td.pa-details-cond {
	padding-left: 16px;
	font-style: italic;
}
.pa-details-matchedlist h5 {
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--pa-ink-muted, #595959);
	margin: 8px 0 2px;
}
.pa-details-matchedlist ul {
	margin: 0 0 8px;
	padding-left: 18px;
	font-size: 12px;
}

/* The database tabs -----------------------------------------------------------
   KEGG / Reactome / MapMan, above the classification, network and enrichment
   blocks in Step 3. They select which database the three panels below describe,
   which makes them the most consequential control on the results page - and
   they were the most confusing thing on it.

   Each label was 23px semibold terracotta, i.e. dressed exactly as `h2`, the
   card-title style used by "Pathways selection", "Multiple databases used" and
   the five headings around them. So the tabs read as one more section heading
   in a page full of them, and the only sign that "KEGG" was a *state* and
   "Reactome" a *thing you could press* was a 1px silver line under one of them.
   Two 23px orange words with a hairline between them is not a tab bar.

   Rebuilt first as one: 14px labels, the selected one near-black with a 2px
   accent rule beneath it, the rest muted and colouring on hover. That is
   smaller than the headings it sits above, which is correct - a tab is a
   control, and the thing it reveals is what the reader came for.

   That pass fixed what the labels said and left untouched what they *were*: a
   word with no box around it. Measured, in this state: the KEGG tab's <a> was
   33px wide and 38 tall - i.e. exactly as wide as the four letters, because the
   only rule holding any horizontal padding named a `button` element (see the
   note on .x-tab-inner below) that this ExtJS build does not render, so it
   never applied to anything. Two bare words and a hairline still read as a
   caption above the card, not as the switch between two databases; the whole
   affordance rested on one of them being darker than the other.

   So the labels get their boxes. Each database is a pill - a fill, a hairline,
   a radius and a 18px hit area either side of the word - and the selected one
   is filled near-black with a white label rather than merely being a darker
   grey than its neighbour. The difference between "KEGG" and "Reactome" is now
   figure/ground rather than two shades of ink, which is what a control that
   changes the whole page below it should look like at a glance.

   The gap between the tabs is set in JS, not here: see the note on
   `defaults.margin` in PA_Step3Views.js. It was 22px, which was the distance
   two undressed words needed to stop reading as one phrase; boxes carry their
   own separation, and 22px between them made the pair read as two unrelated
   buttons rather than as one control with two positions.

   Last pass: bookmarks, not pills.

   The pills were correct about weight and wrong about attachment. They floated
   19px above the card they switch, so what the reader saw was two buttons and,
   separately, a card - and nothing on the page said that pressing one of the
   buttons was what put that card there. A pill is a button; a button's business
   ends when you release it. What this control does is put a *different sheet*
   in front of you, which is the one thing a bookmark tab says and a pill
   cannot: the selected tab and the sheet are one surface, joined along an edge,
   and the unselected one is another sheet behind it.

   So: top-rounded tabs sitting directly on the card, the selected one filled in
   the card's own surface and continuing into it across an unbroken seam, the
   rest sunken and behind. The card gives up its top corners to receive them -
   see the rule on .paExploreCard below - which is the price of the join and the
   reason it reads as one object.

   The `!important`s below match Neptune's own on the same properties; the
   heights it hard-codes are what the strip and the tab boxes are measured from,
   so they are restated rather than removed. */
div#tabcontainer_network,
div#tabcontainer_network .x-panel-body-default {
	border-color: transparent;
}
/* The bar's own hairline would run behind the tabs and out past the last one,
   which is the one line a bookmark strip must not have: the edge belongs to the
   sheet below, and the selected tab's job is to break it. */
div#tabcontainer_network .x-tab-bar {
	border-bottom: 0;
	margin-bottom: 0;
}
/* One position of the control.

   `margin-right` is not a typo for the gap and is deliberately absent: a tab bar
   is an ExtJS box layout, which measures each tab and writes an absolute `left`
   for the next one, so a margin from this stylesheet is both overwritten inline
   and ignored by the arithmetic. The gap is a component config; this file cannot
   set it.

   No bottom border, and the radius only on the top two corners: a tab is open at
   the edge it shares with its sheet. */
div#tabcontainer_network .x-tab {
	background: var(--pa-surface-sunken, #F5F4EF);
	border: 1px solid var(--pa-border) !important;
	border-bottom: 0 !important;
	border-radius: var(--pa-radius) var(--pa-radius) 0 0;
	padding: 0 20px;
	height: 36px !important;
	transition: background-color var(--pa-transition),
	            border-color var(--pa-transition);
}
/* The label. This rule also absorbs what a `.x-tab button` selector further
   down used to declare - `line-height: 36px !important; font-size: 14px;
   padding: 0 14px` - and which never once applied: ExtJS 4.2.1 renders a tab as
   `a.x-tab > span.x-tab-wrap > span.x-tab-button > span.x-tab-inner`, with no
   `button` element anywhere in it. That dead rule is why the tabs had no
   padding, and its 36px line-height is restated here, on the element that
   exists: 38px of pill less its two 1px borders is a 36px content box, so the
   label sits on the pill's centre line. */
div#tabcontainer_network .x-tab .x-tab-inner {
	color: var(--pa-ink-muted);
	font-family: var(--pa-font-sans) !important;
	font-size: 14px;
	font-weight: 600 !important;
	height: auto;
	line-height: 35px;
	transition: color var(--pa-transition);
}
/* `:not(.x-active)` rather than letting source order settle it. The two
   selectors score the same specificity, so the active pill would take the hover
   background from whichever rule came last - and the point of the selected
   state below is that it does not change under the cursor. */
div#tabcontainer_network .x-tab:not(.x-active):hover {
	background: var(--pa-control-hover-bg);
	border-color: rgba(24, 24, 27, 0.20) !important;
}
div#tabcontainer_network .x-tab:not(.x-active):hover .x-tab-inner {
	color: #27272A;
}
/* The selected state: the tab is the card.

   Same fill, same border, and a 1px shadow cast straight down that paints over
   the card's own top border for exactly the width of the tab - which is the
   seam. Without that one line the card's edge runs unbroken beneath every tab
   and the selected one is merely a white box sitting on a box, which is what a
   pill was.

   No accent and no knocked-out label: the accents on this page encode data (the
   significance tints, the database badges, the omic colours), and a control that
   borrowed one would be claiming a meaning it does not have. Selection is
   carried by the join, and by the label going from muted to full ink. */
div#tabcontainer_network .x-tab.x-active {
	background: var(--pa-surface, #FFFFFF);
	border-color: var(--pa-border) !important;
	position: relative;
	z-index: 3;
	box-shadow: 0 1px 0 0 var(--pa-surface, #FFFFFF);
}
div#tabcontainer_network .x-tab.x-active .x-tab-inner {
	color: var(--pa-ink-title, #1A1A18);
}
/* The seam is a 1px overhang, so nothing between the tab and the card may clip
   it. Neptune gives all three of these `overflow: hidden`. */
div#tabcontainer_network .x-tab-bar,
div#tabcontainer_network .x-tab-bar-body,
div#tabcontainer_network .x-tab-bar .x-box-inner {
	overflow: visible !important;
}
/* The card receives the tabs. Square across the top, and hard against the bar
   above it: a rounded corner under a square-bottomed tab is the one detail that
   gives away that these are two objects rather than one sheet. The bottom
   corners keep the radius - that edge is the card's own.

   A descendant selector, because ExtJS puts four generated wrappers between the
   tab's panel and the card - a body, an outerCt, an innerCt, and the card's own
   container - none of which a child combinator can be written against. */
#tabcontainer_network .x-tabpanel-child .paExploreCard {
	margin-top: 0;
	border-top-left-radius: 0;
	border-top-right-radius: 0;
}
/* Unless there are no tabs. A single-database organism hides the bar entirely
   (see `tabBar.hidden` in PA_Step3Views), and a card squared off to receive
   tabs that are not there is just a card with two corners missing - on the one
   configuration where nobody can see why. */
#tabcontainer_network.onedatabase .x-tabpanel-child .paExploreCard {
	margin-top: 5px;
	border-top-left-radius: var(--pa-radius);
	border-top-right-radius: var(--pa-radius);
}
/* Neptune's 1px filler strip under the bar. Nothing draws a line there now, and
   this would be the only one - 40px below the pills and attached to neither
   them nor the card. */
div#tabcontainer_network .x-tab-bar-strip {
	display: none;
}
div#tabcontainer_network .x-tab-bar .x-tab-bar-body {
	height: 36px !important;
	border: 0 !important;
	padding: 0;
}
div#tabcontainer_network .x-tab-bar .x-tab-bar-body .x-box-inner {
	height: 36px !important;
}
div#tabcontainer_network .x-tab-center {
	margin-top: 0;
}
/* The per-database split, inside the summary band.

   This table used to be the centrepiece of its own card, "Multiple databases
   used" - two paragraphs of prose over a bordered, filled table. It sits in
   the band's third cell now (see "The summary band" further down), where the
   band's hairline separators already frame it, so a border and a fill of its
   own would draw a box inside a box. Bare rows: tiny muted headers, the badge,
   the name, two columns of lining figures. The ids on the count cells are
   still what the pathway filters refresh, so the markup is untouched. */
div#multisource_summary table {
	border-collapse: collapse;
}
div#multisource_summary table th {
	padding: 0 12px 5px;
	color: var(--pa-ink-muted, #595959);
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	text-align: left;
	white-space: nowrap;
}
div#multisource_summary table td {
	padding: 4px 12px;
	font-size: 13px;
	color: var(--pa-ink-body, #27272A);
}
/* Counts read as a column of magnitudes, so they are right-aligned on lining
   figures - "368" over "520" should line up digit for digit. */
div#multisource_summary table th:nth-child(n+3),
div#multisource_summary table td:nth-child(n+3) {
	text-align: right;
	font-variant-numeric: tabular-nums;
}
/* The badge column carries no header and no label, only the coloured initial. */
div#multisource_summary th.db_chip,
div#multisource_summary td.db_chip {
	width: 1px;                /* shrink to the chip */
	padding-left: 0;
	padding-right: 0;
}
div#multisource_summary td.db_name {
	text-align: left;
	font-weight: 600;
	white-space: nowrap;
}
div#multisource_summary td.db_significant {
	color: var(--pa-ink-title, #1a1a1a);
	font-weight: 600;
}

div.onedatabase .x-panel-body-default {
	border: none !important;
}

div.onedatabase .contentbox {
	margin-left: 0 !important;
	margin-right: 0 !important;
}

div.onedatabase div[id ^= "networkview_"] {
	margin-left: 0 !important;
	margin-right: 0 !important;
}

/* The tab panel is a grouping, not a surface: the cards inside it draw their
   own borders. Its body border added a pixel to the left of everything in the
   KEGG and Reactome tabs, so "Pathways classification" and "Pathways network"
   started 1px right of the four cards outside the tabs - a hairline stagger
   down the page. Removed for one database already, via the rules above; the
   side that had two databases is the side a reader normally sees.

   All four sides now, not just the two. There are two of these bodies nested,
   each contributing a 1px top border, and together they held the card 2px below
   the tab bar - which is 2px the selected tab's seam cannot reach across, and
   the difference between a bookmark tab and a button sitting near a card.
   Measured: tab bar bottom 127.9, card top 129.9. */
#tabcontainer_network > .x-panel-body,
#tabcontainer_network .x-tabpanel-child > .x-panel-body {
	border-width: 0 !important;
}

/********************************************************************************
**STYLES FOR STEP 4
*********************************************************************************/
.x-tab-bar-strip-default-top {
	height: 1px;
}
i.relevantFeature:before {
	content: "\f005";
	font: normal normal normal 9px/1 FontAwesome;
	color: red;
	padding-left: 5px;
}
i.relevantAssociationFeature:before {
	content: "\f005";
	font: normal normal normal 9px/1 FontAwesome;
	color: #f4c800;
	padding-left: 5px;
}
i.masterRegulator:before {
	content: "\f024";
	font: normal normal normal 9px/1 FontAwesome;
	color: #53af93;
	padding-left: 5px;
}
.x-tab {
	background: none;
}
.x-tab-default .x-tab-inner {
	color: #333;
	/* Was 300. A tab label is 13px UI text, not a display figure, and 300 now
	   selects a real Light face rather than resolving to the only face there
	   was. This is the one place the 300s were not a deliberate treatment. */
	font-weight: 400;
}
.x-tab.x-active {
	border-width: 1px !important;
	border: 1px solid #ADD2ED;
	border-radius: 0px;
	border-top: solid 3px #539EF0 !important;
	border-bottom: solid #fff;
	padding-top: 4px;
}
.externalLinksContainer a {
	color: var(--pa-link); /* was #0076E2 - 4.48:1, and bypassed the token */
}
.step4PathwayBox {
	display: inline-table;
	width: 300px;
	text-align: center;
	border: solid 1px #9E9E9E;
	border-radius: 5px;
	box-shadow: 0px 0px 15px #B8B8B8;
	margin: 10px 15px;
	min-height: 375px;
}
.searchTrigger {
	background-color: rgb(12, 152, 230);
	width: 40px;
	background-image: url("../images/search_16x16.png");
	background-repeat: no-repeat;
	height: 30px;
	background-position: center !important;
}

.otherFeaturesLabel {
	margin: 10px 0;
}

.boxTitleLabel {
	font-weight: bold;
	padding-top: 5px;
	padding-bottom: 8px;
	border-bottom: 1px solid var(--pa-border);
}

/*PATHWAYS THUMBNAILS FOR HISTORY PANEL*/

/* This panel slides over the pathway diagram, so it has to read as something
   laid on top of the page: a flat #EEE rectangle with a hard grey drop shadow
   and no border read as part of the diagram that had gone wrong. Now a white
   card with the same border, radius and heading as every other card here, and
   a shadow with enough spread to sit above the artwork.

   Hiding used to mean `left: -405px`, a magic offset that assumed the panel's
   offset parent sat at the left edge of the window. It does not: the toolbar it
   is anchored to starts ~680px in, so -405px slid the panel sideways and left
   all of it on screen, on top of the pathway title. Closing it looked like it
   had done nothing. Visibility is a state now, not a coordinate, so it holds
   wherever the panel happens to be anchored. */
.step4HistoryBox {
	max-width: 360px;
	width: 360px;
	position: absolute;
	padding: 0;
	top: 55px;
	left: 0;
	z-index: 23;
	background-color: #FFFFFF;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
	box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
	overflow: hidden;
	opacity: 0;
	visibility: hidden;
	transform: translateY(-8px);
	/* visibility flips only after the fade finishes, so the panel is not
	   click-through mid-animation and does not vanish before it has faded. */
	transition: opacity .18s ease, transform .18s ease, visibility 0s linear .18s;
}
.step4HistoryBox.step4HistoryBoxOpen {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
	transition: opacity .18s ease, transform .18s ease, visibility 0s;
}
/* Eight thumbnails is taller than most viewports over a diagram, so the list
   scrolls inside the card rather than running off the bottom of the screen. */
.step4HistoryBox > div {
	max-height: 58vh;
	overflow-y: auto;
	padding: 10px;
}
.step4HistoryBox h2 {
	text-align: left;
	position: relative;
	margin: 0;
	padding: 12px 46px 12px 16px;
	background-color: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
	border-radius: 0;
	color: #27272A;
	font-size: 14px;
	font-weight: 600;
}
/* Scoped to this panel: the same three classes dress the pathway thumbnails in
   Step 3's network tooltip, which is laid out differently and must not move. */
.step4HistoryBox .step4ThumbContainer {
	position: relative;
	display: flex;
	flex-direction: column;
	width: auto;
	margin: 0 0 8px;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	overflow: hidden;
	background-color: #FFFFFF;
	cursor: pointer;
	transition: border-color .12s ease, box-shadow .12s ease;
}
.step4HistoryBox .step4ThumbContainer:last-child { margin-bottom: 0; }
.step4HistoryBox .step4ThumbContainer:hover {
	border-color: #A1A1AA;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10);
}
.step4HistoryBox .step4ThumbWrapper {
	order: 1;
	height: 120px;
	background-position: center;
}
/* The name was a near-black panel at 81% opacity, 60px tall, sitting over the
   image it was labelling - the thumbnail was mostly hidden behind its own
   caption. It reads as a caption under the image instead. The source order puts
   it first, so `order` moves it without touching the shared markup. */
.step4HistoryBox .step4ThumbTitleContainer {
	order: 2;
	float: none;
	width: auto;
	min-height: 0;
	padding: 9px 12px;
	background-color: #FFFFFF;
	border-top: 1px solid var(--pa-border);
	color: #27272A;
	font-size: 12.5px;
	font-weight: 500;
	line-height: 1.35;
	text-align: left;
}
/* This was absolutely positioned with no positioned ancestor, so it laid itself
   out against the panel rather than against its thumbnail: "Open" appeared as
   30px grey text floating over the middle of the box, attached to nothing. */
.step4HistoryBox .step4PathwayThumbnailHover {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 120px;
	width: auto;
	padding-top: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: rgba(255, 255, 255, 0.82);
	color: #27272A;
	font-size: 14px;
	font-weight: 600;
	z-index: 1;
}
/* The panel is an overlay across the pathway, so it needs the close control
   every other overlay here has - it was dismissed only by pressing the toolbar
   button underneath it a second time. */
.step4HistoryClose {
	position: absolute;
	top: 50%;
	right: 8px;
	transform: translateY(-50%);
	width: 26px;
	height: 26px;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--pa-radius-btn);
	color: #71717A;
	font-size: 14px;
	text-decoration: none;
	transition: background-color .12s ease, color .12s ease;
}
.step4HistoryClose:hover {
	background-color: rgba(0, 0, 0, 0.07);
	color: #27272A;
	text-decoration: none;
}
.step4ThumbContainer {
	margin: 5px;
	border: 1px solid #d3d3d3;
	width: 150px;
	display: inline-block;
}
.step4ThumbWrapper, .step3ThumbWrapper, .step3ChartWrapper {
	height: 125px;
	background-repeat: no-repeat;
	background-size: cover;
}
.step4ThumbTitleContainer {
	float: left;
	width: 100%;
	background-color: rgba(14, 13, 13, 0.81);
	color: #FDFCFC;
	font-size: 14px;
	padding: 10px;
	min-height: 60px;
	text-align: center;
}
.step4PathwayThumbnailHover {
	position: absolute;
	height: 125px;
	width: 150px;
	background-color: rgba(255, 255, 255, 0.67);
	font-size: 30px;
	color: rgb(60, 72, 75);
	text-align: center;
	padding-top: 50px;
	opacity: 0;
	transition: opacity .25s ease-in-out;
	-moz-transition: opacity .25s ease-in-out;
	-webkit-transition: opacity .25s ease-in-out;
	cursor: pointer
}
.step4PathwayThumbnailHover:hover {
	opacity: 1;
}

div[id^=matching_table] table {
	margin: 5px auto;
	border: 1px solid black;
	text-align: center;
	padding: 5px 10px;
}

/**REUSING THUMBNAILS AT STEP3*/

.sigma-tooltip .step4ThumbContainer {
	margin: 0;
	width: 100%;
}
.sigma-tooltip .step4PathwayThumbnailHover {
	width: 100%;
	height: 100%;
	padding-top: 80px;
}
.sigma-tooltip .step4ThumbTitleContainer {
	min-height: inherit;
	float: none;
}

.step4PathwaysTable {
	text-align: center;
}
.step4PathwayBox:hover {
	cursor: pointer;
}
.step4PathwayBox h4 {
	line-height: 25px;
	margin: 5px 0px;
}
.visualSettingsItem {
	margin: 10px 5px;
	display: inline-block;
	color: #4D4D4D;
	font-size: 18px;
	font-size: 18px;
}
.visualSettingsItem.omicSelector {
	color: #BA1925;
}
.visualSettingsItem.selected {
	color: #3C8E49;
}
.visualSettingsItem.colorByCheckbox, .visualSettingsItem.colorScaleCheckbox {
	font-size: 15px;
	display: block;
	margin-left: 10px;
}
/* Was an <img> whose intrinsic 120x18 supplied the width. It is a <span> with a
   live gradient now (paScaleThumb), so the box has to be stated. */
.colorScaleThumb {
	display: inline-block;
	width: 120px;
	height: 12px;
	margin-right: 20px;
	border-radius: 2px;
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .10);
	float: right;
}
.visualSettingsButton {
	background-color: #0076D6; /* was #0079dc - white label at 4.40:1 */
	display: inline-block;
	cursor: pointer;
	color: #ffffff;
	font-family: var(--pa-font-sans);
	font-size: 14px;
	padding: 5px 10px;
	text-decoration: none;
	text-shadow: 0px 1px 0px #0b7ed6;
}
.visualSettingsButton:hover {
	color: white;
	background-color: #2788D7;
}
.tooltipInputName {
	font-size: 9px;
	display: block;
	color: #93A39D;
}
.tooltipInputName.yAxisLabel {
	text-align: right;
}

/*LATERAL PANEL IN PATHWAYS */

.lateralOptionsPanel {
	margin: 15px 0px 15px 10px;
	background: #fff;
	overflow-y: hidden;
	/* Was min-height: 845px, which propped the panel open to a fixed height
	   whatever it contained. Its own height config already sizes it. */
}
/* The 10px above is the gutter *between* panels, and on the pathway view the
   leftmost panel has no panel to its left to be gutted from. Its wrapper already
   carries the page inset (`padding: 5px 10px`), so the pathway image started
   20px inside the column while the information panel beside it stopped 10px
   short of the other edge - a 10px asymmetry that puts the whole of Step 4 off
   centre, which is the one thing a full-width two-panel layout cannot survive.

   Scoped to the wrapper rather than written as a bare `:first-child`: Step 3's
   two network side panels carry the same class and are `float: right`, so the
   first of them in document order is the *rightmost* on screen, and resetting
   its left margin there would close the gutter between Details and Tools
   instead of the one it looks like it is closing. */
#pathwaysPanelsWrapper .lateralOptionsPanel:first-child {
	margin-left: 0;
}
.lateralOptionsPanel-body {
	/* 16px horizontally, matching the header above it - see the note on the
	   left rail further down this file. */
	padding: 12px 16px 35px;
	/*max-height: 98%;*/
	overflow-y: auto;
}
/* Step 4's two side panels carried their colour inline: #337ab7 for Pathway
   information and #D43E3A for Visual settings - a Bootstrap blue and a red, the
   only two panel headers in the application not using the card treatment, and
   red on a panel that only changes colour scales reads as a warning it is not.
   Both now match every other heading here. */
.lateralOptionsPanel-header {
	padding: 12px 16px;
	background: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
}
/* One left rail for a side panel -------------------------------------------
   These are 260-300px columns, not cards, and they were carrying two insets at
   once: the title took --pa-card-inset (26px) from the card rules, while the
   controls under it took the 10px their own containers declare. Measured on
   the pathway view, "Visual settings" started at x=1101 and the first thing it
   titles at x=1085 - a 16px step between a heading and its own contents, in a
   panel narrow enough that 26px is a twelfth of its width.

   16px for both. The card inset is sized for a 1400px column; this is a
   sidebar, and giving 52px of a 300px panel to margins would cost the sliders
   and the p-value radios the room they need. */
div.lateralOptionsPanel > h2,
div[id ^= "pathwayNetworkToolsBox_"],
div[id ^= "networkClustersContainer_"],
div[id ^= "patwaysDetailsWrapper_"],
div[id ^= "sliderClusterNumberContainer_"] {
	padding-left: 16px;
	padding-right: 16px;
}
/* ...and the panel's one action reaches that same rail.

   `.button` carries `margin: 0 5px`, which is the gutter between two buttons
   sitting side by side. These two are alone on their line - "Apply" at the foot
   of the network Tools panel, "Search" at the top of the pathway information
   panel - so the gutter has nothing to separate them from and simply stops each
   one 5px short of the edge every heading and every field in the same panel is
   squared to. Five pixels is small enough to read as sloppiness rather than as
   spacing, which is the worst size for it to be. */
div[id ^= "pathwayNetworkToolsBox_"] > a.button.btn-right,
.findFeaturesContainer a.button {
	margin-right: 0;
}
/* The search field and its button share one flex row. `.button` floats right,
   which is what used to push "Search" onto its own line below the field; a
   flex container ignores floats on its items, so declaring the row is also
   what cancels the float. The input stretches, the button keeps its size, and
   both are as tall as each other - align-items: stretch - so the pair reads
   as one control squared to the panel rail. */
.findFeaturesRow {
	display: flex;
	align-items: stretch;
	gap: 6px;
	margin: 6px 0 10px;
}
.findFeaturesRow .findFeaturesInput {
	flex: 1;
	min-width: 0;
}
.findFeaturesRow .findFeaturesInput input {
	width: 100%;
	height: 100%;
	box-sizing: border-box;
	margin: 0;
	padding: 6px 10px;
	font-family: var(--pa-font-sans);
	font-size: 13px;
	color: var(--pa-ink, #27272A);
	background: var(--pa-surface, #FFFFFF);
	border: 1px solid var(--pa-border, #D4D4D8);
	border-radius: var(--pa-radius-sm, 6px);
}
.findFeaturesRow .findFeaturesInput input:focus {
	outline: none;
	border-color: var(--pa-focus-ring, #0A84FF);
}
.findFeaturesRow a.button.findFeatureButton {
	float: none;
	margin: 0;
	display: flex;
	align-items: center;
	gap: 5px;
}
.lateralOptionsPanel-header h2 {
	color: #27272A;
	font-size: 15px;
	font-weight: 600;
	margin: 0;
}
.lateralOptionsPanel-toolbar {
	margin-top: -5px;
	float: right;
}
/* The same class is used in two places with opposite geometry, and the negative
   top margin above is written for one of them.

   Inside a `.lateralOptionsPanel-header` - Step 4's four panels - the toolbar is
   in a band that already pads it, and -5px only trims that padding back.

   As a *direct child of the panel*, though, there is nothing above it: the
   toolbar floats before the panel's own `<h2>`, so -5px lifts it clear of the
   card's top border and a zero right margin puts it hard against the right one.
   Measured on Step 3's Tools and Details panels: the toolbar ran 1129.9-1160.9
   against a card starting at 1133.9, so the ✕ straddled the corner - four pixels
   of it outside the card it belongs to, at the one place where two cards meet.

   12px centres it in the title band (the band is 54.5px tall and the toolbar
   31px, so 11.75px is exact, and a quarter-pixel is not worth a fractional
   margin); 16px is the inset the panel gives its heading and its body, so the
   toolbar ends where its own title's text would. */
.lateralOptionsPanel > .lateralOptionsPanel-toolbar {
	margin: 12px 16px 0 0;
}
.lateralOptionsSelector.omicSelection {
	padding: 6px;
	border: solid 1px var(--pa-border);
	margin: 10px 0px;
	border-radius: var(--pa-radius-sm);
	background: #fff;
	display: block;
	max-width: 350px;
}
.step4_plotwrappers{display: inline-block;}

/* These were 18px with a 2px border and steel-blue ink - roughly twice the size
   of every other control on the page, in a panel that sits beside it.

   The find-features field opts out: it lives in a flex row that owns its
   geometry (.findFeaturesRow above), and this rule's `margin: 6px 0` outranks
   that row's `margin: 0` - 6px of top margin inside an align-items: stretch
   row pushed the field below the button it shares the line with. */
div.lateralOptionsPanel input[type="text"]:not([id^=numberfield]):not(.findFeaturesInput input) {
	margin: 6px 0;
	width: 100%;
	max-width: 250px;
	font-size: 13px;
	padding: 6px 10px;
	border-radius: var(--pa-radius-sm);
	border: 1px solid var(--pa-border);
	color: #27272A;
}
/* The per-omic reference-value group in Step 4's Visual settings. A bare
   <fieldset>, so it was drawing the browser's default 2px inset `groove`
   border - the one control in the application still showing a bevel - with the
   omic's name in the legend at whatever size the UA picked. Same hairline and
   corner as every other box on the page, and the legend takes the group-label
   treatment its siblings above it already have. */
div.lateralOptionsPanel fieldset {
	margin: 8px 0 16px;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	padding: 4px 12px 12px;
}
div.lateralOptionsPanel fieldset > legend {
	width: auto;
	margin: 0;
	padding: 0 6px;
	border: 0;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--pa-ink-muted);
}
/* These panels sit inside a .contentbox, so their headings match
   `div.contentbox h3` - but they are compact control labels ("Nodes per row:",
   "Node size:") in a narrow settings column, not card headings, and the card
   inset would push them off their own controls. `div.` ties the specificity so
   this rule, being later in the file, wins. */
div.lateralOptionsPanel h3 {
	padding-left: 0;
	padding-right: 0;
}
/* The settings-column type scale -------------------------------------------
   These panels are the densest text in the application: the network's Tools
   column stacks three groups, nine control labels, four sliders, six
   checkboxes and two radio pairs into 260px. With the global scale alone every
   one of those labels was the same near-black at within 1.5px of every other,
   so the column read as an undifferentiated list and the reader had to
   re-derive the grouping from the controls each time.

   Two roles, told apart the way a form does it rather than by colour:

   h4 is the group - "Visual settings", "Network layout settings", "Node
   filtering options". Small, tracked, uppercase and muted, with a rule above
   it. That is the standard treatment for a settings-group label precisely
   because it reads as a divider rather than as another line of content; it
   also survives being one word long, which a 16px green heading did not.

   h5 is the control's own label - "Node coloring:", "Label font size (14)".
   Near-black at body size, sitting directly on top of the thing it names.

   The group treatment is opted into with `paSettingsPanel` rather than applied
   to `.lateralOptionsPanel` at large, because three of the panels sharing that
   class are not settings columns: the pathway view's information strip uses h4
   for the pathway's own name and the omic's, and the network's Details column
   uses it for whatever the network is currently coloured by. Uppercasing
   "Ribosome biogenesis in eukaryotes" is not a label treatment, it is a
   spelling mistake. Names take the plain heading below; only a settings group
   takes the tracked one.

   `:first-child` drops the rule and the space above the first group, which
   would otherwise draw a line immediately under the panel header's own.

   Order matters between the next two rules and cannot be swapped: a settings
   panel carries both classes, and `div.lateralOptionsPanel h4` and
   `div.paSettingsPanel h4` are the same specificity, so the general case has to
   come first for the specific one to win on source order. */
div.lateralOptionsPanel h4 {
	font-size: 13px;
	font-weight: 600;
	color: #1A1A18;
	margin: 16px 0 6px;
}
/* The MORE network's rail is named alongside these because it is the same
   object: a narrow column of grouped controls beside a graph, on the same page,
   one scroll below this one. It had its own scale - `h6` at 11px/700 with
   0.04em of tracking and no rule above it - which is close enough to the one
   below to look like a mistake rather than a distinction, and far enough to
   stop the two rails reading as one component. Its markup uses `h6` for the
   group and `.more-net-field-label` for the control label; the roles map
   exactly, so the selectors join the rules rather than restating them. */
div.paSettingsPanel h4,
.more-net-side h6 {
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--pa-ink-muted);
	margin: 22px 0 10px;
	padding-top: 16px;
	border-top: 1px solid var(--pa-border);
}
div.paSettingsPanel h4:first-child,
.more-net-side h6:first-child {
	margin-top: 4px;
	padding-top: 0;
	border-top: 0;
}
div.lateralOptionsPanel h5,
.more-net-side .more-net-field-label {
	display: block;
	font-size: 12.5px;
	font-weight: 600;
	color: #27272A;
	margin: 14px 0 6px;
}
/* The help mark that trails most of these labels is `float:right` inline, so
   it leaves the line box and the label's own margin no longer clears it. */
div.lateralOptionsPanel h5::after {
	content: "";
	display: block;
	clear: both;
}
.globalHeatmapConfigurator {
	padding: 15px;
	background: #FFF6FF;
	border: 1px solid #A9A9A9;
	padding-bottom: 50px;
}
a.applyOptionsButton {
	background: #15B235;
	margin: 0;
	font-size: 25px;
	padding-bottom: 5px;
}
div.applyWaitMessage {
	margin-top: 30px;
	font-size: 25px;
	color: #9E3AB3;
	display: none;
	clear: both;
}
div.lateralOptionsPanel div.updateMessageContainer {
	color: #B34E23; /* was #BE5325 - 4.18:1 on this box's own #FFEFD9 fill */
	background: #FFEFD9;
	font-size: 15px;
	text-align: center;
	border: 1px solid #EDBE27;
	margin: 10px 0px;
	display: none;
}
div.lateralOptionsPanel div.updateMessageContainer h3 {
	color: #96621C; /* was #CD8627 - 2.64:1 on the same #FFEFD9 fill */
}
div.zoomTool {
	margin-top: 60px;
	position: absolute;
	top: 0;
	left: 10px;
}
div.zoomTool a {
	background: rgba(192, 192, 192, 0.55);
	font-family: var(--pa-font-sans);
	/* The pathway zoom +/- controls. The pill is 55% grey, which composites to
	   about #DCDCDC over the mostly-white KEGG diagram behind it, so the white
	   glyphs sat at 1.37:1 - the reason these read as ghosted in screenshots.
	   Darkening the glyph rather than the pill keeps the control unobtrusive
	   over the diagram: 12.73:1 on the composited fill, and still comfortable
	   on the #40AFFF hover fill below. */
	color: #1A1A1A;
	font-size: 15px;
	text-decoration: none;
	padding: 9px 13px;
	display: block;
	margin-bottom: 3px;
}
div.zoomTool a:hover {
	background-color: #40AFFF;
}
a.zoomIn {
	border-top-left-radius: 10px;
	border-top-right-radius: 10px;
}
a.zoomOut {
	border-bottom-left-radius: 10px;
	border-bottom-right-radius: 10px;
}
div.omicHeatmapsContainer {
	float: left;
}
div.omicHeatmapsContainer > div {
	border-right: 2px dashed #BABABA;
}
div.heatmapSetContainer div {
	display: inline-block;
}
ul.ui-menu.ui-widget.ui-widget-content {
	position: absolute !important;
	border: 1px solid #E2E2E2;
	background: white;
}
ul.ui-menu li.ui-menu-item {
	list-style: none;
	color: #333;
	padding: 5px 5px;
}
ul.ui-menu li.ui-menu-item.ui-state-focus {
	/* The highlighted row of the organism autocomplete - reached by arrow keys,
	   so it is precisely the state a keyboard user relies on. White on #40AFFF
	   was 2.39:1; the fill moves rather than the label so the highlight still
	   reads as "blue selection". */
	color: #FFF;
	background: #0078CE;
}
.ui-autocomplete {
	max-height: 100px;
	overflow-y: auto;
	/* prevent horizontal scrollbar */
	overflow-x: hidden;
}
div.omicSelection {
	cursor: move;
}
div.omicSelection div.omicPosition {
	font-size: 40px;
	float: left;
	width: 40px;
	text-align: left;
	color: #9E3AB3;
}
div.omicSelection div.omicPosition:after {
	content: "\f0dc";
	font: normal normal normal 13px/1 FontAwesome;
	vertical-align: middle;
}
div.omicSelection div.omicPosition + div {
	margin-left: 45px;
}
.lateralOptionsSelector.kSelection label {
	width: 110px;
	display: inline-block;
}
.lateralOptionsSelector.kSelection input {
	width: 35px;
	text-align: right;
}

/********************************************************************************
**STYLES FOR STEP 5
*********************************************************************************/

.featureOverviewInfoContainer a {
	display: block;
	margin: 10px;
}

/***********************************************************/


/**STYLES FOR MESSAGE DIALOGS*******************************/


/***********************************************************/

/* The toast stack that used to live at #msg-div is gone, and so are the two
   .MessageBox_* icon rules beside it. Nothing in the tree creates any of it:
   no id, no .msg / .error_msg / .warning_msg, and their inner selector was
   `.x-box-mc`, which is ExtJS 3 markup that appears nowhere in the 4.2.1 build
   this application runs on.

   Messages have not gone through it for a long time. showSuccessMessage,
   showWarningMessage and showErrorMessage in Util.js all funnel into
   showMessage, which paints #messageDialog and swaps successDialog /
   warningDialog / errorDialog / infoDialog on it - the rules for which are
   further down this file and are the live ones.

   Worth knowing when reading the design system: its Toast component was lifted
   from these declarations, so its palette is real but the thing it describes is
   this dead stack rather than the dialog the product actually shows. */
#warningMessage {
	background: #ffedc4;
	border: 2px solid #fec643;
	display: inline-block;
	margin-left: 65px !important;
	margin-top: -5px !important;
	padding: 10px;
	font-size: 16px;
	font-weight: bold;
}

/***********************************************************/


/**STYLES FOR help tips ************************************/


/***********************************************************/

/* Both of these drew their icon from a PNG that is in neither the repository
   nor the deployed image - `help_12x12.png` and `warning_12x12.png` have been
   missing long enough that no build or deploy ever had them.

   Neither rule reaches a user today, so this is latent rather than live.
   `.formfieldHelpTip` is emitted only by resources/help/helpTips.js, which
   index.html does not load and nothing else references or injects; a rendered
   page contains no element with the class at all. `.warningTip` has no callers
   in the tree either.

   Fixed rather than deleted because the failure is in the reference, not the
   rule: FontAwesome is loaded globally and is already how this stylesheet draws
   checkbox and radio marks, so the glyphs need no file on disk and both work if
   either is ever wired up. */
.formfieldHelpTip {
	cursor: help;
}
.formfieldHelpTip::before,
.warningTip::before {
	display: inline-block;
	font-family: FontAwesome;
	font-style: normal;
	font-weight: normal;
	line-height: 1;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}
.formfieldHelpTip::before {
	content: "\f059";  /* question-circle */
	color: var(--pa-ink-muted);
}
.warningTip {
	line-height: 19px;
	font-style: italic;
	/* Was rgb(163,163,163), which is 2.3:1 on white - below the 4.5:1 the rest
	   of this palette was retuned to, and a warning is the last thing that
	   should be the hardest text on the page to read. */
	color: var(--pa-ink-muted);
}
.warningTip::before {
	content: "\f071";  /* exclamation-triangle */
	margin-right: 5px;
}
.x-tip-body {
	font-size: 12px;
}

/*.icon-help{background-image:url(../images/info_16x16.png) !important;background-repeat:no-repeat;background-position:center;}*/


/***********************************************************/
/**STYLES FOR MESSAGE DIALOGS*******************************/
/***********************************************************/

.x-tab-bar-strip {
	top: 30px !important;
	/* Default value is 20, we add 20 = 40 */
}
.x-tab-bar .x-tab-bar-body {
	height: 33px !important;
	/* Default value is 23, we add 20 = 43 */
	border: 0 !important;
	/* Overides the border that appears on resizing the grid */
}
.x-tab-bar .x-tab-bar-body .x-box-inner {
	height: 31px !important;
	/* Default value is 21, we add 20 = 41 */
}
.x-tab-bar .x-tab-bar-body .x-box-inner .x-tab {
	height: 31px !important;
	/* Default value is 21, we add 20 = 41 */
}
.x-tab-bar .x-tab-bar-body .x-box-inner .x-tab button {
	/*height: 31px !important;*/
	line-height: 33px !important;
	font-size: 14px;
}
.x-tab button .x-tab-inner {
	height: 16px;
}
.x-tab-center {
	margin-top: -10px;
}

/***********************************************************/
/**STYLES FOR GRID FILTERING *******************************/
/***********************************************************/

.ux-rangemenu-icon {
	display: block;
	height: 16px;
	background: no-repeat 5px center;
}
.ux-rangemenu-gt {
	background-image: url(../images/greater_than.png) !important;
}
.ux-rangemenu-lt {
	background-image: url(../images/less_than.png) !important;
}
.ux-rangemenu-eq {
	background-image: url(../images/equals.png) !important;
}
.customcheckcolumn {
	font-size: 20px;
	line-height: normal
}
.fa-checkedcheckcolumn {
	color: #0BA949;
}
.customactioncolumn {
	font-size: 11px;
	line-height: 20px;
	text-align: center !important;
}
.customactioncolumn a {
	padding: 0px 4px;
	/* Was #ADA6A6 - 2.16:1 on the grid's own background, and this is the "Paint
	   this pathway" brush that opens Step 4, i.e. the primary action of the
	   whole pathway table. At that contrast it read as a disabled placeholder;
	   it is the single most repeated failure in the app (~1770 instances on a
	   default Step 3). #756C6C keeps the warm grey but reaches 4.60:1. */
	color: #756C6C;
}
.x-grid-row-selected .customactioncolumn a, .customactioncolumn a:hover {
	/* Deliberately not --pa-link. A selected grid row is filled #C1DDF1 by the
	   Neptune theme, and the token only reaches 3.78:1 against that; #0076E2
	   was 3.17:1. This value clears 4.63:1 on the selected-row fill and 6.53:1
	   on the white fill the :hover half of this selector sits on. */
	color: #005DB3;
}
.x-toolbar {
	padding: 5px 0 6px 8px;
	background-color: #F5F4EF;
}

/***********************************************************/
/**STYLES FOR TOOLTIP ** *******************************/
/***********************************************************/

.tooltipDetailsSpan {
	color: var(--pa-link);
	cursor: pointer
}
.tooltipDetailsSpan a {
	color: rgb(211, 21, 108);
	display: none;
}

/***********************************************************/
/**STYLES FOR GENE OVERVIEW* *******************************/
/***********************************************************/

/* These two are one figure: the strip is the per-sample colour scale and the
   chart to its right is the same values plotted. Left on the default baseline
   alignment they sat on different lines whenever the strip's legend was taller
   than the chart's first row, so the pair is pinned to the top instead. */
.PA_step5_heatmapContainer {
	width: 300px;
	display: inline-block;
	vertical-align: top;
}
.PA_step5_plotContainer {
	min-width: 300px;
	display: inline-block;
	vertical-align: top;
}

/* The colour ramp a heatmap was painted with, drawn by paColorLegend() in
   PA_Step3Views.js.

   It used to write every one of these declarations inline, which is why
   dark.css carried a pair of `[style*="color:#555"]` overrides - an inline
   colour is unreachable by any stylesheet, so `!important` on a value-matched
   attribute selector was the only way to theme it. Stated here, it themes
   itself and those two rules could be deleted.

   The bar is separated from the surface by an inset hairline rather than a
   solid #999 border: a 1px mid-grey box around a 10px strip is most of what
   the eye sees at that size, and it competed with the ramp it was framing. */
.paColorLegend {
	/* Block-level, not inline. As an inline-block it flowed onto the same line
	   as the .PA_step5_heatmapContainer beside it (also inline-block), which
	   is how it ended up marooned to the left of the figure. */
	display: flex;
	flex-direction: column;
	gap: 3px;
	margin: 2px 0 8px 0;
	font-size: 10px;
	line-height: 1.3;
	color: var(--pa-ink-muted, #595959);
}
.paColorLegend-bar {
	position: relative;
	display: block;
	height: 12px;
	border-radius: 2px;
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .10);
}
/* Where zero falls. Drawn slightly proud of the bar at both ends so it reads
   as a tick against the ramp rather than as a seam in it. */
.paColorLegend-tick {
	position: absolute;
	top: -2px;
	bottom: -2px;
	width: 1px;
	margin-left: -0.5px;
	background: rgba(0, 0, 0, .42);
}
.paColorLegend-scale {
	position: relative;
	display: flex;
	justify-content: space-between;
	font-variant-numeric: tabular-nums;
}
.paColorLegend-zero {
	position: absolute;
	transform: translateX(-50%);
}
.paColorLegend-caption {
	color: var(--pa-ink-subtle, #8A8A8A);
}

/* Shown where a figure would go when there is nothing to draw. Previously these
   cases either left a heading with nothing under it or opened an empty white
   box, both of which read as a failure rather than as an answer. */
.paEmptyNote {
	padding: 22px 24px;
	color: #52525B;
	font-size: 12.5px;
	line-height: 1.55;
}
.paEmptyNote p {
	margin: 0;
	max-width: var(--pa-measure-note);
}
.paEmptyNote p + p {
	margin-top: 8px;
	color: #71717A;
}
.geneInfoTitle {
	font-size: 15px;
	cursor: pointer
}
/* The feature panels carry `contentbox`, so their titles match
   `div.contentbox h3` - but this is a 245px strip in the pathway view, not a
   card, and the card inset would take a tenth of its width. `div.` ties the
   specificity so this rule, later in the file, wins. */
div.mainInfoPanel h3 {
	padding-left: 0;
	padding-right: 0;
}
.extraInfoPanel {
	display: none;
	vertical-align: top;
	width: 250px;
}

.findFeaturesContainer .mainInfoPanel {
	display: inline-block;
	min-width: 245px;
}

/***********************************************************/
/**STYLES FOR MYDATA SECTION *************************/
/***********************************************************/

/* Used space --------------------------------------------------------------
   Replaces .myDataSummaryChartWrapper (a 150px Highcharts donut) and
   .myDataSummaryCount (30px counters in coloured icon tiles). One proportion
   against a known maximum reads as a length, so it is a bar; the two counts
   were never charts, so they are stat tiles. */
.po-storage {
	display: flex;
	align-items: center;
	gap: 40px;
	flex-wrap: wrap;
}
.po-storage-meter {
	flex: 1 1 260px;
	min-width: 220px;
}
.po-storage-figure {
	margin: 0 0 10px;
	display: flex;
	align-items: baseline;
	gap: 6px;
}
/* The hero number. Tabular figures so the bar below does not appear to jitter
   when the value ticks over as files are added or deleted. */
.po-storage-used {
	font-size: 34px;
	line-height: 1;
	font-weight: 300;
	color: var(--pa-ink-title, #24252A);
	font-variant-numeric: tabular-nums;
}
.po-storage-unit {
	font-size: 17px;
	color: var(--pa-ink-body, #555);
}
.po-storage-of {
	font-size: 13px;
	color: var(--pa-ink-muted);
	margin-left: 2px;
}
.po-storage-track {
	height: 8px;
	border-radius: var(--pa-radius-pill, 999px);
	background: var(--pa-surface-quiet, #F1F2F4);
	overflow: hidden;
}
.po-storage-fill {
	height: 100%;
	border-radius: var(--pa-radius-pill, 999px);
	background: #288824;
	transition: width var(--pa-transition), background-color var(--pa-transition);
}
/* Status, not category: exactly one of these is on screen at a time, so they
   are never compared against each other. The figure above the bar says the
   same thing in words, which is what keeps this from being colour-alone.
   Values are the design system's status ramp - the amber replaces Highcharts'
   #DDDF0D, which measured 1.6:1 on white and was invisible as a warning. */
.po-storage-fill.is-ok       { background: #288824; }
.po-storage-fill.is-warning  { background: #CB7505; }
.po-storage-fill.is-critical { background: #D36044; }

.po-storage-stats {
	display: flex;
	gap: 34px;
}
.po-stat {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.po-stat-value {
	font-size: 30px;
	line-height: 1;
	font-weight: 300;
	color: var(--pa-ink-title, #24252A);
	font-variant-numeric: tabular-nums;
}
.po-stat-label {
	font-size: 12px;
	letter-spacing: 0.02em;
	color: var(--pa-ink-muted);
}
.po-storage-note {
	margin: 18px 0 0;
	font-size: 12.5px;
	line-height: 1.5;
	color: var(--pa-ink-muted);
	max-width: var(--pa-measure-note);
}

/* The upload toolbar. Its buttons used to state their fills inline, which no
   stylesheet could reach - see the note at the top of upload/Panel.js. With
   those gone they inherit .x-btn-default-small, and only the two that carry a
   different meaning are restated here.

   The measured reason the old values had to go: white on #5CB85C was 2.48:1 and
   on #dd4744 was 4.15:1. Both replacements are the design system's own steps
   and both measure 4.62:1, which is also exactly what the inherited blue makes,
   so the whole toolbar now sits at one contrast rather than three. */
.po-upload-go.x-btn-default-small {
	background-color: #3A843A;
	border-color: #306C30;
}
.po-upload-go.x-btn-default-small-over,
.po-upload-go.x-btn-default-small-focus {
	background-color: #337533;
	border-color: #2A602A;
}
.po-upload-danger.x-btn-default-small {
	background-color: #D43E3A;
	border-color: #B62B28;
}
.po-upload-danger.x-btn-default-small-over,
.po-upload-danger.x-btn-default-small-focus {
	background-color: #C32F2B;
	border-color: #A02723;
}
/* The two cards at the top of Personal storage. `div.contentbox h2` and
   `div.contentbox p` both carry --pa-card-inset, so a heading and a paragraph
   in one of these cards line up - but the account list is a `<dl>` and the
   storage meter is a `<div>`, and neither selector reaches those. Measured on
   the rendered page: "My account" started at x=87 and "User name" directly
   under it at x=61, a 26px step between a heading and the thing it titles.

   Stated on the blocks rather than as padding on the card, because the card's
   own heading is a full-bleed band with a rule under it and must keep running
   edge to edge. */
.po-account,
.po-storage {
	padding-left: var(--pa-card-inset);
	padding-right: var(--pa-card-inset);
}
/* Both cards end on the same baseline. The meter card was `height: 200` against
   the account card's `min-height: 230`, so two cards on one row stopped at
   different heights over a shared background. */
.poMyDataCard {
	padding-bottom: 18px;
}

/* An empty grid. My files and My jobs used to render the header row above an
   empty body and stop, which reads as a grid that failed to load rather than
   as an account with nothing in it yet; they now carry an emptyText.

   The framework's box for that text is stated in the vendored Neptune theme as

       .x-grid-empty { padding:10px; color:gray; background-color:white;
                       font:normal 13px helvetica,arial,verdana,sans-serif }

   - a white block set in Helvetica, which on a dark card is a white block set
   in Helvetica. Restated here rather than in dark.css because the font and the
   inset are wrong in both themes, and the colour is a token that follows the
   theme by itself. Not scoped to these two grids: every empty grid in the
   application has the same box. */
.x-grid-empty {
	padding: 18px var(--pa-card-inset);
	background-color: transparent;
	font-family: var(--pa-font-sans);
	font-size: 13px;
	color: var(--pa-ink-muted);
}

/* The account details. Was table#myDataUserDetails, a 15px two-column table
   whose left column was bold labels. */
.po-account {
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 14px;
}
.po-account-row {
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.po-account dt {
	font-size: 12px;
	letter-spacing: 0.02em;
	color: var(--pa-ink-muted);
}
.po-account dd {
	margin: 0;
	font-size: 15px;
	color: var(--pa-ink-title, #24252A);
	/* An address is data: it should be selectable and never silently clipped. */
	word-break: break-word;
}
.po-account-actions {
	text-align: left;
	margin-top: 22px;
}
/***********************************************************/
/**COMMON STYLES FOR ALL STEPS *************************/
/***********************************************************/
p.formHelpTip {
	margin-left: 155px;
	margin-top: -3px;
	font-size: 10px;
	color: #1282BD;
}

a.toolbarOption {
	font-size: 30px;
	padding: 7px 10px 0px 10px;
	margin: 0px 10px;
	float: right;
	cursor: pointer;
}
/* `div.contentbox { max-width: var(--main-max-width) }` stood here, and it
   quietly won an argument the page-column block up at #mainViewCenterPanel
   thought it had settled. That block ends "`.contentbox` no longer caps itself,
   so surfaces fill the column"; this rule has the same selector and the same
   specificity, sits 1000 lines further down, and so took precedence. Measured on
   the deployed page, every div.contentbox computed max-width: 1900px, not none.

   Nothing moved when it went. The column is enforced by padding on the centre
   panel, so a content box is `100vw - 2 * --pa-gutter` wide, and that gutter is
   `max(40px, (100vw - 1400px) / 2)` - which holds the inner width at 1400px
   however wide the viewport gets. A 1900px ceiling can never be reached, so it
   was doing nothing except contradicting the comment that explains the layout.
   Checked before removing: forcing it to none on the live page left all three
   boxes at 1339px. */
/* The in-panel toolbars ------------------------------------------------------
   The ✕ that closes the Tools and Details columns, and the remaining
   `.toolbarOption` controls elsewhere in the application.

   They wore three colours between them. `.toolbarOption` painted them
   terracotta; `.resumeLayout` overrode three of those to green with
   `!important`; the ✕ inherited the terracotta and read as a destructive
   control on a panel that only hides itself. Nothing chose that arrangement -
   it is two rules written years apart, and the result was a toolbar whose
   colour coding could not be explained because there was none to explain.

   A secondary toolbar's job is to be available without competing with the
   thing it operates on, which here is a network diagram in six saturated
   category colours. So: one neutral ink, and the control announces itself by
   taking a surface under the cursor rather than by being coloured all the time.
   That is the same move the header nav and the card toolbars at level one
   already make; this brings the panels into line with them.

   Step 3's network strip used to be styled from here too. It has moved to
   `.pa-net-tool` in network-views.css, which it now shares with the MORE
   network's buttons; this rule stays for the controls that are not in either
   graph toolbar. */
div.contentbox a.toolbarOption {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	font-size: 13px;
	/* Measured: the five controls in the network strip come to 546px inside a
	   540px bar at 9px of horizontal padding, and 516px at 7px. The hover
	   surface still needs to be a shape rather than a tint on the text, so this
	   is the smallest padding that reads as a control. */
	padding: 5px 7px;
	margin: 4px 1px;
	background: none;
	border-radius: var(--pa-radius-btn);
	color: #3F3F46;
	transition: background-color var(--pa-transition), color var(--pa-transition);
}
div.contentbox a.toolbarOption:hover {
	color: #18181B;
	background-color: rgba(24, 24, 27, 0.06);
}
/* These carry .btn-primary / .btn-danger, which were filled shapes chosen to sit
   on the old coloured header. On a white header a red-filled close button is the
   loudest thing in the panel, so they read as icons and colour on hover. */
div.lateralOptionsPanel-header .toolbarOption {
	font-size: 15px;
	padding: 5px 8px;
	margin: 0 0 0 4px;
	background: transparent;
	border: 1px solid transparent;
	border-radius: var(--pa-radius-btn);
	color: #52525B;
	box-shadow: none;
	transition: background-color .12s ease, color .12s ease;
}
div.lateralOptionsPanel-header .toolbarOption:hover {
	background: rgba(0, 0, 0, 0.06);
	color: #27272A;
}
/* The download control keeps a label, so it stays a button - just a quiet one. */
div.lateralOptionsPanel-header .toolbarOption.downloadTool {
	border-color: var(--pa-border);
	background: #FFFFFF;
}
div.lateralOptionsPanel-header .toolbarOption.downloadTool:hover {
	background: #F9F8F4;
	border-color: #C4C4CC;
}
/* Step 3's two network download controls. They were a 16px icon stacked over a
   10px label on a 7px line box - a two-line control 10px tall, in a header
   whose title is 19px, set in the smallest type anywhere in the application.
   Laid out inline like every other toolbar control instead, and given the same
   quiet outline the Step 4 download button above already uses, so "Download
   (PNG)" and "Download (SVG)" read as the pair of buttons they are. */
.lateralOptionsPanel-toolbar .toolbarOption.downloadTool {
	font-size: 12px;
	line-height: 1.2;
	padding: 5px 10px;
	border: 1px solid var(--pa-border);
	background: #FFFFFF;
	margin-top: 0;
}
.lateralOptionsPanel-toolbar .toolbarOption.downloadTool:hover {
	background: #F9F8F4;
	border-color: #C4C4CC;
}
.lateralOptionsPanel-toolbar .toolbarOption.downloadTool > i {
	font-size: 13px;
}
/* The control that hides a side panel. It is the only ✕ in the results view,
   and terracotta made it read as "delete this" rather than "put this away" -
   the panel comes back from the Configure button two inches to its left. Muted,
   like the ✕ on every dialog in the application, and it colours on hover. */
.lateralOptionsPanel-toolbar .toolbarOption.hideOption {
	color: #8A8B92;
	padding: 5px 7px;
}
.lateralOptionsPanel-toolbar .toolbarOption.hideOption:hover {
	color: #18181B;
	background-color: rgba(24, 24, 27, 0.06);
}

/* Pathways summary counts -------------------------------------------------
   Step 3's "N Found Pathways / N Significant" pair.

   These were styled by `.myDataSummaryCount` -- "30px counters in coloured
   icon tiles", per the note further up this file. That class was deleted with
   the MyData rework and these two call sites were never migrated, so the
   markup carried a class with no rules behind it. What survived was the one
   piece of styling that was inline: `background-color: #F1CC28` on the star's
   own `<i>`. Font Awesome glyphs fill their whole box, so that painted a hard
   yellow rectangle behind the star and nothing behind the sitemap icon -- the
   two counts stopped matching each other, and one looked like a rendering
   fault.

   Restored as the tinted tiles the old class described. The colour is on the
   glyph; the tile is a wash behind it, which is what keeps it from reading as
   a block. Both live here rather than inline so `dark.css` can reach them --
   an inline colour cannot be themed at all. */
.po-pathway-stat {
	display: flex;
	align-items: center;
	gap: 14px;
}
.po-pathway-icon {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	border-radius: 50%;
	font-size: 21px;
	background: rgba(31, 107, 193, 0.12);
	color: var(--pa-accent-blue, #19589E);
}
.po-pathway-icon > i {
	/* Font Awesome's own line-height would offset the glyph inside the
	   circle; the flex centring above is what should place it. */
	line-height: 1;
}
.po-pathway-icon.is-significant {
	background: rgba(173, 80, 34, 0.12);
	color: var(--pa-accent-orange, #AD5022);
}
/* The figure stacks: numeral over label, both left-aligned, which is what
   lets two figures of very different widths ("877" / "45") sit in cells of
   the same height without the labels drifting to different baselines. */
.po-band-figure {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 8px;
}
/* Matches .po-stat-value, the design system's figure treatment, rather than
   inventing a second one. Tabular figures so the number does not jitter
   sideways as it counts up. */
.po-pathway-count {
	font-size: 34px;
	line-height: 1;
	font-weight: 300;
	color: var(--pa-ink-title, #24252A);
	font-variant-numeric: tabular-nums;
}
/* The two biggest numerals on the results page were not set in the page's own
   typeface. `odometer-theme-default.css` ends with
   `.odometer.odometer-theme-default { font-family: "Helvetica Neue"; line-height: 1.1em }`,
   which is (0,2,0) against this block's (0,1,0) - so the vendor theme won both
   declarations and 776 / 101 rendered in Helvetica Neue Light while every other
   glyph on the page was Source Sans. At 34px that is the one place a reader can
   actually see the difference between two grotesques.

   The line-height matters as much as the face. `1.1em` on a 34px figure makes
   the odometer's box 3.4px taller than its digits, and because the box is an
   inline-block its baseline is the baseline of that taller line - so the label
   beside it, aligned with `align-items: baseline`, was pushed off the numeral's
   own baseline by exactly that amount. Measured before: label bottom 289,
   figure bottom 298. Restoring `line-height: 1` puts the two back on one line.

   Three class selectors so this beats the vendor's two without !important. */
.po-pathway-count.odometer.odometer-theme-default {
	font-family: inherit;
	line-height: 1;
}
/* A caption for the figure, so it sits below body copy (14px) rather than above
   it. At 15px the label was larger than the prose around it and read as a
   second heading rather than as the unit of the number it follows. */
.po-pathway-label {
	font-size: 13px;
	color: var(--pa-ink-muted, #595959);
}
/* The summary band --------------------------------------------------------
   One card where three used to be. The old top row was "Pathways selection" -
   four lines of boilerplate about how significance is computed - beside
   "Pathways summary", and a third card ("Multiple databases used") added two
   more paragraphs over a table: ~700px of prose before the first control,
   almost none of it varying by job. What varies now sits in a single band and
   the boilerplate lives in the heading's tooltip; the markup comment in
   PA_Step3Views.js tells the whole story.

   Two rows. The head row is the card's heading strip - h2, the job's name,
   then the job chip and the paint call-to-action on the right. The band under
   it is one row of figures: the two totals, then the per-database split with
   the one-line note that replaced the old paragraphs. Hairlines separate the
   cells; the card's own border frames the whole, so nothing inside carries a
   second box. */
.po-band-head {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 10px 18px;
	background-color: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
	border-radius: var(--pa-radius) var(--pa-radius) 0 0;
	padding: 12px var(--pa-card-inset);
}
/* The heading's dress - white fill, hairline, inset - moved up onto the row so
   the h2 can share a flex line with the chip and the button. Two classes and a
   tag against `div.contentbox h2`'s one-and-two, so no !important. */
.po-band-card .po-band-head h2 {
	background: transparent;
	border: 0;
	border-radius: 0;
	padding: 0;
	flex: none;
}
/* The job's own description ("STATegra - real mouse Ikaros time course"),
   clipped rather than wrapped: it is a subtitle, and a subtitle that can grow
   a second line would push the actions strip off its row. The full text rides
   the title attribute, set where the text is. */
.po-job-name {
	display: block;
	flex: 1 1 160px;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 13px;
	color: var(--pa-ink-muted, #595959);
}
.po-band-actions {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-left: auto;
}
.po-band-actions .button {
	margin: 0;
}
/* The job ID as a chip: the one piece of the old card people actually used -
   the thing you paste into a mail - reduced to the ID plus two glyph buttons,
   open and copy. The URL itself is no longer written out; it is the page's own
   address, and the buttons hand it over without asking anyone to select text. */
.po-job-chip {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	padding: 4px 8px 4px 14px;
	border: 1px solid var(--pa-border);
	border-radius: 999px;
	background: var(--pa-surface-subtle, #F9F8F4);
	font-size: 13px;
	color: var(--pa-ink-muted, #595959);
	white-space: nowrap;
}
.po-job-chip > b {
	font-weight: 600;
	color: var(--pa-ink-title, #24252A);
	letter-spacing: 0.02em;
}
.po-job-chip > a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	color: var(--pa-accent-blue, #19589E);
	font-size: 12px;
}
.po-job-chip > a:hover {
	background: rgba(25, 88, 158, 0.10);
}
/* The moment of feedback after a copy: the glyph itself turns into a tick.
   Green, because the tick reports success, not a link. */
.po-job-chip > a.is-copied {
	color: var(--pa-accent-green, #2A8368);
}
/* The band proper: one row of cells that together own the card's full width.
   line-height 1 keeps the numerals from inheriting the page's paragraph
   rhythm.

   The cells grow, and that is the point of this block. Content-sized cells
   packed left - the first version - left everything right of the database
   table as one unbroken white field, roughly half the card at the widths a
   desktop shows it: dead space with a shape, which reads as a card waiting
   for content that never arrives. Growing cells put the same air *between*
   the figures instead, each centred in its own share with a full-height
   hairline marking the shares, so the width reads as composed rather than
   unfilled. `stretch` is what lets those hairlines run the band's whole
   height whatever cell happens to be tallest. */
.po-band {
	display: flex;
	align-items: stretch;
	flex-wrap: wrap;
	gap: 16px 0;
	padding: 20px var(--pa-card-inset) 22px;
	line-height: 1;
}
.po-band .po-pathway-stat {
	/* Equal shares, but `auto` basis: a zero basis hands "877" and its label
	   exactly a third and lets min-width break the odometer across lines on a
	   narrow card - the same defect the old figures panel documented. Content
	   first, slack split evenly. */
	flex: 1 1 auto;
	justify-content: center;
	padding: 0 24px;
}
.po-band .po-pathway-stat + .po-pathway-stat,
.po-band .po-band-dbs {
	border-left: 1px solid var(--pa-border);
}
/* The per-database cell: the mini table over its one-line note. Column, not
   row - side by side the pair needed ~590px and pushed the band past the card
   at the widths the contents rail leaves it. A slightly heavier share than
   the totals, because its content is wider to begin with; its children are
   centred individually, which the near-equal widths of the table and the
   capped note keep from reading as ragged. */
.po-band-dbs {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	flex: 1.4 1 auto;
	gap: 10px;
	padding: 0 24px;
}
/* Two classes, because `div.contentbox p` (0,1,2) would otherwise re-apply the
   card inset and paragraph margins this note is opting out of. Centred text:
   the note and the table above it are separately-centred blocks of different
   widths, so left-aligned caption text put its edge ~30px outside the table's
   and the pair read as misaligned rather than stacked. */
.po-band .po-band-note {
	margin: 0;
	padding: 0;
	max-width: 320px;
	font-size: 12.5px;
	line-height: 1.5;
	text-align: center;
	color: var(--pa-ink-muted, #595959);
}
/* When the band is too narrow for the split beside the totals - a narrow
   window, or the contents rail taking its column - the database cell becomes
   the band's second row, and its hairline turns horizontal with it. A viewport
   query, not a container query: this card is a full-width box whose width
   tracks the viewport, and ExtJS re-lays the page on window resize anyway. */
@media (max-width: 1100px) {
	.po-band .po-band-dbs {
		flex-basis: 100%;
		border-left: 0;
		border-top: 1px solid var(--pa-border);
		padding: 16px 0 0;
	}
}

.omicSummaryBox, .metaboliteBox {
	width: 49%;
	margin: 5px 0;
	min-height: 230px;
}
.omicSummaryBox:nth-child(odd), .metaboliteBox:nth-child(odd)  {
	float: left;
}
.omicSummaryBox:nth-child(even), .metaboliteBox:nth-child(even) {
	float: right;
}
/* Paired cards end level with each other.

   `width: 49%` on two floats sets where a card starts and stops horizontally
   and says nothing about how tall it is, so a row of two cards ended at two
   different heights - on Step 3, "Pathways selection" ran 45px past "Pathways
   summary" beside it, and the row read as two unrelated blocks rather than as
   one comparison. Cards in a row are a set; a set has one bottom edge.

   Applied to the wrapper ExtJS renders for a `layout: 'column'` container
   rather than to the cards, so nothing has to know its own position: the
   wrapper becomes a wrapping flex row and each card claims half of it minus the
   gutter, which is the arrangement the floats were approximating. `stretch` is
   the default `align-items`, and it is the whole point - it is what a float
   cannot express.

   `:has()` guards it to the wrappers that actually hold these cards; the same
   classes are used inside plain `.contentbox` HTML on Step 2, where there is no
   column layout and the floats still apply. A browser without `:has()` drops
   the whole block and gets exactly the previous behaviour. */
.x-column-layout-ct > * > *:has(> .contentbox.omicSummaryBox),
.x-column-layout-ct > * > *:has(> .contentbox.metaboliteBox) {
	/* !important, and it is the one declaration here that needs it: ExtJS writes
	   `display: table-cell` on this wrapper as an inline style, which no
	   stylesheet selector can outscore. Without it the wrapper stays a table
	   cell, the cards below lose their 49% to `width: auto` and each one takes
	   the full column - measured, on the first attempt at this rule. */
	display: flex !important;
	flex-wrap: wrap;
	align-items: stretch;
	/* 22px is what `100% - 2 × 49%` came to at this column width, so the gutter
	   between the cards does not change. */
	gap: 10px 22px;
}
.x-column-layout-ct > * > *:has(> .contentbox.omicSummaryBox) > .contentbox,
.x-column-layout-ct > * > *:has(> .contentbox.metaboliteBox) > .contentbox {
	float: none;
	width: auto;
	/* Two per row, and a third wraps to the next one at the same width - which
	   is what the odd/even floats did. */
	flex: 1 1 calc(50% - 22px);
	min-width: 0;
	margin: 0;
}

#mainViewCenterPanel.mobileMode div.omicSummaryBox,
#mainViewCenterPanel.mobileMode div.metaboliteBox,
div.omicSummaryBox.expandedBox, div.metaboliteBox.expandedBox{
	width: 100%;
}

/* "Multiple databases used", "Configure the number of clusters" and the
   metabolite-threshold card each take a full row of the step-2 flex layout.
   They used to share a row under `align-items: stretch`, so the short config
   card was stretched to the height of the two database tables beside it - a
   column of white with four fields in its top corner. On its own row a card
   is exactly as tall as its content. The id outranks the class shorthand's
   `flex-basis` above; grow/shrink from that rule still apply. */
#dbs_message, #clusternumber_box, #threshold_box {
	flex-basis: 100%;
}
/* The empty "parity pad" containers Step 2 inserts after each full-width card
   exist for one reason: browsers without :has() lay the cards out with the
   odd/even floats, and the pads keep that parity intact. In the flex layout
   they are actively harmful - a zero-width flex item still owns a 22px gap on
   each side, so the row after the pads packed as [pad, pad, card] and the
   card sat 44px off the left rail with its partner wrapped to the next row.
   Hide them exactly where the flex layout applies: behind the same :has()
   support gate. !important, because they are ExtJS containers and this must
   win whatever the layout writes. */
@supports selector(:has(*)) {
	.x-column-layout-ct .paLayoutPad { display: none !important; }
}
/* The config fields inside the two full-width cards stay centred. The form
   panel itself is stretched to the card by its vbox, and each field row is a
   600px table that ExtJS positions with inline `left: 0; margin: 0` - the
   config's `margin: 0 auto` never survives - so at full card width the fields
   hugged the left edge. left:50% + translateX(-50%) centres each row under
   any card width, and the !important is what lets a stylesheet outrank the
   inline left. All rows share the same 600px width, so their labels still
   align with each other. */
#clusternumber_box .x-form-item,
#threshold_box .x-form-item {
	left: 50% !important;
	transform: translateX(-50%);
}
/* ...and the 50% must resolve against the real card, not ExtJS's memory of it.
   The panel body and box containers carry inline pixel widths measured before
   the flex rules widened the card (815px inside a 1358px panel, observed), and
   ExtJS does not re-measure on a CSS-driven size change. Stretching the chain
   to 100% makes the centring above resolve against the card's true width. */
#clusternumber_box .x-panel-body.divForm,
#threshold_box .x-panel-body.divForm,
#clusternumber_box .divForm .x-box-inner,
#threshold_box .divForm .x-box-inner,
#clusternumber_box .divForm .x-box-target,
#threshold_box .divForm .x-box-target {
	width: 100% !important;
	margin-left: 0 !important;
	margin-right: 0 !important;
}

/* The step-2 illustration of the pathway-view toolbar. It is drawn with the
   toolbar's own markup and .button rules rather than a screenshot, so it
   cannot fall out of date the way settingsbutton.png (2022) did - a restyle
   of the real buttons restyles this figure in the same stroke. Not
   interactive: it is a picture that happens to be made of markup. */
.paToolbarMiniature {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 8px;
	margin: 15px auto 5px;
	pointer-events: none;
}
/* The one button the caption is pointing at. */
.paToolbarMiniature .paMiniatureTarget {
	box-shadow: 0 0 0 2px #B3261E;
}
/* The row's spacing is the flex gap alone. `.button` brings `margin: 0 5px`
   from the shared rule, and five buttons' worth of those margins (50px) is
   exactly what pushed "Go back" onto a second row at laptop card widths while
   every width sum said the row fit. */
.paToolbarMiniature .button {
	margin: 0;
}
/* The figure must stay one row: the real toolbar it depicts is one row, and
   "Go back" wrapping underneath on a narrow laptop turned the picture into a
   layout it was never illustrating. The row needs ~500px at full size, so on
   a narrower card the buttons shrink typographically - smaller type, tighter
   padding - long before the wrap fallback above could fire. Sized by the
   card, not the viewport: the summary cards reflow between one and two
   columns, so viewport width says nothing about the space this figure has.
   inline-size containment only, on the card itself - the same recipe as the
   MORE toolbar column fix - so ExtJS keeps control of the height. */
.omicSummaryBox {
	container-type: inline-size;
}
@container (max-width: 560px) {
	.paToolbarMiniature { gap: 4px; }
	.paToolbarMiniature .button {
		height: 26px;
		padding: 0 8px;
		gap: 4px;
		font-size: 11px;
	}
}
@container (max-width: 430px) {
	.paToolbarMiniature .button {
		height: 24px;
		padding: 0 6px;
		font-size: 10px;
	}
}

/********************************************************************************
** REGISTRATION FORM
*********************************************************************************/
#dataProtection {
	height: 80px;
	width: 100%;
	overflow: auto;
}







.hidden {
  display: none;
}

/* =============================================
   PaintOmics Landing Page - Modernized Styles
   ============================================= */

/* Hero Section ---------------------------------------------------------------

   The first thing anyone sees, and previously the flattest thing on the page: a
   product name set in the lightest weight this family ships, a 24-word nominal
   phrase under it, and the AI feature - the reason this release exists - filed
   into a bordered box at 13px grey.

   Three changes, all typographic. This is research software and the hero is not
   the product, so nothing below adds a gradient, a texture, an entrance
   animation or a device of any kind; the element count, the block order and the
   height are the ones that were already here.

   1. The title is set in the weight it was always meant to be. When this was
      written the family was vendored as two files, MyriadPro-Light at
      `font-weight: normal` and MyriadPro-Semibold at `bold`, so
      `font-weight: 400` was not "regular" - it was Light, literally the
      lightest thing available, and 700 was the first weight that rendered
      differently at all. That is why the title asks for 700 here.

      The family now ships real 400, 600 and 700 cuts (see the @font-face rules
      at the top of this file), so the gap this was working around is gone and
      400 means regular everywhere. The 700 stays because a 52px product name
      wants it, not because it is the only thing that moves.

   2. Scale. The ramp was 46 / 16.5 / 13 - three sizes inside one octave, which
      reads as one size slightly disturbed rather than as a hierarchy. 52 / 17 /
      13 steps properly, and costs six pixels of height.

   3. The AI block stops being a callout. Same three lines, same box, but the
      contents are sized to be read rather than to be got past.
   --------------------------------------------------------------------------- */
.po-hero-section {
  /* Vertical padding unchanged - the hero is the same height it was; see the
     note on .po-hero-ai-highlight for where the taller title is paid for.

     Horizontally this was a hardcoded 30px while every other block on the page
     is inset by --pa-card-inset (26px). Four pixels is not the interesting
     part; sharing the token is, because it is what keeps the hero on the rail
     when the token moves. */
  padding: 40px var(--pa-card-inset);
  /* Was `2px solid #e0e0e0`: a hardcoded light grey at twice the weight of any
     other rule in the application, and one dark.css never restated - so on the
     dark theme a bright 2px bar ran the full width under the hero. --pa-border
     is an alpha of the ink and is redefined per theme, so the one declaration
     is right on both. */
  border-bottom: 1px solid var(--pa-border);
  /* Anchors the diagram's plate below. */
  position: relative;
}

/* No `max-width: 1200px; margin: 0 auto` here any more, and that pairing is
   worth naming because it is a plausible thing to put back.

   Centring a block inside a column that is itself already centred gives the
   page two left edges instead of one, and the second is not at a fixed offset
   from the first - it is half of whatever the column has left over, so it
   *moves with the viewport*. Measured at 1512px: the card's inset put every
   other block's text at x=103, while this rule put the hero's at x=156, a 53px
   drift no screenshot names because the two blocks are hundreds of pixels
   apart and a hero that is slightly indented looks like a decision.

   The page column is already capped at --pa-page-max and centred once, on
   #mainViewCenterPanel. One cap, one centring, one rail. The overlay in
   app/view/common/AlignmentGuides.js reports the rail count directly - it went
   from three to one on this change. */
.po-hero {
  display: flex;
  flex-direction: row;
  gap: 44px;
  align-items: center;
}

.po-hero-text {
  flex: 1;
  min-width: 0;
}

/* .po-hero-badge and `.po-hero h1 span` are gone with the markup that used
   them - the uppercase eyebrow above the title and the version number set
   beside the product name. See the note in PA_Step1Views.js. The version is not
   drawn anywhere on the page now: the header chip that used to carry it was
   removed too, for the space it cost the nav. It lives in APP_VERSION. */

.po-hero h1,
.po-hero-title {
  /* See note 1 above: 700 is the only weight in this family with a face of its
     own. Tight tracking because a semibold at this size needs it - the default
     spacing was drawn for text, not for a headline. */
  font-size: clamp(34px, 3.8vw, 52px);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.04;
  color: var(--pa-ink-title, #1A1A18);
  margin: 0 0 10px;
}

/* The blue every AI surface in the application uses, on the one word of the
   name that is new in this release. 3.34:1 on white, which clears WCAG's 3:1
   for large text several times over at this size; the small AI type in the
   panel below uses a darker one. */
.po-hero-title .po-hero-ai {
  color: var(--pa-ai-blue, #4A90D9);
}

.po-hero-text .po-hero-desc {
  /* Half a point up from 16.5, and capped by characters rather than left to run
     the width of the card - two lines of about 70, which is the same two lines
     the sentence it replaces occupied. */
  font-size: 17px;
  line-height: 1.55;
  color: var(--pa-ink-body, #555);
  max-width: 64ch;
  margin: 0 0 14px;
  padding-left: 0;
  padding-right: 0;
}

/* The AI panel ---------------------------------------------------------------
   Same box, same three lines, same left bar. What changed is inside it: the
   body was 13px #666, one step below the smallest type anywhere else on this
   page, for the paragraph describing the feature the release is named after.
   14px at --pa-ink-body is the size the step cards use for their own prose.

   The left bar moves from the teal of the primary button to the AI blue, which
   is the colour this application already gives every AI surface, so the strip
   names the panel instead of matching a button two rows below it. */
.po-hero-ai-highlight {
  background: #F4F8FC;
  border: 1px solid #CFE0F0;
  border-left: 3px solid var(--pa-ai-blue, #4A90D9);
  border-radius: var(--pa-radius-sm);
  padding: 10px 18px;
  margin-bottom: 16px;
  /* Capped, but not to --pa-measure as it was. At 105ch the panel ran the full
     1200px of the card - roughly 160 characters a line, long enough that the
     eye loses the return, and wide enough that it read as a band across the
     page rather than as part of the column. 78ch is the width at which the
     three verbs below sit on one line, which is what keeps this block, and so
     the hero, the height it was before. */
  max-width: 78ch;
}

.po-hero-ai-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.po-hero-ai-highlight .po-ai-icon {
  display: inline-flex;
  /* The mark was inheriting body ink, so the one graphic that stands for the
     feature was drawn in the same grey as the sentence beside it. */
  color: var(--pa-ai-blue, #4A90D9);
  flex-shrink: 0;
}

.po-hero-ai-highlight strong {
  /* Not the #4A90D9 the mark uses: this is bold body text, which WCAG does not
     count as large, so it needs 4.5:1 rather than 3:1. Same hue, darker.

     This used to read `var(--pa-ai-blue-ink, #1F6BC1)` and say it was carrying
     --pa-accent-blue's measured value. It was not carrying anything:
     --pa-ai-blue-ink is declared in no stylesheet, so the fallback was the
     value, and it went on being #1F6BC1 after --pa-accent-blue moved. Naming
     the token it claims to follow is what makes that claim true. */
  color: var(--pa-accent-blue);
  font-size: 14.5px;
  font-weight: 700;
}

/* Two class selectors, not one. `div.contentbox p` is (0,1,2) and adds
   --pa-card-inset to every paragraph inside a card; a bare
   `.po-hero-ai-highlight p` is (0,1,1) and loses to it, which left this
   paragraph indented 26px from the heading above it and the verbs below it.
   The same trap is noted on .po-hero-desc further down this file. */
.po-hero-text .po-hero-ai-highlight p {
  color: var(--pa-ink-body, #555);
  font-size: 14px;
  line-height: 1.5;
  margin: 6px 0 0;
  padding-left: 0;
  padding-right: 0;
  max-width: none;
}

/* The three things the agent does. Inside the sentence they were a comma list,
   which is read as one clause; on their own line they are read as three. The
   line is free - the sentence lost exactly the one they were taking up in it. */
.po-hero-ai-does {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
}

/* Two classes again, for the reason given on the paragraph above: the rule that
   would otherwise win is `div.contentbox li`, at (0,1,2).

   `padding` in full and not `padding-left`. That rule is `padding: 2px 26px`,
   so overriding only the left edge left every one of these carrying 26px of
   dead space on its right - measured, the three came to 492px inside a 503px
   row, and the third wrapped to a second line for the sake of padding nobody
   can see. Zeroed, they are 414px and the row is one line. */
.po-hero-text .po-hero-ai-does li {
  position: relative;
  padding: 0 0 0 13px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--pa-ink-muted, #595959);
  max-width: none;
}

/* A small square in the AI blue, drawn rather than typed so it is not selected
   with the text and not read out as a character. */
.po-hero-ai-does li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 5px;
  height: 5px;
  border-radius: 1px;
  background: var(--pa-ai-blue, #4A90D9);
}

.po-hero-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}

.po-hero-actions a {
  display: inline-flex;
  align-items: center;
  padding: 8px 20px;
  border-radius: var(--pa-radius-btn);
  font-size: 13.5px;
  font-weight: 700;
  text-decoration: none;
  /* Every button in the row is the same height because every button in the row
     has the same border box. Only the outline and quiet variants below declared
     a border, so the filled primary - the first button in the row, and the one
     the eye squares the others against - rendered 33px tall beside three 35px
     siblings sharing its centre line. A transparent border is invisible on a
     filled button (the background paints under it) and costs the label nothing;
     what it buys is that the variants differ in colour alone. */
  border: 1px solid transparent;
  transition: background var(--pa-transition), border-color var(--pa-transition),
              color var(--pa-transition);
}

.po-hero-actions .po-btn-primary {
  background: #007383;
  color: #fff;
}

.po-hero-actions .po-btn-primary:hover {
  background: #005f6b;
}

.po-hero-actions .po-btn-outline {
  background: transparent;
  border: 1px solid var(--pa-border);
  color: var(--pa-ink-body, #555);
}

.po-hero-actions .po-btn-outline:hover {
  border-color: #007383;
  color: #007383;
}

/* GitHub and Contact. Both are real destinations and neither is what this page
   is for, so they are links rather than a third and fourth button competing
   with the two beside them. */
.po-hero-actions .po-btn-quiet {
  background: transparent;
  border: 1px solid transparent;
  color: var(--pa-ink-muted, #595959);
  padding-left: 12px;
  padding-right: 12px;
}

.po-hero-actions .po-btn-quiet:hover {
  color: #007383;
  background: rgba(0, 115, 131, 0.07);
}

/* 460px, not the 340 the raster had. The diagram's viewBox is 460 wide, so at
   this width one SVG unit is one CSS pixel and its 11px labels are drawn at
   11px. Shrink the column and every label shrinks with it. */
.po-hero-visual {
  flex: 0 1 460px;
  min-width: 0;
  position: relative;
}

/* The diagram's plate ---------------------------------------------------------
   Every other diagram on this page sits on one: the three "How it works" cards
   put theirs on `.po-step-art`, #F6F5F0 at --pa-radius. The hero's was the only
   drawing in the application floating directly on the card, which is what made
   the row read as two unrelated things beside each other rather than as one
   composition - a column of text, and some artwork.

   Full-bleed to the top, right and bottom of the hero rather than boxed. The
   negative offsets are exactly the section's own padding, so the plate finishes
   on the card's edge and is clipped by its radius (`.contentbox` is
   `overflow: hidden`); rounded on the left only, where it meets the text.

   Costs no height - the plate is drawn behind a row whose height the diagram
   already sets. */
.po-hero-visual::before {
  content: "";
  position: absolute;
  top: -40px;
  bottom: -40px;
  right: calc(-1 * var(--pa-card-inset));
  left: -24px;
  background: #F6F5F0;
  border-radius: var(--pa-radius) 0 0 var(--pa-radius);
}

/* Above the plate. Without this the SVG paints under it and the diagram
   disappears. */
.po-hero-viz {
  position: relative;
}

.po-hero-flow {
  display: block;
  width: 100%;
  height: auto;
  font-family: var(--pa-font-sans);
}

/* Kept for the reverse skew of the one above: this stylesheet is cache-busted
   and PA_Step1Views.js is not, so a returning browser can pair this file with a
   twelve-hour-old view that still emits the <img>. Without these rules that
   image would lay out at its natural 1622px and burst the hero. */
.po-hero-visual img {
  width: 100%;
  border-radius: 4px;
  cursor: pointer;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s;
}

.po-hero-visual img:hover {
  transform: scale(1.02);
}

/* The raster is still the better asset when the full detail matters, so it
   keeps its click-to-open behaviour -- as a named link rather than as an
   unlabelled image nobody would think to click. */


/* Running-job progress --------------------------------------------------------
   A job takes minutes, and the only sign it was alive was a spinning GIF with
   two lines of raw seconds under it. A bar answers the question the reader
   actually has - how much longer - without making them do the division. */
.paProgress {
  /* Squared to the dialog's own rail, not centred in it. The dialog body is
     448px wide and the bar was capped at 400 and centred, so it began 24px
     right of the heading above it and ended 24px short of that heading's right
     edge - and because the inset came from `auto` margins rather than from a
     declared one, it was a different inset at every dialog width. The bar is
     the one element on this surface a reader watches, and it was the only one
     not lined up with anything.

     The elapsed/ETA line under it is a flex row squared to the same two edges,
     so the whole block now reads against the text above it. */
  margin: 18px 0 4px;
}
.paProgressTrack {
  height: 6px;
  border-radius: 999px;
  background-color: #E8E8EC;
  overflow: hidden;
}
.paProgressFill {
  height: 100%;
  width: 2%;
  border-radius: 999px;
  /* No fallback: --pa-accent-blue is defined unconditionally at the top of this
     file, and the value that used to sit here as one was #2C7FDD - the exact
     shade the contrast audit replaced, at 4.05:1. A fallback that can only ever
     apply if the token disappears, and that reinstates the rejected colour when
     it does, is worse than none. */
  background-color: var(--pa-accent-blue);
  /* Matches the status poll, so the bar walks forward rather than stepping. */
  transition: width .6s linear;
}
/* No usable estimate, or the estimate has been spent: a stripe that keeps
   moving says "working" without claiming to know how far along it is. */
.paProgressIndeterminate .paProgressFill {
  width: 34% !important;
  transition: none;
  animation: paProgressSlide 1.5s ease-in-out infinite;
}
@keyframes paProgressSlide {
  0%   { margin-left: -34%; }
  100% { margin-left: 100%; }
}
.paProgressMeta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 8px;
  color: #71717A;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.paProgressMeta .paProgressEta {
  color: #52525B;
}
@media (prefers-reduced-motion: reduce) {
  .paProgressFill { transition: none; }
  .paProgressIndeterminate .paProgressFill { animation: none; margin-left: 0; }
}

/* The example picker -----------------------------------------------------------
   The first dialog most people open, and the first thing after the landing page
   that a new reader sees - so it is the point where the level-one surfaces hand
   over to the application, and it was where the handover showed. Seven framed
   ExtJS panels, each with a grey title band, an inner body box and a docked
   toolbar under it holding a single button; see the note in PA_Step1Views.js.

   Rebuilt as cards on the language the rest of the app uses: one hairline, one
   radius, one inset, a hover that lifts the border rather than the card. The
   markup is plain HTML, so everything below is ordinary layout.

   The window's title comes up to the size of the content it introduces -
   Neptune's header text is 13px, which on a 760px dialog full of 15px card
   titles read as a caption above the thing rather than as its name. */
.po-example-window .x-window-header .x-header-text,
.po-example-window .x-window-header .x-panel-header-text {
	font-size: 15px;
	font-weight: 600;
}
.po-example-intro p {
	margin: 18px 0 6px;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--pa-ink-body, #3F3F46);
	max-width: var(--pa-measure);
}
/* The pipeline heading - "Multi-omic pathway analysis", "Region-based omics".
   These group the cards below them, so they take the group-label treatment the
   settings panels use rather than being a fourth size of heading. */
.po-example-group h3 {
	margin: 26px 0 2px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--pa-ink-muted);
}
.po-example-group p {
	margin: 0 0 10px;
	font-size: 12.5px;
	color: var(--pa-ink-muted);
	max-width: var(--pa-measure-note);
}
.po-example {
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
	background: #FFFFFF;
	padding: 16px 18px;
	margin-bottom: 10px;
	transition: border-color var(--pa-transition), box-shadow var(--pa-transition);
}
.po-example:hover {
	border-color: #C4C4CC;
	box-shadow: var(--pa-shadow);
}
/* Title and badges on one line. `wrap` so a long dataset name pushes the badges
   down rather than squeezing them; `baseline` so the 11px chips sit on the
   title's baseline instead of floating in the middle of its line box. */
.po-example-head {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: 8px;
	margin-bottom: 8px;
}
.po-example-title {
	margin: 0;
	font-size: 15px;
	color: #1A1A18;
	flex: 1 1 auto;
}
/* The badges were inline `style=` attributes on a span, which is also why they
   could not follow the dark theme. Same two fills, stated once. */
.po-example-badge {
	flex: 0 0 auto;
	border-radius: 999px;
	padding: 2px 9px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.02em;
	white-space: nowrap;
}
.po-example-badge-sim {
	background: #E8F1FA;
	color: #2F73BC;
}
.po-example-badge-real {
	background: #FDF0E6;
	color: #B4690E;
}
.po-example-badge-default {
	background: var(--pa-surface-quiet);
	color: var(--pa-ink-muted);
}
.po-example-summary {
	margin: 0 0 10px;
	font-size: 13.5px;
	line-height: 1.55;
	color: var(--pa-ink-body, #3F3F46);
	max-width: var(--pa-measure);
}
.po-example-facts {
	margin: 0;
	font-size: 12.5px;
	color: var(--pa-ink-muted);
}
.po-example-facts b {
	color: inherit;
	font-weight: 600;
}
.po-example-dot {
	padding: 0 8px;
	color: #C4C4CC;
}
.po-example-tests {
	margin-top: 12px;
	padding-top: 12px;
	border-top: 1px solid var(--pa-border);
}
.po-example-tests h5 {
	margin: 0 0 6px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--pa-ink-muted);
}
/* Two columns where there is room: these lists run to five short items, and a
   single column of them made the card twice as tall as its own prose. */
.po-example-tests ul {
	margin: 0;
	/* Hung markers, like every other list this design draws (the hero's AI
	   panel is the pattern): the item's box starts on the card rail and the
	   bullet is drawn inside the indent the text steps past. With the ul
	   carrying padding-left instead, every item's box sat 18px right of the
	   prose above it - one rail per card was the whole point of these cards. */
	padding: 0;
	list-style: none;
	columns: 2;
	column-gap: 28px;
	font-size: 12.5px;
	line-height: 1.7;
	color: var(--pa-ink-body, #3F3F46);
}
.po-example-tests li {
	break-inside: avoid;
	position: relative;
	padding-left: 18px;
}
/* Drawn, not typed, for the reason the hero's squares are: a marker that is
   not a character is not selected with the text and not read out loud. */
.po-example-tests li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.6em;
	width: 5px;
	height: 5px;
	border-radius: 1px;
	background: var(--pa-ink-muted, #595959);
}
.po-example-actions {
	margin-top: 14px;
	display: flex;
	justify-content: flex-end;
}
/* The one action on the card, so it is filled rather than outlined - the reader
   is choosing between cards, not between controls inside one.

   #007383 is `.po-hero-actions .po-btn-primary`, i.e. the "Load an example"
   button on the landing page that opens this dialog. The button you press and
   the button it offers you are the same button. */
.po-example a.button.po-example-load {
	background-color: #007383;
	color: #FFFFFF;
	font-size: 13px;
	font-weight: 600;
	padding: 7px 16px;
	margin: 0;
}
.po-example a.button.po-example-load:hover {
	background-color: #005F6B;
	color: #FFFFFF;
}

/* Upload controls -------------------------------------------------------------
   The four "Browse..." buttons and "Download example data" were the last stock
   controls in the application: a #287AB6 steel-blue fill with white text and a
   #1F608F border, next to the flat neutral controls the results page now uses.
   Nothing else on the upload page is that colour, so they read as the loudest
   thing on a page whose actual call to action is "Run PaintOmics".

   Same size and shape, quieter colour.

   Scoped to .omicbox, the upload panel itself, rather than to
   #submittingPanelsContainer as it first was. That id exists only on step 1,
   so Regions2Genes and miRNA2Genes - which build the same panels from the same
   classes, nine of these buttons on the miRNA page alone - kept the stock blue
   and the restyle only ever reached a third of the controls it describes.
   .omicbox travels with the panel, so it covers all three pages; measured on
   the deployed site, every ExtJS button on each of them is inside one, and no
   other button in the application is. */
.omicbox a.x-btn.x-btn-default-small {
  background-color: #FFFFFF;
  background-image: none;
  border: 1px solid var(--pa-border);
  border-radius: var(--pa-radius-btn);
  box-shadow: none;
}
.omicbox a.x-btn.x-btn-default-small:hover {
  background-color: #F9F8F4;
  border-color: #C4C4CC;
}
.omicbox a.x-btn .x-btn-inner {
  color: #3F3F46;
  font-weight: 500;
}
/* The split arrow and its divider come from the Neptune sprite, drawn light for
   a dark button - on white they disappear, and with them the only sign that
   this button opens a menu. Drawn here instead, so they follow the text colour
   rather than the theme image. */
.omicbox a.x-btn .x-btn-wrap {
  background-image: none;
  position: relative;
}
.omicbox a.x-btn .x-btn-wrap::after {
  content: "";
  position: absolute;
  right: 8px;
  top: 50%;
  margin-top: -2px;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid #71717A;
  pointer-events: none;
}
.omicbox a.x-btn .x-btn-wrap::before {
  content: "";
  position: absolute;
  right: 21px;
  top: 4px;
  bottom: 4px;
  border-left: 1px solid var(--pa-border);
  pointer-events: none;
}
/* About / How It Works Section */
.po-about-section {
  padding: 0;
  margin-top: 4px;
}

.po-about-section h2 {
  font-size: 24px;
  color: var(--pa-accent-orange);
  margin-bottom: 8px;
}

.po-steps-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 34px;
  /* Inset to --pa-card-inset so a column's text starts on the same left edge as
     the "How it works" heading above it. The cards used to carry their own 24px
     padding for this; with the boxes gone there is nothing to pad, so the grid
     positions itself instead. */
  margin: 20px var(--pa-card-inset);
  /* Not `start`, which is what this was. These three cards hold unrelated
     amounts of prose - measured on the deployed page they came out 327, 358
     and 278px tall - so `start` gave a row of peers three different bottom
     edges, 80px apart at the extremes, each with its own teal top rule
     starting level and nothing else agreeing after that.

     `stretch` is the grid default and the right one here: the cards are one
     row of one kind of thing, and the row reads as a row when it has one
     baseline top and bottom. The content stays top-aligned inside, so only
     the white box grows. */
  align-items: stretch;
}

.po-step-card {
  /* Not a card any more, despite the name. These three sat inside the "How it
     works" .contentbox, each with its own border and radius, so the outer
     card's edge and the inner cards' edges ran as two near-parallel lines a
     few pixels apart at the top and bottom of the row - a box drawn inside a
     box, which reads as two blocks rather than one.

     They are columns now: no fill, no border, no radius, no shadow. The
     section is one surface, and the three steps are separated by the 34px
     grid gutter and their own numbered discs, which is enough. The padding
     went with the border - the grid carries --pa-card-inset instead, so a
     column's text lines up with the heading above it. */
  background: transparent;
  border: 0;
  /* Left, not centre. The disc and the heading were centred over a
     left-aligned paragraph, so nothing in the column shared an edge - the
     specific reason this block read as slightly off rather than as designed. */
  text-align: left;
  /* A column, so the diagram can be pushed to the bottom with
     `margin-top: auto`. The three hold unequal prose - measured at 297, 277
     and 238px against a common 335px height - and that difference used to fall
     out as dead space at the foot of each one, 97px of it at worst. The
     picture spends the gap, and all three land on one line across the row. */
  display: flex;
  flex-direction: column;
}


/* The header row: the numbered disc and the step's title on one line, with a
   hairline under them. `margin-bottom` on the disc used to stack them, which
   spent 58px of every column before the first word and left an orange dot
   floating over a heading it had no visible relationship to. */
.po-step-head {
  display: flex;
  align-items: center;
  gap: 11px;
  padding-bottom: 11px;
  margin-bottom: 14px;
  /* The one horizontal line the three columns share at the top. Three of them
     at an identical y is what makes the row read as one row of three peers
     rather than three stacks that happen to be side by side. */
  border-bottom: 1px solid var(--pa-border);
}

/* The stage diagram at the foot of each card. `margin-top: auto` is what puts
   all three plates on one line: it eats whatever vertical slack the column has
   left, and the columns are stretched to a common height by the grid.

   A plate, not a loose drawing. Measured on the deployed page the three
   diagrams sat 33, 56 and 79px below their own last line of prose - three
   different rhythms down one row, which is the specific thing that read as
   "not in order". The gap is now structural: everything above it is prose,
   everything inside it is a picture, and the boundary is in the same place in
   all three columns.

   No border on it. The section is already inside a bordered .contentbox, and a
   second outline a few pixels in from the first is what the step cards lost
   their own boxes to avoid. A fill alone is enough to read as a plate. */
.po-step-art {
  margin-top: auto;
  background: #F6F5F0;
  border-radius: var(--pa-radius);
  padding: 16px;
  min-height: 136px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.po-step-art-svg {
  display: block;
  /* All three diagrams are now one 186x96 frame drawn on one row rhythm, so a
     shared width gives a shared height for free - which the previous
     `height: 104px` had to force, and which still left them 215, 138 and 202px
     wide. Identical boxes, centred in identical plates. */
  width: 100%;
  max-width: 204px;
  height: auto;
  margin: 0;
  /* The labels inside these diagrams are drawn with fill="currentColor" so they
     can follow the theme. They used to be a hardcoded #3F3F46, which is a dark
     grey - fine on the white the diagrams used to sit on, and invisible on the
     dark plate they sit on now. Only the *type* inherits this: the omic row
     tints and the bwr value swatches stay literal in both themes, because they
     are pictures of colours the application really draws. */
  color: #3F3F46;
}

/* The lift-on-hover went with the box. A card that raises itself off the page
   needs an edge and a shadow to raise; on three columns of prose sharing one
   surface it just nudged the text sideways. */

/* 26px, not 34. On its own line the disc was the largest single object in the
   column and had to carry it; sitting beside a 16px heading it is a marker on
   that heading, and a disc more than half again the cap height of the words it
   numbers reads as the more important of the two. */
.po-step-number {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--pa-accent-orange);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 13px;
  /* The heading beside it can wrap to two lines at narrow widths; without this
     the disc is squeezed into an oval by the flex row. */
  flex: 0 0 auto;
  margin-bottom: 0;
}

/* `div.` is load-bearing: this has to out-specify `div.contentbox h3`, which
   these headings also match - the step cards live inside a .contentbox. */
div.po-step-card h3 {
  font-size: 16px;
  /* Was #007383. That teal was the last cold colour on a warm page, and the
     card already carries an accent - the orange disc directly above it. A
     heading and a badge in two different accent hues, on a card whose only
     other colour is body grey, is what makes three of these in a row look
     restless. Near-black, so the disc is the one accent per card. */
  color: var(--pa-ink-title, #1A1A18);
  font-weight: 600;
  /* Zeroed entirely rather than only horizontally. `div.contentbox h3` supplies
     `padding: 6px var(--pa-card-inset)` and `margin: 12px 0 4px`; the inset is
     applied a second time here because the column already carries one, and the
     vertical pair now has to go too - the heading is a flex item centred
     against a 26px disc, so any margin or padding of its own offsets it from
     the disc it is supposed to sit level with. */
  padding: 0;
  margin: 0;
}

/* The same double-inset, on the prose. `div.contentbox p` adds
   --pa-card-inset to every paragraph inside a card, which is right for a
   paragraph sitting directly on a card and wrong twice over here: the step
   card already pads itself by 24px, and the hero runs its own layout.

   Centring hid this. With the heading centred and the text left-aligned there
   was no shared edge to be wrong about, so a 26px indent just read as the way
   the card was. Left-aligning made it visible immediately: measured, the card
   heading started at x=86 and its own paragraph at x=112, and in the hero the
   title sat at 120 with its description at 146. Same 26px in both places.

   Specificity is a tie with `div.contentbox p` in the step-card case, decided
   by source order - which is why this sits below that rule, exactly as the h3
   fix above it does. */
div.po-step-card p,
.po-hero-text .po-hero-desc {
  padding-left: 0;
  padding-right: 0;
}

.po-step-card p,
.po-step-card ol {
  font-size: 13px;
  /* Was #666 (5.74:1). This is the landing page's primary running text - the
     paragraphs that explain what the product does - not secondary metadata,
     so it takes the same #3F3F46 body ink (10.4:1) the rest of the app's prose
     uses rather than a muted grey. A mid grey on the first screen a new user
     reads is the most expensive place in the product to look faint. */
  color: #3F3F46;
  /* 1.6 gave a 20.8px line box, and the inline buttons sitting in this prose
     are 22px tall - so every one of them stuck out of its own line and
     overlapped the line above. In step 3, where two of them land on
     consecutive lines, the paint control and the AI Interpret pill collided
     outright. 1.75 is 22.75px, which clears the tallest of them. */
  line-height: 1.75;
  text-align: left;
}

/* The inline buttons in these cards are pictures of real controls, so they
   should look like the control they name. Two things stopped them:

   The shape. Every real button in the toolbar is a 999px pill; these were 6px
   rectangles, so a sentence saying "click Run PaintOmics" showed a shape that
   appears nowhere in the interface.

   And "Load example" was the wrong colour entirely. The toolbar's own Load
   example is a white outline button - measured, white fill with #24252A ink -
   while its inline portrait was solid green on white. Two controls with the
   same name and nothing in common to look at. `btn-secondary` is right in the
   toolbar, where a later `.btn-right` rule neutralises it; inline there is no
   such rule, so the raw green came through. Restated here rather than by
   changing btn-secondary, which other screens rely on. */
.po-step-card a.button.btn-inline {
  border-radius: var(--pa-radius-btn);
  vertical-align: middle;
}

.po-step-card a.button.btn-secondary.btn-inline {
  background: #FFFFFF;
  color: #24252A;
  border-color: #878787;
}

/* Step 3's two controls, previously carrying their colours in a style
   attribute along with a `font-size: 14px` that is what made them 24px tall
   and overlapping. The colours are unchanged; they are just declared where
   they can be themed and where btn-small's 12px reaches them. */
.po-step-card a.button.btn-paint {
  background-color: #756C6C;
  border-color: #756C6C;
  color: #FFFFFF;
}

/* The "AI Interpret" chip in the third How-it-works card, and the real button
   it is a picture of, are the same action and now the same colour. This was
   #2F73BC -- a third blue, between the accent and the widget's own, belonging
   to neither. */
.po-step-card a.button.btn-ai {
  background-color: var(--pa-ai-action-bg);
  border-color: var(--pa-ai-action-bg);
  color: #FFFFFF;
}
/* Step 3's AI Interpret button. It takes .btn-info, which is the application's
   general action blue -- the same class as Search, Paint and Find in Pathway --
   and in the light theme that resolves to the same #19589E the AI colour does,
   so nothing looked wrong. In the dark theme it does not: --pa-accent-blue
   moves to #6BA8F0 there and this button would drift away from every other AI
   surface, and from "Draft this for me", which is the other button in the
   product that asks a model for something. One fill for both. */
#aiInterpretButton.button.btn-info {
  background-color: var(--pa-ai-action-bg);
  border-color: var(--pa-ai-action-bg);
  color: #FFFFFF;
}
#aiInterpretButton.button.btn-info:hover {
  background-color: var(--pa-ai-action-bg-hover);
  border-color: var(--pa-ai-action-bg-hover);
}

.po-step-card ol {
  padding-left: 20px;
  margin: 8px 0 0;
}

.po-step-card li {
  margin: 4px 0;
}

/* The prose column of the Regions2Genes / miRNA2Genes intro card, beside the
   tool's logo. It used to carry `padding-left: 10px` inline, which put its h4
   on 10px while the paragraph under it sat on --pa-card-inset from
   `div.contentbox p` - a heading and its own body copy on two different left
   edges, each of them a different edge again from the "About input data" card
   directly below. The column now starts at the card's edge and the heading
   takes the same inset the prose already had, so all three agree. */
.po-tool-intro {
  max-width: 600px;
  float: left;
}
.po-tool-intro > h4 {
  padding-left: var(--pa-card-inset);
}

/* AI Section */
.po-ai-section-body {
  background: linear-gradient(to bottom, #f8faff, #f2f6fc);
  border: 1px solid #c8d6e5;
  /* 8px was a fourth corner radius, in a stylesheet whose token block exists
     because three of them read as unfinished. This is a panel, so it takes the
     surface radius like every other panel. */
  border-radius: var(--pa-radius);
  /* 22px of air above and below, not 18: the panel reads as a card now, with
     a switch for a title, and the tighter padding made it look packed. The
     sides stay 22 so the prose keeps its rail. */
  /* 16/20, not 22/22/20. The panels were sized when each held a switch, a
     paragraph and a row of pills; they now hold one field and a margin note,
     and 22px top and bottom on a 90px column is a band of empty colour under
     everything in it. */
  padding: 16px 20px;
  /* --pa-card-inset, not 20px: this callout sits directly under the
     "AI-Powered Pathway Interpretation" heading, so a 6px difference between
     the two left edges is visible as a step. ExtJS's box layout reads this
     margin off the computed style and subtracts it from the stretched width,
     so the right edge follows without a second declaration. */
  margin: 5px var(--pa-card-inset) 10px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  overflow: visible;
}

/* Section 1's panel -- the organism and its databases.

   The same panel as .po-ai-section-body above, minus the colour: identical
   border weight, radius, padding, margin and shadow, so the three sections of
   the upload form read as three panels of one kind. The fill is neutral because
   on this form the blue IS the signal -- both panels wearing it are AI surfaces
   and this one is not. */
.po-form-panel {
  /* --sunken, not --subtle. Subtle is #F9F8F4 and this panel sits on a white
     card: at that lightness the fill was invisible and the section read as
     unstyled again, which is the thing being fixed. */
  background: var(--pa-surface-sunken);
  /* --pa-border is rgba(24, 24, 27, 0.10) -- a hairline for a card on the ivory
     page, and one that vanishes for a panel on a white card. Same ink, one step
     up; dark.css restates it with that theme's own solid line, which needs no
     such help. */
  border: 1px solid rgba(24, 24, 27, 0.14);
  border-radius: var(--pa-radius);
  /* 16/20, not 22/22/20. The panels were sized when each held a switch, a
     paragraph and a row of pills; they now hold one field and a margin note,
     and 22px top and bottom on a 90px column is a band of empty colour under
     everything in it. */
  padding: 16px 20px;
  margin: 5px var(--pa-card-inset) 10px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  overflow: visible;
}

/* The title over a right-hand group. inputformat.css states these four
   declarations for Section 3's "Works with"; this joins that rule rather than
   restating it, so the two titles are one shape and stay one shape.

   The 4px is lead: an ExtJS field label carries that much above its text, so
   without it the title's box starts at y=8 while "Organism:" starts at y=12 --
   two headings on two lines at the top of one panel. Measured after: both at
   y=140. Section 3 needs no such nudge; both of its columns open with prose. */
/* The title is a <p> (see the note in PA_Step1Views), so it needs the card's
   paragraph padding taken back off: `div.contentbox p` insets every paragraph
   by --pa-card-inset, which would put the title 26px right of the column it
   heads.

   It used to share its type with Section 3's "Works with" title, stated once in
   inputformat.css. Section 3 has no aside any more -- its formats are one line
   under its sentence -- so this is the only aside title on the form and it is
   stated where the panel that carries it is styled. */
div.contentbox p.po-aside-title,
p.po-aside-title {
  padding: 0;
  max-width: none;
  font-size: 13px;
  color: var(--pa-ink, #18181B);
  margin: 0 0 8px;
  line-height: 18px;
}

.po-organism-panel .po-aside-title {
  margin-top: 4px;
  /* No bottom margin here, unlike Section 3's copy of this title: what follows
     it is a row of controls that has to sit on the same line as the field in
     the column beside it, not a paragraph. With the 8px the checkboxes started
     at y=155 against a field at y=146 -- half a row low, which is exactly the
     kind of drift that reads as "something is off" without being nameable.
     Measured after: field 146, checkbox row 147. */
  margin-bottom: 0;
}
/* ExtJS renders a label cell even for a field with hideLabel, and pads the body
   cell beside it by 4px -- enough to push every checkbox 4px right of the title
   above them, which is the one rail in this panel a reader can check. Measured
   before: title at x=775, first checkbox at 779. */
.po-database-group td.x-field-label-cell {
  display: none;
}
.po-database-group > tbody > tr > td.x-form-item-body {
  padding-left: 0;
}

/* "Request an organism", under the field.

   It was `Not your organism? <a style="color: rgb(211, 21, 108)">Request a new
   organism</a>.` behind a blue (i) -- a sentence of setup for one action, in a
   magenta that appears in no other link in the application and, being inline,
   was unreachable by dark.css. It is an action, so it is shaped like one, and
   it borrows the chip shape from the databases beside it so the row has one
   vocabulary. */
/* The action and the line that says what it does, on one row -- the shape
   ".po-exp-design-draft" gives "Draft this for me" and its caption in Section 2,
   at the same 14px gap. */
.po-organism-request {
  margin-top: 12px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.po-organism-request-hint {
  font-size: 12px;
  line-height: 1.45;
  color: var(--pa-ink-muted);
  min-width: 0;
}
/* Same box as "Draft this for me" in the panel below: 26px tall, 8px corner,
   12px label. It was 28 against that button's 26 -- close enough to look like a
   mistake rather than a difference, which is what the owner saw. The fill is
   still white and the ink still muted: they are the same size and shape because
   they are the same kind of thing, and different colours because one of them is
   the AI action. */
a.po-ghost-action,
div.contentbox a.po-ghost-action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid rgba(24, 24, 27, 0.16);
  border-radius: var(--pa-radius-btn);
  background: var(--pa-surface);
  font-size: 12px;
  line-height: 16px;
  color: var(--pa-ink-control);
  transition: border-color var(--pa-transition), color var(--pa-transition);
}
a.po-ghost-action:hover {
  border-color: var(--pa-accent-blue);
  color: var(--pa-accent-blue);
}
a.po-ghost-action > i.fa {
  font-size: 11px;
  opacity: 0.7;
}

/* The four databases.

   One checkbox per database, spread evenly across the rest of the row, so the
   group ends where the panel does and "does this organism have Reactome?" is
   answered by looking at a box rather than by reading a sentence under one.
   They used to be a two-column block beside a "Databases:" label, which spent
   300px on four short words and left the rest of the row empty.

   Boxes and labels, and nothing else. A first pass drew each one as a bordered
   chip -- filled when ticked, dashed when unavailable -- and the owner was
   right about it: the border said nothing the checkbox was not already saying,
   and four boxes-inside-a-box on a panel is three frames deep. The state lives
   where a form puts it, in the control and in its ink. It also matches the one
   other checkbox on this form, the AI consent in the panel below, which has
   never been in a box.

   ExtJS renders a CheckboxGroup as a table of tables: td.x-form-check-group per
   column, and inside each a table.x-form-type-checkbox carrying the state
   classes (x-form-cb-checked, x-item-disabled). Hence the selectors. */
.po-database-group table.x-table-plain {
  /* The four fill the row they are in: one cell each, so the names sit at even
     intervals across the section rather than in a 2x2 block with a gap down the
     middle of it. The cap this carried existed for that 2x2 arrangement. */
  width: 100%;
  /* Fixed, so the four columns are four equal widths and the four checkboxes
     start on four evenly spaced rails. Left to auto the table sizes each column
     to its own label, and "KEGG required" against "MapMan" put them at four
     arbitrary positions. */
  table-layout: fixed;
}
.po-database-group td.x-form-check-group {
  padding-right: 12px;
  vertical-align: top;
}
.po-database-group td.x-form-check-group:last-child {
  padding-right: 0;
}
/* Neptune fades a disabled field to 30%, which is right for a control that is
   temporarily unavailable and wrong here: KEGG is disabled because it is always
   on, and a database that is not installed still has to be readable to say so.
   Both are stated in ink instead. */
.po-database-group .x-item-disabled,
.po-database-group .x-item-disabled .x-form-cb-label {
  opacity: 1;
}
.po-database-group .x-form-cb-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  /* The row is the height of the field beside it and the box is centred in it,
     so the two controls share a centre line rather than merely both being "near
     the top" of their columns. Stated as a min-height rather than as padding:
     the framework's own cell already carries vertical padding, and adding to it
     moved the row down instead of centring what is in it. */
  padding: 0;
  min-height: 24px;
}
.po-database-group .x-form-cb-wrap > .x-form-cb-label {
  /* ExtJS writes the label width inline. */
  width: auto !important;
  margin: 0;
  font-size: 13px;
  line-height: 16px;
  color: var(--pa-ink-control);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.po-database-group .x-form-cb-wrap > .x-form-checkbox {
  flex: 0 0 auto;
  /* Neptune gives the box `margin: 5px 0 0` to sit it on a text baseline in the
     table cell it normally lives in. In a centred flex row that margin is the
     centring, applied twice: measured, the box landed 7px from the top of a
     24px row and 1px from the bottom. */
  margin: 0;
}
/* Available but not ticked reads as ordinary; not installed reads as absent. */
.po-database-group td.x-form-check-group > table.x-item-disabled:not(.x-form-cb-checked) .x-form-cb-label {
  color: var(--pa-ink-muted);
}
.po-database-group td.x-form-check-group > table:not(.x-item-disabled) .x-form-cb-wrap {
  cursor: pointer;
}
/* "required" on KEGG, "not installed" on a database this organism has not got:
   a note inside the label, a step down from the name it qualifies. */
.po-db-tag {
  font-size: 11px;
  font-weight: 400;
  color: var(--pa-ink-muted);
}

/* The "Request an organism" dialog, opened by the pill above.

   One line of introduction and two fields, so the body needs nothing but a
   reading size and some air under the sentence. The <h2> it used to open with
   is gone: it repeated the window's own title in the card heading's terracotta,
   at 23px, inside a 600px dialog. */
.po-dialog .po-dialog-intro p {
  margin: 0 0 16px;
  padding: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--pa-ink-body, #3F3F46);
}
.po-dialog .x-form-item-label {
  font-size: 12px;
  color: var(--pa-ink-label);
}
/* One action, one way out. Neptune paints every window button the same filled
   blue, so "Send request" and "Cancel" carried identical weight -- two primary
   buttons, which is no primary button. The request keeps the fill; the way out
   steps down to an outline, which is the pair this application uses everywhere
   else it asks for something.

   Both selectors carry .po-dialog so nothing outside this window moves, and
   .x-btn-default-small is stated on the same rule to beat the ramp declared for
   it further down this file. */
.po-dialog .po-dialog-primary.x-btn-default-small {
  background-color: var(--pa-accent-blue);
  border-color: var(--pa-accent-blue);
}
.po-dialog .x-btn-default-small:not(.po-dialog-primary) {
  background-color: var(--pa-surface);
  border-color: rgba(24, 24, 27, 0.20);
}
.po-dialog .x-btn-default-small:not(.po-dialog-primary) .x-btn-inner {
  color: var(--pa-ink-control);
}
.po-dialog .x-btn-default-small:not(.po-dialog-primary):hover {
  background-color: var(--pa-control-active-bg);
  border-color: rgba(24, 24, 27, 0.30);
}

/* The one line this section can still print: the installed-database list could
   not be read at all. Empty, and invisible, in every other case. */
div.contentbox p.po-db-note,
p.po-db-note {
  margin: 10px 0 0;
  padding: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--pa-ink-muted);
  max-width: none;
}
/* The "Available omics / Selected omics / Help" row.

   The row carries the whole card inset and the two outer columns carry none on
   their outer side (see the padding/margin configs on #availableOmicsContainer
   and #additionalInfoContainer, and the margin on .availableOmicsBox below), so
   the first drag source and the right edge of the Help card land on the same
   two lines as every heading above them. The 10px those columns still carry on
   their inner sides is the gutter between the three. */
.po-step1-omics-row {
  margin-left: var(--pa-card-inset);
  margin-right: var(--pa-card-inset);
}

/* The two column titles, "Available omics" and "Selected omics".

   They were 23px terracotta h2s carrying `text-align: center` inline - the same
   size and colour as "Data uploading", the heading of the whole card they sit
   inside, so the section shouted over its own owner three times. And they were
   centred over columns in which nothing else is: the drag chips, the omic
   cards, the fields inside them and the help note beside them are all set from
   the left, so each title floated free of the rail its own contents keep.

   They label the columns under them, which is the same job the "HELP" label
   does in the third column and the group labels do in the settings panels, so
   they take that treatment and the row ends up with three headings that match. */
/* `div.` and the tag both needed: `div.contentbox h2` is (0,1,2) and owns the
   26px card inset these would otherwise take. A column label belongs on the
   outer edge of the cards it labels - and the two columns put that edge in
   different places, which is why the inset is set per column below rather than
   once here.

   The flat `padding: 0 10px` this used to carry was measured against the
   Selected column alone, where `#submittingPanelsContainer` insets its cards by
   10px and the label duly landed on them at x=405. The Available column insets
   nothing: `.availableOmicsBox` declares `margin: 10px 0` precisely so the first
   drag chip starts on the page rail with the headings above it. The same 10px
   therefore pushed "Available omics" to x=145 over chips at x=135 - the one
   column label on the row not sitting on the thing it labels, and the more
   visible of the two because the chips are solid blocks of colour whose edge the
   eye reads as the rail. */
div.contentbox h2.po-omics-col-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--pa-ink-muted);
  background: none;
  border-bottom: 0;
  padding: 0;
  margin: 6px 0 6px;
}
/* The Selected column's own inset, taken from the cards it labels rather than
   from a number: `#submittingPanelsContainer` carries `margin: 10`, so its
   `.omicbox` cards start 10px inside it and the label follows them there.
   Measured after: Available title glyph and `.availableOmicsBox` both at x=135,
   Selected title glyph and `.omicbox` both at x=405. */
#submittingPanelsContainer h2.po-omics-col-title {
  padding-left: 10px;
}
/* An <i> is display:inline-block, which UAX#14 makes a line-break opportunity,
   so "…do not need with 🗑." was breaking between the icon and its full stop
   and leaving the stop alone at the start of the next line. */
.po-nowrap {
  white-space: nowrap;
}

.po-ai-section-body p {
  word-wrap: break-word;
  overflow-wrap: break-word;
  white-space: normal;
}

/*****************************************************************************
**** VENDORED EXTJS CONTROLS *************************************************
*****************************************************************************/
/* index.html loads ext-theme-neptune-all.css at line 28 and this file at line
   34, so everything below wins at equal specificity - no `!important`, and no
   edits to the vendored theme, which any ExtJS upgrade would overwrite.

   Two problems, one fix. Neptune paints its default button with a four-stop
   vertical gradient plus a GIF fallback, which is the single most dated surface
   left in the app; and its #3892d3 fill carries white at 3.38:1, the last
   open WCAG failure from entry 2 of proposed-changes.md, deferred at the time
   precisely because the fill could not be changed without also owning the
   hover, focus, pressed and disabled states. This owns all of them.

   The ramp keeps Neptune's own direction - each state darker than the last -
   and its hue, so a Browse... button still reads as the same control. It just
   clears AA at every step now instead of only when held down:

     base     #3892d3 3.38:1  ->  #287AB6 4.62:1
     over     #3386c2 3.94:1  ->  #236FA8 5.37:1
     focus    #3386c2 3.94:1  ->  #236FA8 5.37:1
     pressed  #2a6d9e 5.56:1  ->  #1D5C89 7.13:1

   `background-image: none` is what removes both the gradient and the GIF. */
.x-btn-default-small {
	background-image: none;
	background-color: #287AB6;
	border-color: #1F608F;
	border-radius: var(--pa-radius-btn);
}
.x-btn-default-small-over,
.x-btn-default-small-focus {
	background-image: none;
	background-color: #236FA8;
	border-color: #1A5279;
}
.x-btn-default-small-pressed,
.x-btn-default-small-menu-active {
	background-image: none;
	background-color: #1D5C89;
	border-color: #1A5279;
}
/* Disabled keeps Neptune's meaning but loses its gradient GIF, which would
   otherwise be the one place the old treatment survived. An inactive control
   is exempt from the contrast minimum, so this is about consistency only. */
.x-btn-default-small-disabled {
	background-image: none;
	background-color: #8FB4CF;
	border-color: #7CA3C0;
}

/* Combo and file-field wrappers carried the border while the input inside them
   carried the radius, so a square frame sat around a rounded field. The frame
   is the visible edge, so the radius belongs on it. */
.x-form-trigger-wrap {
	border-radius: var(--pa-radius-sm);
}

/* Checkboxes and radios ------------------------------------------------------
   These are the clearest 1.4.11 case in the application and the last one left:
   a checkbox has no label inside it, so its outline is not merely the thing
   that identifies the control, it is the entire control. Neptune draws them
   from a 30x30 sprite whose unchecked outline is #cccccc - 1.47:1 on the
   #F5F5F5 toolbar where the search filters live, and 1.61:1 on white.

   Because the outline is baked into an image, no CSS rule can recolour it. The
   ring is drawn *around* the sprite instead: an outset 1px box-shadow, which
   needs no new asset, leaves the checkmark and the vendored image untouched,
   and costs no layout because a shadow does not take space. #767676 gives
   4.17:1 on the toolbar and 4.54:1 on white, and is the grey already used for
   the segmented control's outline and .btn-default's hover border.

   The ring was a stopgap, and it has been overtaken. It drew a second outline
   *around* a sprite that already had one, so the control read as a box inside a
   box - and once the app's own checkboxes (`div.checkbox`, further up this
   file) were redrawn in CSS, the Step 3 enrichment toolbar ended up showing
   both kinds side by side: a redrawn one for the database filters and a
   ringed sprite for "Regular expression" three inches to its left.

   So the framework's controls take the same drawing. Same 16px box, same 4px
   corner, same 1.5px edge, same accent fill, same tick - the difference between
   an ExtJS checkbox and one this application wrote by hand stops being visible,
   which is the only acceptable outcome when they sit in the same toolbar.

   `background-image` rather than a pseudo-element for the mark: these are
   `<input type="button">`, and a replaced element cannot carry ::before or
   ::after. That constraint is what forced the earlier attempt to hang the tick
   off the wrapping `<td>`; a background image goes on the control itself and so
   also works for the radio, whose mark is a dot and could not have shared a
   single wrapper rule with the checkbox's tick.

   The scale that used to hold this treatment for `.paGridBar` alone is gone
   with it - this is the same set of declarations, applied everywhere the
   framework draws one of these. */
.x-form-checkbox,
.x-form-radio {
	box-sizing: border-box;
	width: 16px;
	height: 16px;
	background-image: none;
	background-color: #FFFFFF;
	background-repeat: no-repeat;
	background-position: center;
	background-size: 100% 100%;
	border: 1.5px solid #8A8A91;
	border-radius: 4px;
	box-shadow: none;
	transition: background-color var(--pa-transition), border-color var(--pa-transition);
}
.x-form-radio {
	border-radius: 50%;
}
.x-form-checkbox:hover,
.x-form-radio:hover {
	border-color: #52525B;
}
/* `background-position` is restated on the checked rules, not just on the base
   one above. Neptune's own `.x-form-cb-checked .x-form-checkbox` sets
   `background-position: 0 -15px` - it is sliding a two-state sprite up to show
   its lower half - and that selector is (0,2,0) against the base rule's
   (0,1,0), so it went on winning after the sprite it was scrolling had been
   replaced. Measured: a filled blue box with the tick parked 15px above it. */
.x-form-cb-checked .x-form-checkbox {
	background-color: var(--pa-accent-blue);
	border-color: var(--pa-accent-blue);
	background-position: center;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M3.6 8.4l2.9 2.9 5.9-6.3'/%3E%3C/svg%3E");
}
.x-form-cb-checked .x-form-radio {
	background-color: var(--pa-accent-blue);
	border-color: var(--pa-accent-blue);
	background-position: center;
	background-image: none;
	box-shadow: inset 0 0 0 3.5px #FFFFFF;
}

/* Data grid -------------------------------------------------------------------
   The pathway table is the densest thing in the application and was built like a
   spreadsheet: 34px rows, 11px type, a 700-weight header in #666 grey, and a
   hard #C0C0C0 rule between every column. Set against the reference table, which
   runs 53px rows and 14px type with hairline separators, it read as cramped
   rather than as dense-by-design.

   Rows go to 40px, written inline per cell in PA_Step3Views.js - short of the
   reference's 53px on purpose, because this table routinely holds 888 rows and
   its job is comparison across many of them, not a shortlist of twenty. That
   part holds: every cell measures 40px plus its border.

   The cell type did not follow, and this comment used to claim it had. Measured
   on the deployed enrichment grid, one row carries three sizes:

     Paint                                   11px  no font-size set anywhere
     database badge, pathway name,           13px  ExtJS Neptune's default
       the class colour bar, both counts
     combined and Reactome-class p-values    12px  set inline, the only columns
                                                   PA_Step3Views.js sizes

   So the table went from a uniform 11px to a mixed 11/12/13, and the 12px is
   real but reaches two columns out of eight. Unifying it is a product call
   rather than a fidelity one - the design system names --pa-text-12 and
   --pa-text-13 for other roles and specifies no grid cell size at all - so it
   is left as found and described accurately instead.

   Here the header stops shouting and the rules soften: the column rule drops
   from #C0C0C0 to the shared hairline, so the eye follows rows - which is how
   this table is actually read - instead of being cut into cells by the stronger
   of the two axes. */
.x-column-header {
	background-color: var(--pa-surface-quiet);
	background-image: none;
	/* Transparent rather than zero-width: ExtJS measures column widths from the
	   rendered box, so removing the border outright shifts every column. This
	   keeps the metrics and takes away the line. */
	border-right-color: transparent;
}
.x-column-header .x-column-header-text {
	/* 700 in #666 was heavy and low-contrast at once - a rare combination to get
	   wrong in both directions. 600 in near-black is lighter on the page and
	   clearer to read: 15.3:1 against the header fill, up from 5.0:1. */
	font-weight: 600;
	color: #1A1A1A;
}
/* No vertical rules at all, which is what both reference tables do. A grid ruled
   on both axes makes the eye read cells; ruled on one it reads rows, and a row
   here is one pathway - the thing being compared. The header band and the row
   hairlines carry all the structure that is actually needed. */
.x-grid-with-col-lines .x-grid-cell {
	border-right-color: transparent;
}
.x-grid-with-row-lines .x-grid-cell,
.x-grid-cell {
	border-bottom-color: var(--pa-border);
}

/* Grid toolbar overflow ------------------------------------------------------
   The Step 3 pathway-enrichment toolbar needs 1753px to lay out its controls.
   Its box is as wide as the grid, so below roughly 1750px of viewport the tail
   of the toolbar is simply cut off - at 1280px that is "Show combined
   p-values", "Configure" and "Download as XLS", i.e. the table's only export.

   None of it was reachable. ExtJS's own answer, `enableOverflow`, collapses the
   surplus into a `»` menu, but it re-renders those items into a floating menu
   attached to the document body, and their click handlers are bound with
   jQuery against the *grid's* element ($("#" + this.el.id + " a.downloadXLS")
   in ExtJS_extensions.js). Moving them would leave the menu entries inert, so
   the download would look present and do nothing - worse than being clipped.

   Wrapping is not available either: the box layout positions every toolbar item
   absolutely with a `left` computed in JavaScript, so no flex-wrap rule can
   reach them.

   Scrolling the box is what is left, and it is the option that keeps every
   element exactly where its handler expects it. `auto` means the bar appears
   only when the toolbar actually overflows, so wide viewports are untouched;
   the scrollbar is styled because the platform's overlay bar is invisible until
   moved, which is why the clipping went unnoticed in the first place.

   Deliberately not inside a media query: the overflow depends on the toolbar's
   own width, not the viewport's, and it already occurs at 1440px. */
.x-toolbar.x-docked-top > .x-box-inner {
	overflow-x: auto;
	scrollbar-width: thin;
	scrollbar-color: #B0B0B0 transparent;
}
.x-toolbar.x-docked-top > .x-box-inner::-webkit-scrollbar {
	height: 8px;
}
.x-toolbar.x-docked-top > .x-box-inner::-webkit-scrollbar-thumb {
	background: #B0B0B0;
	border-radius: 4px;
}
.x-toolbar.x-docked-top > .x-box-inner::-webkit-scrollbar-track {
	background: transparent;
}

/* Responsive
   Two tiers, and they must stay in this order: at equal specificity the later
   block wins, so the 900px rules below correctly override these once the
   viewport is narrow enough. */

/* The How It Works cards went straight from three-up to a single column. At
   901px that left three ~250px columns of running text; one pixel narrower and
   each became full width. This fills in the missing two-up tier so the step
   down is gradual, and tightens the hero gap, which by this width is spending
   40px of a shrinking row on empty space. */
@media (max-width: 1100px) {
  .po-steps-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .po-hero {
    gap: 24px;
  }
}

@media (max-width: 900px) {
  .po-hero {
    flex-direction: column;
  }
  .po-hero-visual {
    flex: 0 0 auto;
    max-width: 460px;
  }
  /* The plate stops bleeding once the row stacks. Its offsets are the hero's
     own padding, which is right when the diagram is the right-hand column and
     wrong the moment it is the bottom one: -40px at the top would reach up
     behind the action buttons and put a grey panel under them. Boxed and
     rounded on all four corners here - which is exactly what .po-step-art is,
     and this is the width at which those cards go single-column too. */
  .po-hero-visual::before {
    top: 0;
    bottom: 0;
    left: -14px;
    right: -14px;
    border-radius: var(--pa-radius);
  }
  .po-steps-grid {
    grid-template-columns: 1fr;
  }
}

/* ----- Step-2: Replicate detection card ----- */
.repDetectionBox { width: 100% !important; }
.repDetectionCard {
    padding: 14px 16px;
    margin: 0;
    border-top: 1px solid #ececec;
}
.repDetectionCard:first-of-type {
    border-top: none;
    padding-top: 16px;
}
div.repDetectionCard h3 {
    margin: 0 0 6px 0;
    padding: 0;
    background: none;
    border: none;
    font-size: 14px;
    color: #333;
}
.repDetectionSummary { margin: 0 0 6px 0; color: #555; }
.repDetectionWarn {
    color: #b66400;
    background: #fff7e6;
    border-left: 3px solid #b66400;
    padding: 4px 8px;
    margin: 6px 0;
}
.repDetectionPreview {
    list-style: none;
    padding: 4px 8px;
    margin: 6px 0;
    background: #F9F8F4;
    border: 1px solid #ececec;
    border-radius: 3px;
    max-height: 140px;
    overflow-y: auto;
    font-size: 12px;
}
.repDetectionPreview li { padding: 1px 0; }
.repDetectionMore { color: var(--pa-ink-muted); font-style: italic; }
.repDetectionRadios { margin: 8px 0 4px 0; }
.repDetectionRadios label {
    display: inline-block;
    margin-right: 18px;
    padding: 2px 0;
    cursor: pointer;
}
.repDetectionRadios input[type=radio] { margin-right: 6px; }
.repDetectionRadios input[disabled] + * { color: #aaa; }
.repDetectionRecommended { color: #2a8a3a; font-size: 12px; }
.repDetectionManual {
    margin: 6px 0 6px 22px;
    padding: 6px 8px;
    background: #F9F8F4;
    border: 1px solid #ececec;
    border-radius: 3px;
}
.repDetectionFormatHint { font-size: 11px; color: var(--pa-ink-muted); margin: 4px 0 0 0; }
.repDetectionStatus { font-size: 12px; margin-top: 8px; min-height: 14px; }
.repDetectionStatus.repDetectionOK    { color: #2a8a3a; }
.repDetectionStatus.repDetectionError { color: #c0392b; }


/* Reduced motion ------------------------------------------------------------
   Honour the OS-level "reduce motion" setting, which the app currently ignores
   entirely. What moves here is the lateral options panel sliding in, the hero
   image scaling on hover, and the How It Works cards lifting on hover - the
   sort of transform and slide motion that triggers symptoms for people with
   vestibular disorders.

   Durations are collapsed to 0.01ms rather than set to `none` so that any
   `transitionend` or `animationend` listener still fires; killing the
   transition outright would leave such a handler waiting forever. This block
   only applies to users who have actually asked for reduced motion.

   The analysis progress indicator is an animated GIF, which CSS cannot pause,
   so it is swapped for a still frame instead - see below. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Collapsing the duration above only makes these two instant, not absent -
     the image still jumps 2% larger and the card still steps up 2px, which is
     displacement, which is the thing being asked about. Both are decoration on
     a marketing surface and nothing depends on them, so under this setting
     they do not move at all.

     Only these two. `translateY(-50%)` appears twice elsewhere as optical
     centring, and removing a transform that is doing layout would break it. */
  .po-hero-visual img:hover {
    transform: none !important;
  }
  .po-step-card:hover {
    transform: none !important;
  }

  /* loadingpaintomics2.gif loops for the whole analysis, which for the example
     dataset is around 90 seconds of continuous motion. `content` on a replaced
     element swaps the rendered image without touching the markup, so the
     dialog keeps its branded indicator as a still frame. The PNG is frame 5 of
     the original, extracted because it carries the most colour and so reads as
     a deliberate graphic rather than a half-drawn one. */
  #messageDialogSpin img {
    content: url('../images/loadingpaintomics2-static.png');
  }
}





/*****************************************************************************
**** RESULTS PAGE CONTENTS ***************************************************
*****************************************************************************/
/* Step 3 stacks eight analyses down ~2900px of scroll with no way to see what is
   there or reach any of it. This strip lists them and jumps to them.

   Sticky rather than fixed, so it scrolls with the column and pins under the
   header only once it reaches it - it belongs to the results, not to the app
   chrome, and pinning it permanently would have re-created the second bar that
   folding the step actions into the header just removed. It uses the same pill
   language as the navigation so the page has one idea of what a link-that-moves
   -you looks like, but muted, because this is orientation rather than action. */
/* Horizontal fallback, used below 1200px where there is no room for a column.
   Sticky rather than fixed so it belongs to the results and pins under the
   header only once reached. */
.pa-toc {
	position: sticky;
	top: 50px;
	z-index: 150;
	margin: 0 10px 4px;
	padding: 8px 0;
	background: rgba(255, 255, 255, 0.94);
	backdrop-filter: saturate(180%) blur(8px);
	-webkit-backdrop-filter: saturate(180%) blur(8px);
	border-bottom: 1px solid var(--pa-border);
}
.pa-toc-list {
	display: flex;
	flex-wrap: nowrap;
	overflow-x: auto;
	align-items: center;
	gap: 4px;
	margin: 0;
	padding: 0;
	list-style: none;
	scrollbar-width: thin;
	scrollbar-color: #C3C5CB transparent;
}
.pa-toc-list::-webkit-scrollbar { height: 6px; }
.pa-toc-list::-webkit-scrollbar-thumb { background: #C3C5CB; border-radius: 3px; }
.pa-toc-list::-webkit-scrollbar-track { background: transparent; }
.pa-toc-list > li { flex: 0 0 auto; }
.pa-toc-link {
	display: block;
	padding: 5px 11px;
	border-radius: 999px;
	font-size: 12px;
	line-height: 1.2;
	color: #55565E;          /* 7.0:1 on white */
	text-decoration: none;
	white-space: nowrap;
	transition: background-color var(--pa-transition), color var(--pa-transition);
}
.pa-toc-link:hover {
	background: var(--pa-surface-quiet);
	color: #111111;
	text-decoration: none;
}
.pa-toc-link.current {
	background: #33343A;
	color: #FFFFFF;
}

/* Sidebar ---------------------------------------------------------------------
   Above 1200px the contents become a column beside the results instead of a bar
   above them. Two reasons it is better here than as a strip: eight analyses read
   as a list rather than as a row of pills competing for one line, and the strip
   was spending 41px of vertical space on every screen forever, on a page whose
   whole problem is that it is 2900px long.

   Fixed, not sticky, because the element that scrolls is the centre panel rather
   than the window - a sticky child would scroll away with the content. It sits
   in the gutter the centred column already provides, and the panel's left
   padding grows to make room, so the results keep their full width rather than
   being squeezed. */
@media (min-width: 1200px) {
	/* Keyed off the class buildAnalysisTOC sets, so the column is only reserved
	   on views that actually have a contents list. The pathway view has none and
	   gets the full width. */
	/* One rail for the whole page. The TOC used to sit at --pa-gutter-railed
	   (which bottoms out at 40px) while the header wordmark sits at --pa-gutter
	   (~96-110px on laptops, and ~103px further right at every width) - two
	   left edges that never coincided, directly under each other. The header's
	   own comment states the contract ("the wordmark lines up with the left
	   edge of the content beneath it"), so the wordmark's rail is the master:
	   the TOC starts on it, the results column starts after gutter + rail, and
	   the leftover space flows to the right - the docs-site grid. The cost is
	   ~56-68px of empty margin at laptop widths, paid for a page where every
	   left edge derives from one token. --pa-gutter-railed keeps its definition
	   for anything external that read it, but nothing here does any more. */
	#mainViewCenterPanel.pa-has-toc {
		padding-left: calc(var(--pa-gutter) + var(--pa-rail));
		padding-right: max(40px, calc(100vw - var(--pa-gutter) - var(--pa-rail) - var(--pa-page-max)));
	}
	/* The header follows the column it sits above. On these railed pages the
	   results column's right edge comes from the leftover-space formula above,
	   not from --pa-gutter, so the header's utility corner (and the step
	   actions anchored to its left) ended 48px shy of the cards directly
	   beneath it at laptop widths - "the top button is misaligned with the
	   body". The +20 is the inset the ExtJS step containers still add between
	   the column edge and a card's border (two stacked 10px paddings), i.e.
	   the edge the reader actually sees. The left side needs no counterpart:
	   the wordmark and the TOC both already sit on --pa-gutter. fitHeaderNav's
	   body observer re-measures --pa-header-utilities when pa-has-toc lands,
	   so the step actions follow this padding without new script. */
	body:has(#mainViewCenterPanel.pa-has-toc) .mainTopToolbar {
		padding-right: calc(max(40px, 100vw - var(--pa-gutter) - var(--pa-rail) - var(--pa-page-max)) + 20px);
	}
	.pa-toc {
		position: fixed;
		top: 62px;
		left: var(--pa-gutter);
		/* The wordmark's own width, so the column under "PaintOmics AI" ends
		   where the wordmark does and the results column starts at the next
		   thing in the header - the "Job view" pill (see --pa-rail). Names
		   wrap one line earlier than they did at 190px, which the column has
		   the vertical room for. */
		width: 147px;
		max-height: calc(100vh - 84px);
		overflow-y: auto;
		margin: 0;
		padding: 12px 0 12px 0;
		background: transparent;
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		border-bottom: 0;
		/* No dividing rule. The column is already separated from the results by
		   the gap between them, so a border only adds a grey line that carries
		   no information - and the current-item marker is the one line in here
		   that should draw the eye. */
		border-right: 0;
		z-index: 140;
	}
	/* A quiet label, so the column reads as a table of contents rather than as
	   a stray list of links. */
	.pa-toc::before {
		content: 'Analyses';
		display: block;
		margin: 0 12px 8px;
		font-size: 10.5px;
		font-weight: 600;
		letter-spacing: 0.08em;
		text-transform: uppercase;
		color: #83848C;        /* 4.6:1 on the page */
	}
	.pa-toc-list {
		flex-direction: column;
		flex-wrap: nowrap;
		align-items: stretch;
		overflow-x: visible;
		gap: 1px;
		padding-right: 12px;
	}
	.pa-toc-link {
		border-radius: 0 6px 6px 0;
		padding: 7px 10px 7px 12px;
		/* Names wrap here rather than being truncated - the column is narrow but
		   vertical space is what it has plenty of. */
		white-space: normal;
		border-left: 2px solid transparent;
	}
	.pa-toc-link.current {
		background: var(--pa-surface-quiet);
		color: #111111;
		border-left-color: #33343A;
		font-weight: 600;
	}
}

/*****************************************************************************
**** EMPTY EXPRESSION-VALUE PANELS *******************************************
*****************************************************************************/
/* The two "Expression Value" panels used to be styled here for their empty
   state, because they sat on the page as 350px voids from the moment it loaded.
   That is now handled where it belongs - the panels are hidden until a row is
   painted (see revealPlotPanel in Util.js), so there is no empty state left to
   dress up, and the table beside them gets the width back until then.

   The earlier note here claimed the container had to stay so the Highcharts
   plot would have its height reserved. It does not: the plot is drawn into an
   element that is created and sized by the same handler that reveals it. */

/*****************************************************************************
**** GRID TOOLBARS ***********************************************************
*****************************************************************************/
/* Now that the controls sit on two rows they no longer overflow, so the
   horizontal scroll added earlier as a workaround has nothing left to do. Kept
   as `auto` rather than removed: a species with more databases can still push
   a row past the edge, and a scrollbar that appears only then costs nothing. */
.paGridBar {
	background: #FFFFFF;
	border-bottom: 1px solid var(--pa-border);
	/* 6px on the left, not 10: ExtJS's box layout adds 4px of its own before the
	   first item, so a 10px bar padding put "Search" at x=282 while the grid it
	   belongs to starts its first column's text at 278. The bar and the table
	   under it are read as one block, and they were on two rails four pixels
	   apart - close enough to look like a mistake and too close to look like a
	   decision. 6 + 4 lands the bar's first label on the grid's own rail. */
	padding: 6px 10px 6px 6px;
}
.paGridBar-filters {
	/* The second row carries the choices that shape the results, so it reads as
	   a filter bar rather than as a continuation of the search. */
	background: #FAFAFB;
}
/* Neptune's separators were doing the work of grouping when everything was
   crammed onto one line. With two rows the grouping is the rows themselves. */
.paGridBar .x-toolbar-separator {
	border-color: transparent;
	margin: 0 4px;
}

/* ---------------------------------------------------------------------------
   Stouffer weights panel (the "Configure" popover on the enrichment toolbar)

   This opened at 100px wide, which left the sliders with no track to drag and
   pushed the "Defaults" button off the edge - the panel appeared, but nothing
   in it could be operated. The width now comes from the component config; what
   is left here is making it read as a deliberate popover rather than a tooltip,
   and surfacing each weight as a number so the six omics can be compared
   without dragging every one of them.
--------------------------------------------------------------------------- */
.paWeightsTip {
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius);
	box-shadow: 0 6px 20px rgba(0, 0, 0, 0.13);
	background-color: #FFFFFF;
}

.paWeightsTip .x-tip-header,
.paWeightsTip .x-tip-header-default {
	background: #F9F8F4;
	border-bottom: 1px solid var(--pa-border);
	border-radius: var(--pa-radius) var(--pa-radius) 0 0;
	padding: 7px 10px;
}

.paWeightsTip .x-tip-header-text {
	color: #33343A;
	font-size: 12.5px;
	font-weight: 600;
}

.paWeightsTip .x-tip-body,
.paWeightsTip .x-tip-body-default {
	background-color: #FFFFFF;
	padding: 8px 12px 4px;
}

/* Each row is "omic name .... [n] ---slider---". The number is pinned to the
   right edge of the label column so the values line up as a scannable column. */
.paWeightsTip .x-form-item-label {
	position: relative;
	padding-right: 30px;
	box-sizing: border-box;
	color: #3F3F46;
	font-size: 12px;
	line-height: 20px;
}

.paWeightsTip .x-form-item-label[data-pa-weight]::after {
	content: attr(data-pa-weight);
	position: absolute;
	right: 2px;
	top: 50%;
	transform: translateY(-50%);
	min-width: 20px;
	padding: 0 4px;
	border: 1px solid var(--pa-border);
	border-radius: var(--pa-radius-sm);
	background-color: #F4F4F5;
	color: #18181B;
	font-size: 11px;
	font-weight: 600;
	line-height: 17px;
	text-align: center;
	font-variant-numeric: tabular-nums;
}

/* A weight of 0 drops that omic out of the combined p-value entirely, so it
   gets called out rather than sitting in the row looking like any other value. */
.paWeightsTip .x-form-item-label[data-pa-weight="0"]::after {
	border-color: #E0BFBC;
	background-color: #FCEEEC;
	color: #B3261E;
}

.paWeightsTip .x-slider {
	margin-bottom: 2px;
}

.paWeightsTip .x-btn {
	border-radius: var(--pa-radius-btn);
}

/* Grid rows ------------------------------------------------------------------
   Three things every table in the app was missing, all of them about reading a
   row rather than decorating it.

   Hover: with 888 rows and a dozen columns, tracking one pathway across the
   full width was unaided - there was no feedback at all under the cursor.

   Lining figures: the p-value and count columns exist to be compared down the
   column, and proportional digits make "0.00533" and "0.00966" different widths,
   so the decimal points wander. Tabular figures line them up.

   Selection: Neptune's selected row is a saturated blue fill that fights the
   omic colour-coding in the same row. A pale tint plus a left marker says the
   same thing without competing for attention. */
.x-grid-item-over .x-grid-cell {
	background-color: #F9F8F4;
}

.x-grid-cell-inner {
	font-variant-numeric: tabular-nums;
}

.x-grid-item-selected .x-grid-cell,
.x-grid-row-selected .x-grid-cell {
	background-color: #EEF4FC;
	border-bottom-color: var(--pa-border);
}

/* Cells that carry a significance tint set it inline, so it survives both of
   the rules above - the colour is data, not styling, and must not be
   overridden by hover or selection. */

/* Wrapping column headers ----------------------------------------------------
   The Hub Analysis grid sizes its columns with flex percentages chosen for the
   *data* - a step number, a count, a ratio - while the headers naming them are
   two and three words long. The result was a header row reading "DE neighbo",
   "not DE neighb", "Percentage.", "Percentile..": the columns you most need
   explained were the ones whose explanation was cut off.

   Widening them is not available; the nine data columns already use the full
   width of a two-thirds panel. So the header text wraps instead, and the header
   type drops a step below the data it labels - at the body size the labels were
   taking width the values needed, and "C00042" rendered as "C00...". Still 600
   weight and near-black, so the band reads as a label row rather than content.

   Scoped by class, because the enrichment grid above uses grouped headers whose
   heights ExtJS calculates on the assumption of a single line. */
.paWrapHeaders .x-column-header-text {
	white-space: normal;
	overflow: visible;
	text-overflow: clip;
	line-height: 1.25;
	font-size: 11.5px;
}
/* 10px, matching `.x-grid-cell-inner`, and that is the whole point of the rule.
   At 5px every header in these two grids sat exactly 5px left of the values it
   names - "Metabolite" over "C00032", "Step" over "1", thirteen columns of it
   across the hub-analysis and metabolite-class grids - which is the one
   misalignment in a table a reader cannot help seeing, because the two things
   are inches apart with nothing between them.

   The 5px was taken to give the wrapping header text more room. It does not
   need it: the text wraps rather than truncating (see `white-space: normal`
   above), so the cost of the extra 10px is at most one more line in the two
   widest headers, and both already wrap to two. Measured after: "DE neighbors"
   and "not DE neighbors" are still two lines in a 94px column, and all thirteen
   headers now start on their own column's rail. */
.paWrapHeaders .x-column-header-inner {
	padding: 6px 10px;
}

/* Grid toolbar controls -------------------------------------------------------
   The two rows fit and every control is reachable, but the controls themselves
   were still stock Neptune: sprite-image checkboxes, a search field and two
   combos at three different heights, and labels that were a mix of plain
   toolbar text and inline-styled bold spans. Read together they looked like
   three different toolbars sharing a bar.

   One height (26px), one radius, one border colour, one label style.
--------------------------------------------------------------------------- */
.paGridBar .x-toolbar-text {
	color: #52525B;
	font-size: 12.5px;
}

/* The bold inline-styled group labels ("Databases to view:", and the plain-text
   "Search") become one quiet label voice. The inline font-weight on the spans
   is in the markup, so it has to be overridden rather than removed. */
.paGridBar .x-toolbar-text span[style],
.paGridBar .x-toolbar-text {
	font-weight: 600 !important;
	color: #52525B;
}

/* Text input and combos ------------------------------------------------------ */
.paGridBar .x-form-text {
	height: 26px;
	border: 1px solid #C9C9CE;
	border-radius: var(--pa-radius-sm);
	background-image: none;
	background-color: #FFFFFF;
	color: #27272A;
	font-size: 12.5px;
	padding: 0 8px;
	transition: border-color var(--pa-transition), box-shadow var(--pa-transition);
}
.paGridBar .x-form-text:hover {
	border-color: #A1A1AA;
}
/* Neptune draws the combo's frame on a wrapping table rather than on the field,
   so the field's own border has to be suppressed there or the two stack up. */
.paGridBar .x-form-trigger-wrap {
	border: 1px solid #C9C9CE;
	border-radius: var(--pa-radius-sm);
	background-color: #FFFFFF;
	overflow: hidden;
}
.paGridBar .x-form-trigger-wrap .x-form-text {
	border: 0;
	border-radius: 0;
	height: 24px;
}
.paGridBar .x-form-trigger {
	background-color: transparent;
	border-left: 1px solid var(--pa-border);
}

/* The box itself is drawn by the shared `.x-form-checkbox` rule further down
   this file, which started here and was generalised once the same control
   appeared in the Step 3 settings panels. Only the label is toolbar-specific. */
.paGridBar .x-form-cb-label {
	color: #3F3F46;
	font-size: 12.5px;
	padding-left: 6px;
}

/* Actions --------------------------------------------------------------------
   "Configure" and "Download as XLS" are anchors rather than buttons, and read
   as two unrelated links. Same ghost-control treatment as the header. */
.paGridBar a.downloadXLS,
.paGridBar a.multiDelete,
.paGridBar #configureButton {
	display: inline-block;
	padding: 4px 10px;
	border: 1px solid transparent;
	border-radius: var(--pa-radius-btn);
	font-size: 12.5px;
	line-height: 16px;
	transition: background-color var(--pa-transition), border-color var(--pa-transition);
}
.paGridBar a.downloadXLS:hover,
.paGridBar #configureButton:not(.x-item-disabled):hover {
	background-color: #F4F4F5;
	border-color: var(--pa-border);
}
.paGridBar a.multiDelete:hover {
	background-color: #FCEEEC;
	border-color: #E0BFBC;
}
/* Disabled Configure - the method is not Stouffer - should read as unavailable
   rather than as a link that ignores you. */
.paGridBar #configureButton.x-item-disabled {
	opacity: 1;
	color: #9A9AA0;
	cursor: default;
}

/* Hero visualisation -----------------------------------------------------------
   The animated figure beside the headline. It exists because the hero had
   nothing on its right once the flow diagram moved into the step cards, and
   because a picture there should earn its place: this one shows the two things
   the product does that nothing else on the page demonstrates - painting a
   network with your values, and reading that network back as prose.

   It runs continuously, as a conversation would. Each 9s cycle swaps to a
   different pathway - a different topology carrying different values, not the
   same graph recoloured - and wipes the interpretation away to write a new
   one. That is the loop a user is actually in: ask about something else, get
   a different answer. Two complete networks cross-fade rather than one being
   morphed, because morphing edge paths between topologies is a lot of
   machinery for something the eye reads as a dissolve anyway.

   Order lives in the markup: every line of the interpretation carries its own
   `--d` delay. */
.po-hero-viz {
  display: block;
  width: 100%;
  height: auto;
}

.po-hero-viz .po-viz-net {
  opacity: 0;
  animation: poVizSwapA 9s ease-in-out infinite;
}

/* The second network runs the same keyframes half a cycle out of phase, so one
   is always arriving as the other leaves and the panel is never beside an
   empty space. */
.po-hero-viz .po-viz-net-b {
  animation-name: poVizSwapB;
}

/* The two wires either side of the agent are a fixed pipe - always drawn,
   never animated - and only the pulse riding each one comes and goes. That
   is the opposite of the network/panel pieces, which fade in once and then
   hold: a wire with nothing moving on it would read as decoration, so it
   gets no idle state worth drawing attention to. */
.po-hero-viz .po-viz-wire {
  fill: none;
  stroke: #878787;
  stroke-opacity: .5;
  stroke-width: 1.6;
  stroke-dasharray: 3 3;
}

.po-hero-viz .po-viz-pulse {
  fill: var(--pa-ai-blue, #4A90D9);
  opacity: 0;
  animation-duration: 9s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.po-hero-viz .po-viz-pulse-in { animation-name: poVizPulseIn; }
.po-hero-viz .po-viz-pulse-out { animation-name: poVizPulseOut; }

/* The agent itself fades in once, alongside the network, rather than every
   cycle - it is a fixture of the pipeline, not something that arrives with
   the data. Only its ring pings each time it is doing something. */
.po-hero-viz .po-viz-agent-body {
  fill: var(--pa-surface, #FFFFFF);
  stroke: var(--pa-ai-blue, #4A90D9);
  stroke-width: 1.8;
  opacity: 0;
  animation: poVizIn 0.4s ease-out 0.3s forwards;
}

.po-hero-viz .po-viz-agent-mark {
  color: var(--pa-ai-blue, #4A90D9);
  opacity: 0;
  animation: poVizIn 0.35s ease-out 0.4s forwards;
}

/* Two rings rather than one so the ping reads as layered activity - a single
   ring pulsing once per network read as a notification badge, not as work
   being done. transform-box: fill-box because these are plain <circle>s with
   their own cx/cy; without it, scale() would expand from the SVG's origin
   instead of the ring's own center. */
.po-hero-viz .po-viz-agent-ring {
  fill: none;
  stroke: var(--pa-ai-blue, #4A90D9);
  stroke-width: 1.5;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation-name: poVizAgentPing;
  animation-duration: 9s;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}

.po-hero-viz .po-viz-agent-ring-2 {
  animation-delay: .27s;
}

.po-hero-viz .po-viz-panel {
  /* --pa-surface, not --pa-surface-quiet. The interpretation panel was the
     quiet grey because it was drawn on the white card; the diagram now sits on
     a #F6F5F0 plate, and #F3F2ED on #F6F5F0 is a 0.3% luminance step - the
     panel would have vanished into the plate behind it. Same figure-ground,
     inverted: the plate is the quiet surface and the panel on it is the card.
     Dark inverts again, where --pa-surface is darker than --pa-surface-quiet. */
  fill: var(--pa-surface, #FFFFFF);
  stroke: var(--pa-border);
  opacity: 0;
  animation: poVizIn 0.5s ease-out 0.5s forwards;
}

.po-hero-viz .po-viz-mark,
.po-hero-viz .po-viz-title {
  opacity: 0;
  animation: poVizIn 0.4s ease-out 0.65s forwards;
}

.po-hero-viz .po-viz-title { fill: var(--pa-ink-title, #1A1A18); }

/* The interpretation: each line grows from its left edge, holds while it is
   read, and fades out in place. It used to clear the same way it grew - scaleX
   back to 0 against the same left origin - which read as the text sliding
   backward off the panel toward the network and agent, now that those sit to
   its left; a fade removes the direction entirely. The per-line delay makes
   growth cascade rather than move as a block, which is what reading a
   generated answer looks like. The
   delays themselves (--d, in the markup) are untouched by the agent stage
   below - only the shared keyframes' own start point moved later, so every
   line still lands relative to the others exactly as before, just after the
   agent has had its turn rather than the instant the network appears.

   There are two answers, not one replayed. Retyping the same line lengths
   every cycle reads as a loop rather than as a new response, so the second
   has its own rhythm - different lengths and two citations instead of three -
   and each is written during the half of the cycle its own network is on
   screen. */
.po-hero-viz .po-viz-line,
.po-hero-viz .po-viz-cite {
  --po-viz-on-opacity: 1;
  transform: scaleX(0);
  transform-origin: left center;
  opacity: 0;
  animation-duration: 9s;
  animation-timing-function: cubic-bezier(.2,.7,.3,1);
  animation-delay: var(--d, 0s);
  animation-iteration-count: infinite;
}

.po-hero-viz .po-viz-ans-a .po-viz-line,
.po-hero-viz .po-viz-ans-a .po-viz-cite { animation-name: poVizAnswerA; }

.po-hero-viz .po-viz-ans-b .po-viz-line,
.po-hero-viz .po-viz-ans-b .po-viz-cite { animation-name: poVizAnswerB; }

.po-hero-viz .po-viz-line { fill: var(--pa-border); }
/* The citations are the one thing in the panel that is not grey: an
   interpretation you can check is the point of the feature. Its .32 rest
   opacity is read by the shared keyframes below through --po-viz-on-opacity,
   so it still fades in and out in step with the lines around it without
   ever reaching their full opacity. */
.po-hero-viz .po-viz-cite { --po-viz-on-opacity: .32; fill: #4A90D9; }

@keyframes poVizIn { to { opacity: 1; } }

/* Each network holds for about four seconds and hands over in half of one. */
@keyframes poVizSwapA {
  0%,  42%  { opacity: 1; }
  50%, 92%  { opacity: 0; }
  100%      { opacity: 1; }
}

@keyframes poVizSwapB {
  0%,  42%  { opacity: 0; }
  50%, 92%  { opacity: 1; }
  100%      { opacity: 0; }
}

/* The pulse riding the wire from network to agent: a dot that fades in at the
   network's edge, slides to the agent, and fades out - twice a cycle, once
   for each network's turn. The second burst is the first shifted exactly
   half a cycle, same as the network swap above. */
@keyframes poVizPulseIn {
  0%,  2%    { opacity: 0; transform: translateX(0); }
  3%         { opacity: 1; transform: translateX(0); }
  8%         { opacity: 1; transform: translateX(27px); }
  9%,  52%   { opacity: 0; transform: translateX(0); }
  53%        { opacity: 1; transform: translateX(0); }
  58%        { opacity: 1; transform: translateX(27px); }
  59%, 100%  { opacity: 0; transform: translateX(0); }
}

/* The agent's ring, pinging while the data it just received is being
   searched and reasoned over. Starts as the inbound pulse is still arriving
   and clears before the outbound one leaves, so the three overlap at their
   edges rather than snapping from one to the next. */
@keyframes poVizAgentPing {
  0%,  6%    { opacity: 0;  transform: scale(.75); }
  7%         { opacity: .8; transform: scale(.75); }
  17%        { opacity: 0;  transform: scale(1.55); }
  18%, 56%   { opacity: 0;  transform: scale(.75); }
  57%        { opacity: .8; transform: scale(.75); }
  67%        { opacity: 0;  transform: scale(1.55); }
  68%, 100%  { opacity: 0;  transform: scale(.75); }
}

/* The pulse from agent to panel - shorter hop, same idea as the inbound one.
   Arrives right as the panel starts writing (below), which is the point of
   drawing it at all: the report does not start until the agent's answer
   does. */
@keyframes poVizPulseOut {
  0%,  14%   { opacity: 0; transform: translateX(0); }
  15%        { opacity: 1; transform: translateX(0); }
  19%        { opacity: 1; transform: translateX(24px); }
  20%, 64%   { opacity: 0; transform: translateX(0); }
  65%        { opacity: 1; transform: translateX(0); }
  69%        { opacity: 1; transform: translateX(24px); }
  70%, 100%  { opacity: 0; transform: translateX(0); }
}

/* Answer A now starts once the network -> agent -> panel relay above has run
   its course (was 9%/40%; the grow and wipe points keep their original 9-
   point and 6-point spans, just moved later) and is cleared as network A
   hands over to B. Answer B mirrors it against network B's own relay. */
/* Growing (scaleX from a left origin) reads as writing; clearing used to run
   the same scaleX back to 0, which reads as the text sliding back the way it
   came - toward the network and agent, now that those sit to the panel's
   left. So only the grow edge moves the width; the clear edge is a fade in
   place; both share these keyframes via opacity: var(--po-viz-on-opacity),
   which resolves to each element's own rest opacity (1 for lines, .32 for
   citations). */
@keyframes poVizAnswerA {
  0%,  19%   { transform: scaleX(0); opacity: 0; }
  28%, 40%   { transform: scaleX(1); opacity: var(--po-viz-on-opacity); }
  46%, 100%  { transform: scaleX(1); opacity: 0; }
}

@keyframes poVizAnswerB {
  0%,  69%   { transform: scaleX(0); opacity: 0; }
  78%, 90%   { transform: scaleX(1); opacity: var(--po-viz-on-opacity); }
  96%, 100%  { transform: scaleX(1); opacity: 0; }
}

/* Anyone who has asked for less motion gets the finished picture and no loop:
   the first network, painted, beside a written interpretation. This restates
   the end state rather than only zeroing the duration - with `animation: none`
   alone the elements keep their starting opacity: 0 and scaleX(0), and the
   figure would be half missing. */
@media (prefers-reduced-motion: reduce) {
  .po-hero-viz .po-viz-net-a { opacity: 1; animation: none; }
  .po-hero-viz .po-viz-net-b { opacity: 0; animation: none; }
  .po-hero-viz .po-viz-panel,
  .po-hero-viz .po-viz-mark,
  .po-hero-viz .po-viz-title { opacity: 1; animation: none; }
  .po-hero-viz .po-viz-ans-a .po-viz-line,
  .po-hero-viz .po-viz-ans-a .po-viz-cite { transform: scaleX(1); opacity: var(--po-viz-on-opacity); animation: none; }
  .po-hero-viz .po-viz-ans-b { display: none; }
  /* The agent renders in, idle: present in the pipeline, nothing pinging or
     travelling along the wires either side of it. */
  .po-hero-viz .po-viz-agent-body,
  .po-hero-viz .po-viz-agent-mark { opacity: 1; animation: none; }
  .po-hero-viz .po-viz-agent-ring,
  .po-hero-viz .po-viz-pulse { opacity: 0; animation: none; }
}

/* ---------------------------------------------------------------------------
   Organism picker rows (xtype organismcombo, app/view/common/OrganismSearch.js)
   The list marks the parts of a name the query matched and shows the KEGG code
   at the row's right edge. <mark> is bold rather than a highlighter fill: the
   row already has hover and selection fills, and a third one under a few
   letters read as a rendering fault. Colour is inherited, so the dark theme's
   row ink (dark.css, .x-boundlist-item) carries through with nothing to restate.
   --------------------------------------------------------------------------- */
.po-organism-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75em;
  min-width: 0;
}
.po-organism-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.po-organism-code {
  flex: none;
  font-family: "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--pa-ink-muted);
}
.po-organism-name mark,
.po-organism-code mark {
  background: transparent;
  color: inherit;
  font-weight: 700;
}

/* ==========================================================================
   Step 2 - "Choose for me" (AI compound disambiguation)

   Built entirely from tokens dark.css already redefines, so the theme follows
   without a single [data-theme] rule here.

   The one idea this styling has to carry: after the button runs, you should be
   able to tell at a glance WHAT touched each card. Three states, three colours,
   and they are not interchangeable --

     Auto       --pa-ink-muted     a name-matching rule; no model was involved,
                                   so it must not wear the AI colour
     AI         --pa-ai-blue       the application's one AI colour, shared with
                                   the mark, the consent block and the widget
     AI unsure  --pa-accent-orange the model declined; this one is still yours

   Cards the run did not change carry no mark at all. Badging those too put a
   chip on nearly every card, and a chip on everything is a chip on nothing.
   ========================================================================== */

/* ---- The section header, and the AI offer beside it ---------------------- */

/* The metabolite cards are laid out by `width: 49%` plus odd/even floats (see
   `.omicSummaryBox, .metaboliteBox` above). The card that INTRODUCES them was
   picking that up as well, so the section opened with a half-width block of
   prose and an empty half-row beside it. It is a header, not a card in the
   grid: it takes the row.

   `min-height` is reset for the same reason -- 230px is a floor that keeps two
   side-by-side cards even, and this one has nothing beside it. */
.contentbox.omicSummaryBox.compoundsIntroBox {
	float: none;
	width: 100%;
	min-height: 0;
	margin-bottom: 14px;
}

/* Two columns: what the user has to do, and the offer to do it for them.
   Neither half works alone at this width -- prose across 1360px runs to about
   140 characters a line, and a lone button under it had no surface of its own
   and nothing to balance against. */
.compoundsIntroLayout {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	/* No column gap. The two halves are 49% each and the gutter is the 2%
	   `space-between` distributes -- which is exactly how the floated cards
	   below make theirs. A 28px gap on top of 98% overflows the row and wraps
	   the panel under the text. */
	gap: 10px 0;
}
/* 49%, which is exactly the column the metabolite cards below use (see
   `.omicSummaryBox, .metaboliteBox`). The header then sits ON the grid it
   introduces rather than beside it -- the alternative, letting these two size
   themselves, gave either a 944px line of prose or a 528px pool of dead space
   between the halves, both of which read as a layout accident. */
.compoundsIntroText {
	flex: 0 1 49%;
	min-width: 0;
}

/* ---- The AI offer ------------------------------------------------------- */

/* Its own surface, tinted with the application's AI colour. A control that
   costs a gateway call and changes what the analysis is about should look
   like a thing you decide to use, not like a link someone left in a paragraph.

   color-mix keeps the tint honest in both themes: it is always the CURRENT
   --pa-ai-blue over the CURRENT surface, so dark.css's lighter blue on its
   near-black panel comes out at the same weight the light theme has. The plain
   declarations before each one are the fallback for a browser without it. */
.aiSuggestActions {
	flex: 0 0 49%;
	/* Its own two columns: what it is on the left, the control on the right.
	   Inside a 49% panel they sit about 300px apart -- close enough to read as
	   one offer, where the same split across the whole 1360px card had put a
	   thousand pixels between a sentence and the button it describes. */
	display: flex;
	align-items: center;
	gap: 18px;
	margin: 2px 0;
	padding: 15px 17px;
	border-radius: var(--pa-radius);
	background: var(--pa-surface-subtle);
	background: color-mix(in srgb, var(--pa-ai-blue) 7%, var(--pa-surface));
	border: 1px solid var(--pa-border);
	border: 1px solid color-mix(in srgb, var(--pa-ai-blue) 26%, transparent);
}

/* A small-caps label rather than a sentence: the panel's job is to be
   recognised, and the line under it does the explaining. Opted out of
   `div.contentbox h3`'s padding, which would inset it from its own panel. */
/* The `.aiSuggestActionsBody >` prefix is load bearing twice over. `div.contentbox
   h3` is one class and two elements, which outranks a bare class, so `padding: 0`
   lost and the label carried the CARD's 26px inset inside its own panel. And the
   parent has to be named correctly: written as `.aiSuggestActions >` it matched
   nothing at all once the panel gained its body wrapper, and the label silently
   fell all the way back to the card's h3 -- dark grey, 13.5px, not the AI colour
   and not small caps. */
.aiSuggestActionsBody > .aiSuggestActionsTitle {
	display: flex;
	align-items: center;
	gap: 7px;
	margin: 0 0 7px 0;
	padding: 0;
	font-size: 11.5px;
	font-weight: 700;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--pa-ai-blue);
}
/* `:first-child` is here to outscore `div.contentbox h3 > svg.po-ai-mark:first-child`,
   which hangs the mark a full em into the gutter. That is right for a heading
   inside a card, whose gutter is the card's 26px inset -- and wrong here, where
   the gutter is this panel's 17px padding and the mark would hang past its own
   border. Without it the mark sat outside the tinted surface. */
.aiSuggestActionsBody > .aiSuggestActionsTitle > svg.po-ai-mark:first-child {
	width: 15px;
	height: 15px;
	margin-left: 0;
}
.aiSuggestActionsBody > .aiSuggestHint {
	margin: 0;
	padding: 0;
	font-size: 12.5px;
	line-height: 1.5;
	color: var(--pa-ink-muted);
}

.aiSuggestActionsBody {
	flex: 1 1 auto;
	min-width: 0;
}
.aiSuggestActionsRow {
	display: flex;
	align-items: stretch;
	gap: 8px;
	flex: 0 0 auto;
}
/* `.button` floats right and carries side margins, which inside a flex row
   would put the primary and its Undo in the wrong order with a gap nobody
   asked for. */
.aiSuggestActionsRow > .button {
	float: none;
	margin: 0;
}

/* The primary. Fills the panel it lives in, so it reads as the panel's action
   rather than as a button that happens to be nearby. --pa-ai-action-bg is the
   fill the rest of the application already uses for an AI call to action, and
   is deliberately NOT re-themed: it carries white text. */
#aiSuggestButton {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	padding: 11px 20px;
	/* Fits the longest label this button takes ("Choosing...") without
	   resizing. Left to size itself it grew and shrank between the three
	   states, and the paragraph beside it reflowed from two lines to one and
	   back on every click. */
	min-width: 172px;
	font-size: 13.5px;
	font-weight: 600;
	color: #FFFFFF;
	background-color: var(--pa-ai-action-bg);
	border-color: var(--pa-ai-action-bg);
	box-shadow: var(--pa-shadow);
}
#aiSuggestButton:hover {
	color: #FFFFFF;
	background-color: var(--pa-ai-action-bg-hover);
	border-color: var(--pa-ai-action-bg-hover);
}
#aiSuggestButton svg.po-ai-mark {
	width: 16px;
	height: 16px;
	color: inherit;
}
/* Anchors, so `disabled` does nothing; pointer-events is what stops a second
   click while the gateway is answering. */
#aiSuggestButton.aiWorking {
	opacity: 0.72;
	pointer-events: none;
}

/* Undo is an escape hatch, not a second offer: icon only, quiet, and it keeps
   its meaning through the tooltip. Giving it equal weight would ask the user
   to choose between two buttons when only one of them is the point. */
#aiUndoButton {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 38px;
	padding: 10px 12px;
	color: var(--pa-ink-control, #3F3F46);
	background-color: transparent;
	border-color: var(--pa-border);
}
#aiUndoButton:hover {
	color: var(--pa-ink-title, #222);
	background-color: var(--pa-surface);
	border-color: var(--pa-ink-muted);
}

/* ---- What happened, once it has run ------------------------------------- */

.contentbox.aiSuggestSummary {
	margin: 5px 0 14px 0;
	padding: 15px var(--pa-card-inset);
	border-left: 3px solid var(--pa-ai-blue);
	background: var(--pa-surface-subtle);
	clear: both;
}
.aiSuggestSummaryHead {
	display: flex;
	align-items: baseline;
	gap: 9px;
	font-size: 15px;
	line-height: 1.5;
	color: var(--pa-ink-body, #333);
}
.aiSuggestSummaryMark {
	display: inline-flex;
	color: var(--pa-ai-blue);
	/* Baseline alignment puts the mark slightly high against 15px text. */
	position: relative;
	top: 2px;
}
/* Which model answered, and that its answers were checked. Available to a
   reader who wants it rather than announced to one who does not. */
.aiSuggestModel {
	margin-top: 7px;
	font-size: 12px;
	line-height: 1.5;
	color: var(--pa-ink-muted);
}

/* ---- Per-card provenance ------------------------------------------------- */

.aiBadge {
	display: inline-block;
	margin-left: 8px;
	padding: 1px 8px;
	border: 1px solid currentColor;
	border-radius: 10px;
	font-size: 11px;
	font-weight: normal;
	/* Fixed line-height and inline-block so a metabolite name wrapping to two
	   lines cannot stretch the chip with it. */
	line-height: 16px;
	white-space: nowrap;
	vertical-align: middle;
	cursor: help;
}
.aiBadge .fa {
	font-size: 10px;
	margin-right: 3px;
}
.aiBadge-auto   { color: var(--pa-ink-muted); }
.aiBadge-ai     { color: var(--pa-ai-blue); }
.aiBadge-unsure { color: var(--pa-accent-orange); }

/* A 3px rail, so the state is readable while scanning a column of cards and
   not only when reading one. */
.contentbox.metaboliteBox.aiBox-auto   { border-left: 3px solid var(--pa-ink-muted); }
.contentbox.metaboliteBox.aiBox-ai     { border-left: 3px solid var(--pa-ai-blue); }
.contentbox.metaboliteBox.aiBox-unsure { border-left: 3px solid var(--pa-accent-orange); }

/* The one candidate the machine is responsible for. A card can show several
   ticked-looking rows, so "which row" is said rather than left to the box. */
.metaboliteCompound.aiPickedCandidate > a {
	font-weight: 600;
	color: var(--pa-ai-blue);
}

/* One quiet reveal on the cards that moved. The panel is rebuilt wholesale, so
   this runs once per apply and never on the cards left alone -- which is the
   point: the motion is the answer to "what just changed?". */
@keyframes aiCardSettle {
	from { background-color: var(--pa-surface-subtle); }
	to   { background-color: transparent; }
}
.contentbox.metaboliteBox.aiBox-auto,
.contentbox.metaboliteBox.aiBox-ai,
.contentbox.metaboliteBox.aiBox-unsure {
	animation: aiCardSettle 900ms ease-out 1;
}
@media (prefers-reduced-motion: reduce) {
	.contentbox.metaboliteBox.aiBox-auto,
	.contentbox.metaboliteBox.aiBox-ai,
	.contentbox.metaboliteBox.aiBox-unsure {
		animation: none;
	}
}

/* The jump from an omic's mapping summary down to the compound cards. Sits
   under the mapped/unmapped caption, on the caption's own centre line. */
.mappingSummaryJump {
	text-align: center;
	font-size: 12px;
	padding-bottom: 6px;
}
.mappingSummaryJump > a {
	color: var(--pa-link);
	text-decoration: none;
	border-bottom: 1px solid transparent;
}
.mappingSummaryJump > a:hover {
	color: var(--pa-link-hover);
	border-bottom-color: currentColor;
}

/* ------------------------------------------------------------------
   Metabolite class activity map (PA_Step3MetaboliteView.drawClassMap).

   Fills live in CSS rather than on the SVG nodes so dark.css can restyle
   the chart without a redraw -- an inline fill is unreachable from a
   stylesheet, and the theme toggle does not re-render Step 3.
   ------------------------------------------------------------------ */
.paClassMap { display: block; max-width: 100%; }
.paClassMapBand { fill: #F1F5F8; }
.paClassMapGrid { stroke: #D2DBE4; stroke-width: 1; }
.paClassMapDisc { fill: #C4CFDA; stroke: #FFFFFF; stroke-width: 2; }
.paClassMapUp { fill: #0F59A9; stroke: #FFFFFF; stroke-width: 1; }
.paClassMapDown { fill: #A51F1E; stroke: #FFFFFF; stroke-width: 1; }
.paClassMapNeutral { fill: #4A6785; stroke: #FFFFFF; stroke-width: 1; }
.paClassMapRing { fill: none; stroke: #141E2B; stroke-width: 1.8; }
.paClassMapSigLine { stroke: #B5761F; stroke-width: 1.5; stroke-dasharray: 6 4; }
.paClassMapSigLabel { fill: #B5761F; font-size: 11px; font-weight: 600; }
.paClassMapTick { fill: #78879A; font-size: 10.5px; }
.paClassMapAxis { fill: #78879A; font-size: 10px; letter-spacing: .07em; text-transform: uppercase; }
.paClassMapLabel { fill: #141E2B; font-size: 11.5px; }
.paClassMapCount { fill: #44556A; font-size: 10px; }
.paClassMapMark { cursor: pointer; }
.paClassMapMark:hover .paClassMapDisc { stroke: #B5761F; }
.paClassMapSummary { position: relative; margin: 2px 0 10px; color: #44556A; font-size: 13px; }
.paClassMapWarn { color: #A51F1E; }
.paClassMapMuted { color: #78879A; }
/* Legend. A list, not a run-on sentence: each item is one nowrap flex child,
   so a narrow card wraps BETWEEN keys instead of mid-phrase. */
/* Aligned to the plot area by drawClassMap, which sets padding-left from the
   chart's own padL so the keys start under the axis caption rather than at the
   card edge. Each li is one nowrap flex child, so a narrow card wraps BETWEEN
   keys instead of mid-phrase. */
.paClassMapKeys { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center;
                  margin: 12px 0 4px; padding: 0; list-style: none;
                  color: #44556A; font-size: 12px; }
.paClassMapKeys li { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
/* No own indent: the ul carries the chart's padL, and `div.contentbox li`
   (padding-left: 4px, two class-and-element parts to this rule's one) put the
   first swatch 4px right of the axis line it sits under -- measured with the
   guides: li at 337, swatch at 341, axis at 337. Written at the higher
   specificity so it actually applies. */
div.contentbox .paClassMapKeys li { padding-left: 0; }
/* Colour keys say what a mark is made of; shape keys say what it scored. The
   extra space is the only thing separating the two ideas. */
.paClassMapKeys li.paKeyBreak { margin-left: 12px; padding-left: 24px;
                                border-left: 1px solid #E4EAF0; }
.paKeySwatch, .paKeyGlyph { box-sizing: border-box; flex: none; }
.paKeySwatch { width: 12px; height: 12px; border-radius: 2px; }
.paKeyUp { background: #0F59A9; }
.paKeyDown { background: #A51F1E; }
.paKeyNeutral { background: #4A6785; }
.paKeyNull { background: #C4CFDA; }
.paKeyGlyph { width: 13px; height: 13px; border-radius: 50%; }
/* No outer halo: box-shadow paints outside the layout box, so this key read a
   third wider than every other one while measuring the same. */
.paKeyArea { background: #C4CFDA; }
.paKeyRing { border: 1.8px solid #141E2B; }

/* Hover linkage. Pointing at a mark rings it and drops every other class to a
   fifth, which is what makes eight marks read as a set being compared rather
   than eight independent dots. */
.paIsFocusing .paClassMapMark { opacity: .45; }
.paIsFocusing .paClassMapMark.paIsFocused { opacity: 1; }
.paClassMapMark.paIsFocused .paClassMapDisc { stroke: #B5761F; stroke-width: 3; }
/* The caption doubles as the hover readout. The readout is laid OVER the
   caption, not swapped into its place: the caption is what sizes this
   paragraph, and the SVG sits directly under it, so a swap that changed the
   line count moved the map out from under a resting cursor -- mouseleave,
   swap back, mouseenter, 27 times a second, and the mark "shone". visibility,
   not display, on the hidden caption: it must keep occupying its lines.
   padding: inherit, because the card inset lives on `div.contentbox p` and an
   absolute child at left: 0 sits at the padding edge -- measured 26px left of
   the caption it stands in for. */
.paClassMapSummary .paClassMapReadout { display: none; position: absolute; top: 0; left: 0; right: 0;
                                        padding: inherit; box-sizing: border-box; }
.paClassMapSummary.paIsFocused .paClassMapBase { visibility: hidden; }
.paClassMapSummary.paIsFocused .paClassMapReadout { display: block; }
.paClassMapSummary.paIsFocused { color: #141E2B; }
.paDirUp { color: #0F59A9; font-weight: 600; }
.paDirDown { color: #A51F1E; font-weight: 600; }
.paClassMapControls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
                      margin: 4px 0 12px; }
.paClassMapControlLabel { font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
                          color: #78879A; font-weight: 600; }
.paClassMapSelect { padding: 4px 8px; border: 1px solid #B5C1CE; border-radius: 4px;
                    background: #FFFFFF; color: #141E2B; font-size: 13px; }
.paClassMapControlNote { color: #78879A; font-size: 12px; }

/* The class ranking (drawClassMap): one row per class. The whole row is the
   hover/click target; the selected row stays tinted while its compounds are
   open in the detail below. */
.paClassRankHit { fill: transparent; }
.paClassMapMark:hover .paClassRankHit { fill: #F1F5F8; }
.paClassMapMark.paIsSelected .paClassRankHit { fill: #E9F1F9; }
.paClassMapMark.paIsSelected .paClassMapLabel { font-weight: 600; }
.paClassRankHead { fill: #78879A; font-size: 10px; letter-spacing: .07em; text-transform: uppercase; }
.paClassMapParent { fill: #78879A; font-size: 9.5px; }
.paClassRankStem { stroke: #4A6785; stroke-width: 3; stroke-linecap: round; }
.paClassRankStemBelow { stroke: #C4CFDA; }
.paClassRankDot { fill: #141E2B; stroke: #FFFFFF; stroke-width: 1.5; }
.paClassRankFdr { fill: #78879A; font-size: 11px; font-variant-numeric: tabular-nums; }
.paClassRankFdr.paIsSig { fill: #141E2B; font-weight: 600; }

/* The selected class's compounds, inside the same card as the ranking. */
.paClassDetail { display: none; }
/* 26px each side: the text rail every paragraph and heading in a
   .contentbox sits on. Flush to the card edge, the class heading was
   the one line on the card 26px left of every other. */
.paClassDetail.paIsOpen { display: block; margin-top: 16px; padding: 14px 26px 0; border-top: 1px solid #E3E9EF; }
.paClassDetailHead { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.paClassDetailHead h4 { margin: 0; font-size: 15px; font-weight: 600; color: #141E2B; }
.paClassDetailStats { color: #78879A; font-size: 12.5px; }
.paClassDetailTools { margin-left: auto; display: flex; align-items: center; gap: 16px; font-size: 12.5px; color: #44556A; }
.paClassDetailToggle { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.paClassDetailToggle input { margin: 0; }
.paClassDetailClose { border: 0; background: transparent; color: #78879A; font-size: 20px; line-height: 1;
                      padding: 0 2px; cursor: pointer; }
.paClassDetailClose:hover { color: #141E2B; }
.paClassDetailScale { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 10px 0 8px;
                      color: #78879A; font-size: 12px; }
/* Heatmap and chart are one figure: the strip is fixed, the chart takes the
   rest, and on a class with many rows the chart stays in view while the
   strip scrolls past. */
.paClassDetailFigure { display: flex; align-items: flex-start; }
.paClassDetailFigure .PA_step5_heatmapContainer { flex: none; }
.paClassDetailFigure .PA_step5_plotContainer { flex: 1 1 320px; min-width: 320px; position: sticky; top: 8px; }
