
/* Separator attached to the top of each control section (except nav bar) */
.ctrl-section::before {
  content: "";
  display: block;
  border-top: 1px solid var(--sep-color);
  margin: var(--sep-margin);
}

#search-wrap::before,
#color-control-wrap::before { display: none !important; }

/* Separator between module and gene result groups */
.search-result-sep {
  border-top: 1px solid var(--sep-color);
  margin: 4px 8px;
}

/* Unified font sizes across graph controls */
#search-input,
#search-input:focus,
#search-input:active,
#search-input:focus-visible {
  font-size: 11px !important;
  min-height: 0 !important;
  padding-top: 5px !important;
  padding-bottom: 5px !important;
}

#search-input::placeholder { font-size: 11px !important; }

.search-result-item { font-size: 11px !important; }

#my-multi-dropdown .dash-dropdown-trigger,
#my-multi-dropdown [class*="singleValue"],
#my-multi-dropdown [class*="placeholder"],
#my-multi-dropdown span { font-size: 11px !important; }

/* .contcb-title is not overridden here any more: size, caps and spacing are the
   chip-label look, owned in one place by stylesheet.py. An !important font-size
   here beat it and left the title reading as body text. */
.contcb-txt   { font-size: 11px !important; }

/* Info card: tighter spacing between metric rows */
#info-essential p, #info-location p,
#info-biological-process p, #info-top-cell p,
#info-module-category p, #info-module-certainty p, #info-module-support p {
  margin: 0 0 2px 0;
  line-height: 1.3;
}

/* Right-side panels: lift Bootstrap's popover max-width cap */
#info-box, #help-popover, #ud-help-popover {
  max-width: none !important;
}

/* ── Scrollbars: single colour token — change --sb-thumb to restyle all ─── */
:root {
  /* ── Scuba theme tokens — change these two when switching dbc theme ── */
  --scuba-text:  var(--bs-body-color, #dee2e6); /* primary text; auto-tracks Bootstrap */
  --scuba-muted: #999;                           /* controls, labels, body text */
  --hover-bg:    #272a42;                        /* uniform hover background (rgba(255,255,255,0.10) over #141726) */
  --ctrl-width:  clamp(144px, 12.5vw, 211px);    /* control-stack width — used by Python/JS via var(--ctrl-width) */
  /* --scuba-font / --scuba-mono are theme tokens too, but they are declared in
     stylesheet.py's :root rather than here: Python owns the stack (UI_FONT_STACK)
     because the cytoscape sheet needs the literal — a canvas resolves no CSS
     variables — and one literal in one language beats two that must agree. */

  /* ── Palette tokens — Phase 0 of light-mode support ───────────────────
     Every hex literal that used to be sprinkled through stylesheet.py,
     user_data.css and this file now resolves through one of these, so a
     light palette is a matter of overriding this block (or adding a
     [data-theme="light"] block that does) rather than hunting through three
     files again. Values below are copied byte-for-byte from what was
     already on each surface — this pass changes no rendering, only where
     the color is written down. Two literals that were already visually
     identical (e.g. #fff and #ffffff) share one token; two that only
     looked close (e.g. #141726 and #171725) keep separate tokens, since
     merging them would be a real (if subtle) visual change, not a rename.
     --scuba-canvas-bg is the one exception: #app-root and #panel-right used
     to carry two close-but-different values (#000019 vs #07091a) — by
     request, both now read this single token, set to the value #panel-right
     used to have. */
  --scuba-canvas-bg:        #07091a; /* app canvas — #app-root AND #panel-right, one colour for both */
  --scuba-panel-bg:         #141726; /* the app's one card/panel background */
  --scuba-panel-bg-alt:     #171725; /* sticky table sub-header row */
  --scuba-landing-page-bg:  #070b1a; /* #cell-selector-page — its own close-but-different value, deliberately not unified with --scuba-canvas-bg */
  --scuba-topbar-bg:        #0a0c1b; /* #top-bar, #ud-header-bar */
  --scuba-white:            #fff;    /* strong on-dark text/icons */
  --scuba-light-text:       #ddd;    /* secondary on-dark text, one step down from --scuba-white */
  --scuba-muted-alt:        #bbb;    /* search-result text — a second muted level, lighter than --scuba-muted */
  --scuba-accent:           #75CBF5; /* loading spinner, active rail tab, gene-card links */
  --scuba-active-bg:        #343743; /* active/selected menu & legend row */
  --scuba-border-accent:    #373A46; /* upload-page card border */
  --scuba-border-dark:      #222;    /* modal close-button border */
  --scuba-mobile-overlay-bg:#0a1628; /* portrait-mode overlay */
  --scuba-rail-hover-color: #ccc;    /* right-panel rail icon hover */
  --scuba-search-open-bg:   #1a1d2e; /* open search box */
  --scuba-hover-bg-2:       #282f48; /* upload-page flat-button / search-box hover (distinct from --hover-bg) */
  --scuba-flat-btn-bg:      #1e2236; /* upload-page flat button, resting/disabled */
  --scuba-disabled-border:  #3a3d4a;
  --scuba-disabled-text:    #5c6070;
  --scuba-landing-desc-color:#aaa;   /* landing gene-card description text */
  --scuba-tooltip-text:     #c8ccd6; /* the four shared tooltip surfaces */
  --scuba-tooltip-bg:       rgba(74, 70, 90, 0.9);
  --scuba-tooltip-border:   rgba(255, 255, 255, 0.18);
  /* Chrome elevation (chips, the control panels, their collapse handles,
     a chip's dropdown menu) — see the light-mode value's own comment for
     why this is "none" here rather than a token these surfaces don't use
     at all in dark mode. One token so the panel and its handle can never
     end up with two different shadows again — see collapse-handle inline
     styles in layout.py/SCUBA.py, which read this same variable. */
  --scuba-chrome-shadow:    none;

  /* FC/Mod chart bars (config.MOD_BAR_COLORS) and their legend swatches
     (layout.py's _landing_fc_legend/_landing_mod_legend) — kept as CSS
     tokens rather than a var()-string passed into Plotly, because the two
     consumers need the colors two different ways: the legend swatches are
     plain DOM elements and read these directly; the Plotly bars are drawn
     from literal values (Plotly's marker.color doesn't resolve CSS
     variables), so user_data/callbacks.py picks between
     MOD_BAR_COLORS/MOD_BAR_COLORS_LIGHT in Python and serializes whichever
     is active — both paths ultimately come from the same config.py source
     values, so they can't drift apart even though they're resolved two
     different ways. */
  --mod-bar-pos-sig: #805142;
  --mod-bar-pos-ns:  #8A736C;
  --mod-bar-neg-sig: #426a81;
  --mod-bar-neg-ns:  #6C7E89;

  /* ── Wash tokens ───────────────────────────────────────────────────────
     Borders, dividers and hover washes painted as white-at-N% over the dark
     canvas — the ones that weren't already behind --sb-thumb/--sep-color.
     Same rule as the palette tokens above: each value is copied byte-for-
     byte from wherever it already was, named only by its opacity since
     that's the one thing that was ever chosen per use (the color itself
     was always plain white). Two Bootstrap-variable fallbacks
     (var(--bs-secondary-color, rgba(255,255,255,0.5)) and its --bs-body-
     color twin, both in stylesheet.py) are deliberately left alone: once
     the theme swap (Phase 2) lands, Bootstrap's own variable is always
     defined and the fallback never resolves either way. The tooltip's own
     border (#nav-tooltip etc., below) is also left alone on purpose — see
     the note there. */
  --scuba-wash-04: rgba(255,255,255,0.04);
  --scuba-wash-07: rgba(255,255,255,0.07);
  --scuba-wash-08: rgba(255,255,255,0.08);
  --scuba-wash-10: rgba(255,255,255,0.10);
  --scuba-wash-12: rgba(255,255,255,0.12);
  --scuba-wash-14: rgba(255,255,255,0.14);
  --scuba-wash-15: rgba(255,255,255,0.15);
  --scuba-wash-18: rgba(255,255,255,0.18);
  --scuba-wash-20: rgba(255,255,255,0.20);
  --scuba-wash-25: rgba(255,255,255,0.25);
  --scuba-wash-28: rgba(255,255,255,0.28);
  --scuba-wash-30: rgba(255,255,255,0.30);
  --scuba-wash-40: rgba(255,255,255,0.40);
  --scuba-wash-50: rgba(255,255,255,0.50);
  --scuba-wash-60: rgba(255,255,255,0.60);
  --scuba-wash-70: rgba(255,255,255,0.70);
  --scuba-wash-90: rgba(255,255,255,0.90);

  /* ── Other tokens ─────────────────────────────────────────────────── */
  --sb-thumb:  rgba(255, 255, 255, 0.40);
  --sep-color: rgba(255, 255, 255, 0.50);
  --sep-margin: 4px 12.5% 10px;
}

/* ══════════════════════════════════════════════════════════════════════
   Light mode — first draft
   ══════════════════════════════════════════════════════════════════════
   Overrides every token above for :root.theme-light. The class lands on
   <html> (not <body>) because the toggle script in SCUBA.py's index_string
   sets it before <body> exists, to paint the right theme on first frame
   rather than flashing dark-then-light.

   Colors below are a first pass, not audited the way the dark palette's
   comments show it was (contrast ratios measured against #000019, etc.) —
   picked to roughly mirror the dark palette's relative weights, ready to
   be tuned once seen. The wash tokens all flip white-at-N% to black-at-N%,
   which is the standard move for a dark-surface wash reused on a light
   surface, and keeps every border's relative weight (0.15 heavier than
   0.07, etc.) intact.

   NOT covered here: the Cytoscape network canvas and the Plotly UMAP/
   sunburst figures. Both are rendered off Python-built color literals
   (stylesheet.py's NODE_BORDER_COLOR/NODE_LABEL_COLOR/EDGE_LINE_COLOR,
   config.py's CT_BASE_COLORS etc., and plotly_dark templates in
   layout.py/callbacks.py) rather than CSS, so a CSS class cannot reach
   them — that needs its own theme-aware build path server-side. Until
   that lands, those surfaces stay dark under light mode by design, not
   by oversight. */
:root.theme-light {
  --scuba-canvas-bg:         #f7f8fb; /* #app-root AND #panel-right, same as dark mode's single value */
  --scuba-panel-bg:          #e4e6ec; /* by request: one shade darker than the first pass's #f2f3f7 */
  --scuba-panel-bg-alt:      #dcdee4; /* shifted down with --scuba-panel-bg above, to stay the darker of the two — it was #eaecf2 to #f2f3f7's lighter panel-bg, and would've inverted (lighter than panel-bg) if left alone */
  --scuba-landing-page-bg:   #f7f8fb;
  --scuba-topbar-bg:         #eef0f6;
  --scuba-white:             #14161f; /* strong text/icons — was the palette's brightest; now its darkest */
  --scuba-light-text:        #33374a;
  /* Missing from the first pass — --scuba-muted had no light-mode value at
     all, so it kept resolving to dark mode's #999 (chosen for contrast
     against near-black) everywhere it's used: chip category labels, legend
     titles and item labels, panel-type labels, and more. Against a light
     panel that same #999 is the near-invisible pale-gray-on-white this was
     reported against. Darker than --scuba-muted-alt below on purpose — the
     dark palette has muted-alt as the BRIGHTER of the two (a step toward
     --scuba-white), so its light-mode mirror has to be the step toward
     --scuba-white too, i.e. darker, to keep the same relative ordering. */
  --scuba-muted:             #454a5c;
  --scuba-muted-alt:         #5b607a;
  --scuba-accent:            #1868c9;
  --scuba-active-bg:         #d7e0f5;
  --scuba-border-accent:     #ced3e0;
  --scuba-border-dark:       #c7cbd6;
  --scuba-mobile-overlay-bg: #eef0f6;
  --scuba-rail-hover-color:  #3a3f52;
  --scuba-search-open-bg:    #ffffff;
  --scuba-hover-bg-2:        #e3e7f2;
  --scuba-flat-btn-bg:       #eceef4;
  --scuba-disabled-border:   #d7dae2;
  --scuba-disabled-text:     #9a9fae;
  --scuba-landing-desc-color:#5b607a;
  /* By request: reversed rather than left dark-on-both-themes (the first
     pass's choice, which held the four shared tooltip surfaces at their
     dark-mode purple-slate regardless of theme). Genuinely reversed, not a
     separate light design: the light background sits in the same
     purple-slate hue family the dark background did, and the text goes to
     a dark version of that same family — swapping which end (light bg /
     dark text vs. dark bg / light text) plays which role, rather than
     picking an unrelated light palette for it. Border flips from a
     white-alpha wash to a black-alpha one, same reasoning as the app's
     other wash tokens: white-alpha over a light background just fades
     toward invisible. */
  --scuba-tooltip-text:      #34303f;
  --scuba-tooltip-bg:        rgba(228, 225, 236, 0.97);
  --scuba-tooltip-border:    rgba(0, 0, 0, 0.18);
  /* Dark mode's panel/canvas tones are far enough apart (#141726 vs
     #07091a) to read as separate layers on a hairline border alone —
     that's why the dark value above is "none", not a lighter/smaller
     shadow. Light mode's are much closer (#f2f3f7 vs #f7f8fb), which is
     the actual reason this exists: a chip, panel or the rail can blend
     into the canvas with nothing but that same border. Deliberately not
     used for the tooltip (own rule, own comment) or #panel-rail (own
     rule, a different direction/weight fits its edge-of-viewport geometry
     better than this one does). */
  --scuba-chrome-shadow:     0 1px 3px rgba(0, 0, 0, 0.08);

  /* Must match config.MOD_BAR_COLORS_LIGHT byte-for-byte — see that
     constant's comment for the HLS math. */
  --mod-bar-pos-sig: #b78a7c;
  --mod-bar-pos-ns:  #baafac;
  --mod-bar-neg-sig: #7ca2b7;
  --mod-bar-neg-ns:  #acb4b9;

  --scuba-wash-04: rgba(0,0,0,0.04);
  --scuba-wash-07: rgba(0,0,0,0.07);
  --scuba-wash-08: rgba(0,0,0,0.08);
  --scuba-wash-10: rgba(0,0,0,0.10);
  --scuba-wash-12: rgba(0,0,0,0.12);
  --scuba-wash-14: rgba(0,0,0,0.14);
  --scuba-wash-15: rgba(0,0,0,0.15);
  --scuba-wash-18: rgba(0,0,0,0.18);
  --scuba-wash-20: rgba(0,0,0,0.20);
  --scuba-wash-25: rgba(0,0,0,0.25);
  --scuba-wash-28: rgba(0,0,0,0.28);
  --scuba-wash-30: rgba(0,0,0,0.30);
  --scuba-wash-40: rgba(0,0,0,0.40);
  --scuba-wash-50: rgba(0,0,0,0.50);
  --scuba-wash-60: rgba(0,0,0,0.60);
  --scuba-wash-70: rgba(0,0,0,0.70);
  --scuba-wash-90: rgba(0,0,0,0.90);

  --hover-bg:  #dde3f0;
  --sb-thumb:  rgba(0, 0, 0, 0.30);
  --sep-color: rgba(0, 0, 0, 0.30);
}

/* Icon assets loaded via <img src> (not the mask-image rail icons, which
   take their color from background-color and need nothing here) all bake
   in a fixed white/near-white stroke or fill for the dark canvas — SVG
   files, not CSS, so no --scuba-* token can reach them. Inverting is a
   correct, exact fix for exactly this set: every one of them is a plain
   two-tone (transparent + one near-white) glyph, so flipping every channel
   turns white-on-transparent into black-on-transparent with nothing in
   between to distort. Keyed to the asset filename via [src$=], not to a
   container class, so it reaches every place a given icon is used without
   having to be re-declared per page. */
:root.theme-light img[src$="/arrow.svg"],
:root.theme-light img[src$="/back_icon.svg"],
:root.theme-light img[src$="/fit.svg"],
:root.theme-light img[src$="/fullscreen_exit_icon.svg"],
:root.theme-light img[src$="/fullscreen_icon.svg"],
:root.theme-light img[src$="/help_small.svg"],
:root.theme-light img[src$="/link.svg"],
:root.theme-light img[src$="/reset_icon.svg"],
:root.theme-light img[src$="/search_icon.svg"],
:root.theme-light img[src$="/settings_icon.svg"],
:root.theme-light img[src$="/show_table_icon.svg"],
:root.theme-light img[src$="/theme_toggle.svg"] {
  filter: invert(1);
}

/* Small-caps label text (.chip-cat's "DISEASE"/"READOUT", .ctrl-legend-
   title's "BIOLOGICAL PROCESS", etc.) is de-emphasized with reduced opacity
   on top of its own color rather than a lighter color outright — one
   opacity value works for both a category label and a legend title without
   the two needing separate colors. That only works in one direction, though:
   fading toward a DARK background asymptotes toward a still-visible dim
   color, but fading a dark foreground toward a LIGHT background asymptotes
   toward white — the lighter the background, the less any amount of
   darkening the base color can claw back, which is why this reads as
   near-invisible pale text once the panel behind it turned light rather
   than dark. Full opacity here, with --scuba-muted's own darkened light-
   mode value (above) doing the de-emphasis instead, sidesteps the asymmetry
   rather than fighting it with an even darker base color that opacity would
   just wash out again. */
:root.theme-light .chip-cat,
:root.theme-light .ctrl-legend-title,
:root.theme-light .panel-type-label,
:root.theme-light .contcb-title,
:root.theme-light .landing-gene-card-loc-lbl,
:root.theme-light .landing-legend-head,
:root.theme-light #info-box .info-synonym {
  opacity: 1;
}

/* var(--scuba-chrome-shadow) — "none" in dark mode, a little elevation in
   light mode (see the token's own comments for why). A little elevation,
   not a redesign: this is every surface that reads it — chips, a chip's
   dropdown menu (Bootstrap's own shadow is suppressed with !important a
   few rules up, so this needs !important too to actually win, not just
   the specificity), the legend/colorbar control panels left and right
   (#control-stack's own visual rule lives here; #ctrl-stack-right's lives
   in stylesheet.py — both read this same token regardless), and their
   collapse handles (inline styles in layout.py/SCUBA.py, same variable,
   so a panel and its own handle can never end up with two different
   shadows again). #panel-rail and the shared tooltips keep their own
   distinct values — see their own rules. */
.chip-group,
.title-chip-tag,
#control-stack,
#ctrl-stack-right,
.title-chip-dd .dropdown-menu {
  box-shadow: var(--scuba-chrome-shadow) !important;
}
:root.theme-light #panel-rail {
  /* Negative x-offset: the rail sits flush against the right viewport edge
     with everything else to its left, so its shadow falls left, same
     direction as the content it's floating above. */
  box-shadow: -2px 0 6px rgba(0, 0, 0, 0.06);
}

/* Unselected rail icons/labels, one shade lighter — by request, and against
   the freshly-darkened panel background above, one more reason to: a
   --scuba-muted icon that was already fairly dark for text legibility read
   heavier still once the surface behind it darkened with it. Not touching
   --scuba-muted itself, which is shared by far more than the rail (chip
   labels, legend text, etc.) — reusing --scuba-muted-alt instead, since
   that's already exactly "one step lighter than --scuba-muted" in this
   palette, rather than inventing a third gray. .rail-icon inherits this via
   currentColor (stylesheet.py), and .rail-label via plain CSS inheritance,
   so one declaration covers both per button. Dark mode keeps --scuba-muted
   — not asked for here, and its rail already had enough contrast against
   the (unchanged) dark panel. */
:root.theme-light .rail-btn {
  color: var(--scuba-muted-alt);
}
/* Restated at matching-or-higher specificity, not left to fall out of the
   rule above: :root.theme-light .rail-btn (0,3,0) outranks stylesheet.py's
   plain .rail-btn:hover and .rail-btn:disabled (0,2,0 each) — without
   these, hovering or a disabled destination would incorrectly stay at
   --scuba-muted-alt in light mode instead of their own intended color. The
   "active destination" rule (stylesheet.py, body:not(.right-closed)
   #panel-rail.rail-active-<view> .rail-btn[data-view]) is specific enough
   already (ID + four classes/attrs) that it was never at risk here. */
:root.theme-light .rail-btn:hover {
  color: var(--scuba-rail-hover-color);
}
:root.theme-light .rail-btn:disabled,
:root.theme-light .rail-btn:disabled:hover {
  color: var(--scuba-muted-alt);
  opacity: 0.25;
}

/* Every scrolling surface in the app is listed in all four blocks below. A
   surface missing from them does not fall back to a plainer SCUBA scrollbar —
   it falls back to the browser's, which is why #panel-pane-help stood out.
   #function-drawer-body is gone from the list because that element is gone;
   #help-popover's scroller stays, since the popover still carries the
   cell-selector page's help topic. */
#search-results,
.dash-dropdown-content,
#info-card-gene,
#info-card-module,
#info-cell-card,
#panel-pane-help,
#panel-table-container,
#table-container,
#help-popover .panel-drag-header ~ div,
#welcome-modal-body {
  scrollbar-width: thin;
  scrollbar-color: var(--sb-thumb) transparent;
}

#search-results::-webkit-scrollbar,
.dash-dropdown-content::-webkit-scrollbar,
#info-card-gene::-webkit-scrollbar,
#info-card-module::-webkit-scrollbar,
#info-cell-card::-webkit-scrollbar,
#panel-pane-help::-webkit-scrollbar,
#panel-table-container::-webkit-scrollbar,
#table-container::-webkit-scrollbar,
#help-popover .panel-drag-header ~ div::-webkit-scrollbar,
#welcome-modal-body::-webkit-scrollbar                            { width: 4px; height: 4px; }

#search-results::-webkit-scrollbar-track,
.dash-dropdown-content::-webkit-scrollbar-track,
#info-card-gene::-webkit-scrollbar-track,
#info-card-module::-webkit-scrollbar-track,
#info-cell-card::-webkit-scrollbar-track,
#panel-pane-help::-webkit-scrollbar-track,
#panel-table-container::-webkit-scrollbar-track,
#table-container::-webkit-scrollbar-track,
#help-popover .panel-drag-header ~ div::-webkit-scrollbar-track,
#welcome-modal-body::-webkit-scrollbar-track                     { background: transparent; }

#search-results::-webkit-scrollbar-thumb,
.dash-dropdown-content::-webkit-scrollbar-thumb,
#info-card-gene::-webkit-scrollbar-thumb,
#info-card-module::-webkit-scrollbar-thumb,
#info-cell-card::-webkit-scrollbar-thumb,
#panel-pane-help::-webkit-scrollbar-thumb,
#panel-table-container::-webkit-scrollbar-thumb,
#table-container::-webkit-scrollbar-thumb,
#help-popover .panel-drag-header ~ div::-webkit-scrollbar-thumb,
#welcome-modal-body::-webkit-scrollbar-thumb                     { background: var(--sb-thumb); border-radius: 2px; }

/* ── Table grouped sub-header row ───────────────────────────────────────── */
/* Sticks just below the group-label row (~37 px tall) */
#table-global thead tr:nth-child(2) th,
#table-module thead tr:nth-child(2) th,
#table-landing thead tr:nth-child(2) th {
  position: sticky;
  top: 33px;
  background-color: var(--scuba-panel-bg-alt);
}

/* ── Info-pane: fit inside panel ─────────────────────────────────────────── */
#panel-pane-info #info-box {
  width: 100%;
  max-width: 100%;
  max-height: none;
  box-sizing: border-box;
}

/* ── Word-wrap for all info text ─────────────────────────────────────────── */
#info-gene-description,
#info-essential,
#info-location,
#info-biological-process,
#info-top-cell,
#info-module-title,
#info-module-category,
#info-module-certainty,
#info-module-support {
  overflow-wrap: break-word;
  word-break: break-word;
  white-space: normal;
}

/* ── Info-action buttons: equal width ────────────────────────────────────── */
.info-actions .ud-flat-btn {
  flex: 1 1 0 !important;
  min-width: 0 !important;
  max-width: none !important;
  text-align: center !important;
  justify-content: center !important;
}

/* The narrow-panel rescaling that lived here is gone with its container. Both
   halves of it — the button padding and icon geometry below, and the --info-font
   steps in stylesheet.py — were @container queries named for #multi-panel, so
   neither can match now that the card is docked. Re-pointing them at
   #panel-right would work (it would need container-type: inline-size there),
   but the case they answered has changed: a floating card had a size the user
   could only fix by resizing the card, whereas the docked panel has a divider,
   an expand button and a rail. Widening is the fix now, not shrinking the type. */

/* ── Title-bar chip dropdowns (dbc.DropdownMenu) ────────────────────────── */
.title-chip-dd { pointer-events: auto; z-index: 5000; margin: 0 !important; padding: 0 !important; display: flex; align-items: center; }

/*
 * Override Bootstrap 5 btn CSS custom properties at higher specificity
 * (.title-chip-dd .btn = 0,2,0 beats .btn-link = 0,1,0).
 * This is more reliable than !important for properties driven by CSS vars.
 */
.title-chip-dd .btn {
  --bs-btn-color:              var(--scuba-muted);
  --bs-btn-bg:                 var(--scuba-panel-bg);
  --bs-btn-border-color:       var(--scuba-wash-15);
  --bs-btn-hover-color:        var(--scuba-text);
  --bs-btn-hover-bg:           var(--hover-bg);
  --bs-btn-hover-border-color: var(--scuba-wash-15);
  --bs-btn-active-color:       var(--scuba-text);
  --bs-btn-active-bg:          var(--scuba-active-bg);
  --bs-btn-active-border-color:var(--scuba-wash-15);
  --bs-btn-focus-box-shadow:   none;
  --bs-btn-disabled-color:     var(--scuba-muted);
  /* structural props still need !important for non-var Bootstrap rules */
  margin:       0 !important;
  width:        100% !important;
  /* The chip around this button owns the border and the corners — see the
     chip-group section below. inherit is what stops a hover background from
     squaring off a corner its chip has rounded. */
  border:        none !important;
  border-radius: inherit !important;
  font-size:    15px !important;
  font-weight:  400 !important;
  padding:      3px 10px !important;
  height:       34px !important;
  box-sizing:   border-box !important;
  box-shadow:   none !important;
  text-align:   left !important;
  line-height:  1.4 !important;
  outline:      none !important;
  text-decoration: none !important;
  display:               flex !important;
  align-items:           center !important;
  gap:                   6px !important;
  /* SLATE's compiled .btn rule bakes in text-shadow:1px 1px 1px rgba(0,0,0,.3)
     — a glossy-button leftover. FLATLY (the light-mode stylesheet) never
     redeclares text-shadow on .btn at all, and SLATE stays loaded even when
     FLATLY is active (it's layered on top, not swapped out), so SLATE's
     shadow survives untouched in both themes — this is the one that showed
     up on the chip text. Inherited by .chip-cat/.chip-val below, so
     overriding it here is enough for both lines of a chip. */
  text-shadow: none !important;
}
/* Module tag: static (non-interactive) chip. Shaped for standalone use — a
   whole border and four corners, which is what #umap-title-chip needs. Inside a
   chip group the border and corners are the group's job, and .chip-group > .chip
   overrides them. It used to be shaped the other way round, carrying the
   `border-left: none` and `0 4px 4px 0` of a chip sitting last in a group, and
   the one standalone user had to undo both. */
.title-chip-tag {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  color: var(--scuba-muted);
  background: var(--scuba-panel-bg);
  border: 1px solid var(--scuba-wash-15);
  border-radius: 4px;
  font-size: 15px;
  font-weight: 400;
  padding: 3px 10px;
  height: 34px;
  box-sizing: border-box;
  white-space: nowrap;
  cursor: default;
  user-select: none;
  -webkit-user-select: none;
  /* Not .btn-classed, so SLATE's .btn text-shadow shouldn't reach this one —
     stated anyway, defensively, so it stays true if that ever changes. */
  text-shadow: none;
}

/* Module names run long ("Ubiquitin Protein Homeostasis"), and #top-bar-center is
   absolutely positioned and centred with flex-shrink: 0 — it grows outward in both
   directions toward the nav buttons with nothing to stop it. Cap the chip and let
   .chip-val ellipsise inside it; update_graph_title puts the full name in the
   chip's tooltip. */
#graph-title-module { max-width: 260px; }

/* Push caret to right edge on all chips */
.title-chip-dd .btn::after { margin-left: auto; }

/* Two-line chip label: category header above value — applies everywhere */
.chip-label {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  overflow: hidden;
  min-width: 0;
  flex: 1;
  line-height: 1.2;
}
.chip-cat {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--scuba-muted);
  opacity: 0.6;
  white-space: nowrap;
  line-height: 1.2;
}
.chip-val {
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  line-height: 1.3;
}

/* Options panel */
.title-chip-dd .dropdown-menu {
  background-color: var(--scuba-panel-bg) !important;
  border: 1px solid var(--scuba-wash-15) !important;
  border-radius: 4px !important;
  min-width: 0 !important;
  width: max-content !important;
  padding: 2px 0 !important;
  box-shadow: none !important;
  z-index: 200000 !important;
}

/* Menu items */
.title-chip-dd .dropdown-item {
  --bs-dropdown-link-color:        var(--scuba-muted);
  --bs-dropdown-link-hover-color:  var(--scuba-text);
  --bs-dropdown-link-hover-bg:     var(--hover-bg);
  --bs-dropdown-link-active-color: var(--scuba-text);
  --bs-dropdown-link-active-bg:    var(--scuba-active-bg);
  font-size:   11px !important;
  font-weight: 400 !important;
  padding:     3px 10px !important;
}

.title-chip-dd .dropdown-item.active {
  color:       var(--scuba-text) !important;
  background:  var(--scuba-active-bg) !important;
  font-weight: 600 !important;
}

/* Divider */
.title-chip-dd .dropdown-divider {
  border-color: var(--scuba-wash-15) !important;
  margin: 4px 12.5% !important;
}

/* ── Chip groups ──────────────────────────────────────────────────────────
 *
 * One ruleset for every chip group in the app: the landing row, the top bar,
 * and both panel headers. Each group is a dbc.ButtonGroup carrying
 * .chip-group, and each direct child is a .chip — a dbc.DropdownMenu with
 * group=True (so Bootstrap nests it as a button group of its own), a static
 * tag, or a wrapper that a callback fills in.
 *
 * This replaces four near-identical copies of the same idea, one per group.
 * Each named the chip that happened to be first, to round its left corners,
 * and the one that happened to be last, to drop its divider and round its
 * right corners. But every chip in every group is shown and hidden at runtime
 * by a callback writing style.display, so those ids were only ever a guess
 * about which chip would be on screen. When the guess was wrong the group drew
 * a square corner inside its own rounded one; where two of the rules disagreed
 * about which side of a seam owned the divider, it got drawn twice.
 *
 * Bootstrap's own .btn-group geometry has exactly the same blind spot — it
 * positions by :first-child/:nth-child, and a display:none child still counts
 * — so it is switched off below and re-expressed against the chips that are
 * actually visible.
 *
 * "Visible" has to be spelled out at every use, since CSS has no way to name a
 * condition once. :not([style*="display: none"]) is how the visibility
 * callbacks hide a chip — they return {display: 'none'} to hide and {}, no
 * display at all, to show — and :not(:empty) covers a wrapper chip that has
 * nothing in it yet, which is the module tag until there is a module to name.
 */
.chip-group {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--scuba-wash-15);
  border-radius: 4px;
  background: var(--scuba-panel-bg);
  flex-shrink: 0;
  pointer-events: auto;
}

/* Bootstrap's index-based spacing, off. The chip draws no border and no
   background of its own — the group draws both, once, so no chip can paint
   over the group's rounded corners. Its radius still matters, because whatever
   it wraps inherits it, and that does paint on hover. */
.chip-group > .chip {
  margin: 0 !important;
  border: none;
  border-radius: 4px;
  background: transparent;
}

/* An empty chip is a wrapper waiting on a callback: nothing to draw, and
   nothing that should take up room between the chips either side of it. */
.chip-group > .chip:empty { display: none; }

/* The divider is one border, owned by the chip to its right, so no two
   neighbours can each contribute half of it. This is every chip that follows a
   visible chip — that is, all but the first.
   Only the chip being LOOKED AT has to be tested for visibility; the one being
   styled does not, because a hidden chip paints nothing and occupies nothing.
   That is why the test appears once per rule rather than on both sides. */
.chip-group > .chip:not([style*="display: none"]):not(:empty) ~ .chip {
  border-left: 1px solid var(--scuba-wash-15);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

/* And every chip with a visible chip after it — all but the last.
   :has() is what reads forward; a sibling combinator only reads backwards. */
.chip-group > .chip:has(~ .chip:not([style*="display: none"]):not(:empty)) {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

/* What actually gets painted is whatever the chip wraps: a Bootstrap button
   for a dropdown, the tag's own box for a static one. Inheriting the radius is
   what keeps a hover or active background inside the corners the chip rounded
   — and it is why .title-chip-dd .btn sets border-radius: inherit !important
   above, since Bootstrap's own .btn-group rules would otherwise square the
   inner button off by child index. That !important rule already covers every
   button in a group (each one is inside a .title-chip-dd), so only the wrapper
   needs naming here. */
.chip-group > .chip .title-chip-dd {
  border-radius: inherit;
  display: flex;
  height: 100%;
}


/* ── Unified tooltip token ───────────────────────────────────────── */
/* All FOUR tooltip surfaces share this visual style. JS toggles      */
/* display: none → block; individual rules below add layout overrides.*/
/*                                                                    */
/* #ud-tooltip joined this group rather than keeping its own copy in  */
/* user_data.css. The copy had drifted in the way copies do: same     */
/* background, border, radius, size, weight and shadow, but at 0.95   */
/* alpha instead of 0.9 — and, because its element sits inside        */
/* #upload-page, it inherited that page's font while its three twins  */
/* inherited the chrome's. Four tooltips meant to be identical cannot */
/* be identical from two files.                                       */
/*                                                                    */
/* Family is deliberately absent here: these inherit the app's stack  */
/* from :root, which is what makes them follow --scuba-font for free. */
#nav-tooltip,
#cy-tooltip,
#landing-tooltip,
#ud-tooltip {
  position: fixed;
  display: none;
  pointer-events: none;
  background: var(--scuba-tooltip-bg);
  border: 1px solid var(--scuba-tooltip-border);
  border-radius: 5px;
  color: var(--scuba-tooltip-text);
  font-size: 11px;
  font-weight: 400;
  line-height: 1.45;
  padding: 6px 10px;
  /* Matched to the same weight every other chrome shadow uses (chips,
     the rail, the control panels) — was 0 3px 12px rgba(0,0,0,0.6), heavy
     enough to stand out as its own, inconsistent design rather than read
     as part of the same elevation system. Unlike those, this one isn't
     scoped to light mode: a tooltip floats over arbitrary content in
     either theme, not just the canvas, so it keeps a little shadow in
     both rather than going flat in dark mode. */
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

/* What the tooltip is ABOUT — the module title or gene symbol — against the
   readings underneath it, which are all one weight. 600 is the app's title
   weight (stylesheet.py), real on every platform the stack targets, and it is
   the whole reason the title is its own element: a tooltip built as one
   textContent string had no way to weight a single line of itself. Written by
   _scubaSetTip in cy_direct.js, for all three node-tooltip callers. */
#cy-tooltip .tip-title {
  font-weight: 600;
}

#nav-tooltip {
  z-index: 200000;
  max-width: 280px;
  white-space: normal;
}

/* Paper citations (catalog picker) — longer sentences read better slightly
   smaller and a bit wider than the default short-tip sizing, so the wrap
   breaks at more natural points instead of a narrow ragged column. */
#nav-tooltip.nav-tooltip-cite {
  font-size: 10px;
  line-height: 1.5;
  max-width: 340px;
}

#cy-tooltip {
  z-index: 200000;
  white-space: pre;
}

#landing-tooltip {
  z-index: 9999;
  white-space: nowrap;
}

/* Cell-type selection landing page */
#cell-selector-page {
  position: fixed;
  inset: 0;
  background: var(--scuba-landing-page-bg);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 90;
}

/* ── Control panel toggle button ────────────────────────────────────────── */
#btn-collapse-controls,
#btn-collapse-controls-right {
  background-color: var(--scuba-panel-bg) !important;
  border: 1px solid var(--scuba-wash-15) !important;
  color: var(--scuba-muted) !important;
}
#btn-collapse-controls {
  border-left: none !important;
}
#btn-collapse-controls-right {
  border-right: none !important;
}
#btn-collapse-controls:hover,
#btn-collapse-controls-right:hover {
  background-color: var(--hover-bg) !important;
  color: var(--scuba-text) !important;
}

/* ── Unified control stack ───────────────────────────────────────────────── */
#control-stack {
  background-color: var(--scuba-panel-bg);
  border: 1px solid var(--scuba-wash-15);
  border-left: none;
  color: var(--scuba-light-text);
  border-radius: 0;
  /* Bottom-anchored, the stack grows upward into the top bar once it is taller
     than the space it has; style.css caps it, so the overflow has to scroll
     rather than be hidden outright. */
  overflow-x: hidden;
  overflow-y: auto;
}

/* Nav-bar hover — defined here so var(--hover-bg) resolves in the same file */
#nav-bar .btn:hover { background-color: var(--hover-bg) !important; }

/* Strip glass/card styling from control-stack children */
#control-stack #nav-bar,
#control-stack #control-box,
#control-stack #color-control-wrap,
#color-control-wrap #my-multi-dropdown,
#color-control-wrap .contcb-card,
#ctrl-stack-right #ctrl-box-right,
#ctrl-stack-right #color-control-wrap-right,
#color-control-wrap-right #my-multi-dropdown-right,
#color-control-wrap-right .contcb-card {
  background: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
}
#control-stack #nav-bar,
#control-stack #nav-bar button { pointer-events: auto !important; }
#control-stack #nav-bar        { overflow: visible; }
#control-stack #control-box    { display: flex; flex-direction: column; overflow: visible; }
#ctrl-stack-right #ctrl-box-right { display: flex; flex-direction: column; overflow: visible; }
#control-stack #search-wrap    { position: relative; }

/* Color control: keep z-index for dropdown */
#color-control-wrap {
  position: relative;
  z-index: 4001;
  overflow: visible;
}

/* Horizontal continuous colorbar */
.contcb-bar {
  height: 10px !important;
  width: auto !important;
  flex: 1 !important;
  /* 2px, not the 5px that made this a pill at 10px tall. The ends of this bar
     are the part being read — they are what the two labels either side name —
     and a full capsule tapers away exactly the extreme colour they point at.
     It is also a control, not a badge: it drag-selects a range
     (assets/colorbar_select.js, see pointer-events below), which wants ends you
     can aim at. Squared off entirely would read as unstyled next to the 4px
     everything else in the app is cut to, and 2px on a 10px bar is about the
     same proportion as 4px on a card. */
  border-radius: 2px !important;
  border: none !important;
  margin: 0 !important;
  /* Click-and-drag range selection (assets/colorbar_select.js) needs this
     specific element interactive even though its floating-legend ancestor
     is pointer-events:none (so the rest of the overlay doesn't block clicks
     on the graph behind it) — pointer-events isn't blocked by an ancestor's
     none once a descendant sets its own value explicitly. */
  position: relative !important;
  pointer-events: auto !important;
  cursor: crosshair !important;
}

.contcb-card  { padding-left: 10px !important; padding-right: 10px !important; }
/* The `.contcb-title { text-align: left; margin: 0 }` that sat here is gone —
   its margin reset was what removed the gap under every legend title. Both
   declarations now live in the base rule in stylesheet.py. */
.contcb-txt   { white-space: nowrap !important; }

/* ── Info pane: transparent, so #panel-right's own background shows ──────── */
#panel-pane-info {
  background: transparent !important;
  background-color: transparent !important;
}
/* UMAP canvas in right panel — transparent background */
#panel-right-body #umap-canvas,
#panel-right-body #umap-placeholder {
  background: transparent !important;
}

/* ── Control panel legend sections (shared design) ───────────────────────── */
.ctrl-legend { padding: 6px 8px; }
/* Same chip-label look as .contcb-title (stylesheet.py) — these are the control
   panel's legends and there is no reason for their titles to read differently
   from the floating one's. Change the two together. */
.ctrl-legend-title {
  color: var(--scuba-muted);
  font-weight: 400;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  line-height: 1.2;
  margin-bottom: 8px;
  opacity: 0.6;
  white-space: nowrap;
}
.ctrl-legend-items {
  display: flex;
  align-items: center;
  gap: 14px;
}
.ctrl-legend-item {
  display: flex;
  align-items: center;
  gap: 5px;
  /* Hover and the lit state differ only in colour, so the band geometry lives
     here rather than being repeated in both — duplicated, the two could drift.
     Negative margins pull the row out through the legend panel's 10px side
     padding so both run edge to edge like a chip-menu row; the matching padding
     puts the swatch and label back. !important because the buttons carry
     width/padding as inline style. */
  margin-left: -10px !important;
  margin-right: -10px !important;
  padding-left: 10px !important;
  padding-right: 10px !important;
  width: calc(100% + 20px) !important;
  border-radius: 0 !important;
}
/* Standard app-wide hover highlight (var(--hover-bg), same token #ud-nav-bar/
   #nav-bar/.dash-dropdown-option use) — covers every clickable legend item,
   category and significance, left and right panel alike, since all four
   share this one class. !important overrides the inline
   backgroundColor:"transparent" each button sets directly. */
.ctrl-legend-item:hover {
  background-color: var(--hover-bg) !important;
}

/* Focus ring, not the lit state: these are <button>s, so clicking one leaves it
   focused and the browser/Bootstrap ring persists after the pointer moves away —
   which read as the white border coming back, most visibly on shift-click since
   that keeps the focus while the stack changes underneath.
   :focus-visible is deliberately left alone, so keyboard tabbing still shows
   where it is; only pointer focus is silenced. */
.ctrl-legend-item:focus,
.ctrl-legend-item:focus:not(:focus-visible) {
  outline: none !important;
  box-shadow: none !important;
  border: none !important;
}
/* The swatch whose nodes are the current selection stays lit until that
   selection ends (assets/legend_select.js owns the class). Styled to match
   `.title-chip-dd .dropdown-item.active` above — same #343743, flat, no radius
   and no ring — so a lit legend row and a selected chip-menu row read as the
   same state rather than two dialects of "active".

   The negative margins pull the band out through the legend panel's own 10px
   side padding so it runs edge to edge like a menu row; the matching padding
   puts the swatch and label back where they were. !important throughout because
   the buttons carry width/padding as inline style, which would otherwise win. */
.ctrl-legend-item.leg-active {
  background-color: var(--scuba-active-bg) !important;
}

.ctrl-legend-label {
  font-size: 11px;
  color: var(--scuba-muted);
}
.ctrl-legend-item.leg-active .ctrl-legend-label {
  color: var(--scuba-text);
}

#size-legend-m .ctrl-legend-items {
  width: 100%;
  justify-content: space-between;
  gap: 0;
}
#size-legend-m .ctrl-legend-item {
  flex: 1;
  justify-content: center;
}

/* ── Info panel markdown sections ────────────────────────────────────────── */
.info-md { font-size: 12px; }

/* ── External link icon ──────────────────────────────────────────────────── */
.ext-link-icon {
  width: 8px;
  height: 8px;
  margin-left: 5px;
  vertical-align: middle;
  opacity: 0.7;
  position: relative;
  top: -1px;
}
