/* ArthOMix Explorer design system.
   Token-based, restrained blue/white scientific SaaS look. Every shared
   Shiny/shinydashboard primitive used across the app (box(), valueBox(),
   navbarPage's own nav markup, tabsetPanel's nav-tabs, DT tables, buttons,
   inputs) is reskinned here so all 15 sub-modules inherit the same system
   without any of their own R code changing. App-shell-specific classes
   (.app-header, .omics-sidebar, .workflow-stepper, .pipeline-summary,
   .scale-check-panel, ...) back the new components in R/ui_shell.R and the
   Preprocessing flagship page. */

:root {
  --color-primary: #2563EB;
  --color-primary-dark: #1D4ED8;
  --color-ink: #172033;
  --color-ink-secondary: #64748B;
  --color-border: #E2E8F0;
  --color-surface-blue: #EFF6FF;
  --color-success: #16A34A;
  --color-success-surface: #F0FDF4;
  --color-warning: #F59E0B;
  --color-warning-surface: #FFFBEB;
  --color-page-bg: #F8FAFC;
  --radius-card: 10px;
  --radius-sm: 6px;
}

/* ---------------------------------------------------------------------------
   Typography
   --------------------------------------------------------------------------- */

html, body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: var(--color-ink);
  background: var(--color-page-bg);
}
input, button, select, textarea, .btn, .form-control, .selectize-input, code, pre, kbd {
  font-family: inherit;
}

h1, h2, h3, h4, h5, h6 {
  font-family: inherit;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-ink);
}

.container-fluid {
  background: var(--color-page-bg);
}

.page-header h2 {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 4px;
}
.page-header-tight { margin: 6px 0 10px 0; }
.page-header-tight p { font-size: 13px; }
.page-header-compact h2 { font-size: 22px; }
.page-header-compact p { font-size: 13.5px; }
.page-header p {
  color: var(--color-ink-secondary);
  max-width: 900px;
  font-size: 14.5px;
  line-height: 1.6;
}
.page-header { margin: 6px 0 20px 0; position: relative; }

/* Optional, extremely subtle scientific/network pattern in the upper-right
   of a page header - decorative only, never under the title text. */
.page-header-pattern {
  position: absolute;
  top: 0; right: 0;
  width: 260px; height: 100%;
  min-height: 70px;
  pointer-events: none;
  opacity: 0.5;
  background-image: radial-gradient(circle, var(--color-border) 1.4px, transparent 1.4px);
  background-size: 20px 20px;
  -webkit-mask-image: linear-gradient(to left, black, transparent);
  mask-image: linear-gradient(to left, black, transparent);
}

.box-header .box-title {
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--color-ink);
}
.small-box h3, .info-box-number { font-weight: 700; letter-spacing: -0.01em; }
.small-box p, .info-box-text { font-weight: 500; letter-spacing: 0.01em; }
p, label, .control-label { line-height: 1.55; }

/* ---------------------------------------------------------------------------
   Card system - every box()/valueBox() in the app renders through this
   --------------------------------------------------------------------------- */

/* .box.box-primary (every box() in this app - all called with
   status = "primary") beats plain .box on specificity, so AdminLTE's own
   saturated border color wins unless overridden at the same specificity;
   !important guarantees it regardless of stylesheet load order too. */
.box, .box.box-primary {
  background: #ffffff;
  box-shadow: none;
  border: 1px solid var(--color-border) !important;
  border-radius: var(--radius-card);
}
.box.box-primary > .box-header,
.box.box-solid.box-primary > .box-header {
  border-bottom: 1px solid var(--color-border);
  background: transparent;
}
.box-header { padding: 14px 18px 10px 18px; }
.box-body { padding: 16px 18px; overflow-x: auto; }

.small-box, .info-box {
  box-shadow: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
}

/* valueBox()/infoBox(): AdminLTE's solid saturated fills (bg-red, bg-purple,
   bg-green, ...) read as loud/inconsistent next to the rest of this
   restrained palette. Map every color= choice to a light tint + dark text
   instead - keeps color meaningful (red = flagged, green = good) without
   the "block of bright color" look. */
.small-box, .small-box.bg-aqua, .small-box.bg-light-blue, .small-box.bg-blue, .small-box.bg-navy,
.small-box.bg-teal, .small-box.bg-purple, .small-box.bg-fuchsia, .small-box.bg-black, .small-box.bg-gray {
  background: var(--color-surface-blue) !important;
}
.small-box.bg-green, .small-box.bg-olive, .small-box.bg-lime {
  background: var(--color-success-surface) !important;
}
.small-box.bg-red, .small-box.bg-maroon {
  background: #FEF2F2 !important;
}
.small-box.bg-yellow, .small-box.bg-orange {
  background: var(--color-warning-surface) !important;
}
.small-box h3, .small-box p {
  color: var(--color-ink) !important;
  text-shadow: none !important;
}
.small-box.bg-green h3, .small-box.bg-olive h3, .small-box.bg-lime h3 { color: var(--color-success) !important; }
.small-box.bg-red h3, .small-box.bg-maroon h3 { color: #B91C1C !important; }
.small-box.bg-yellow h3, .small-box.bg-orange h3 { color: #8A5A00 !important; }
.small-box.bg-aqua h3, .small-box.bg-light-blue h3, .small-box.bg-blue h3, .small-box.bg-navy h3,
.small-box.bg-teal h3, .small-box.bg-purple h3, .small-box.bg-fuchsia h3, .small-box.bg-black h3, .small-box.bg-gray h3 {
  color: var(--color-primary-dark) !important;
}
.small-box p { color: var(--color-ink-secondary) !important; font-weight: 500; }
.small-box .icon { color: var(--color-ink) !important; opacity: 0.12; }
.small-box .small-box-footer {
  background: transparent !important;
  color: var(--color-ink-secondary) !important;
  border-top: 1px solid var(--color-border);
}

.info-card {
  background: #ffffff;
  border-radius: var(--radius-card);
  padding: 20px;
  min-height: 190px;
  border: 1px solid var(--color-border);
  margin-bottom: 20px;
  overflow-wrap: break-word;
  transition: border-color 0.15s ease;
}
.info-icon { font-size: 22px; color: var(--color-primary); margin-bottom: 8px; }
.info-card h4 { font-weight: 600; margin-bottom: 8px; }
.info-card p { color: var(--color-ink-secondary); font-size: 13.5px; }

/* Reusable plain card, for the new flagship-page sections that aren't
   shinydashboard box()es (data-source picker, source-data panel, right
   column). Same recipe as .box, available as a plain div wrapper. */
.card {
  background: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  padding: 20px;
  margin-bottom: 16px;
}
.card-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--color-ink);
  margin: 0 0 4px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.card-title svg, .card-title .fa { color: var(--color-primary); }
.card-subtitle {
  color: var(--color-ink-secondary);
  font-size: 13px;
  margin: 0 0 14px 0;
}

/* ---------- Buttons ---------- */

.btn-primary {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
  background-color: var(--color-primary-dark) !important;
  border-color: var(--color-primary-dark) !important;
}
.btn-default {
  border-color: var(--color-border);
  color: var(--color-ink);
}
.btn { border-radius: var(--radius-sm); font-weight: 500; }

/* ---------- Module cards ---------- */

.module-group-title { font-weight: 600; color: var(--color-ink); margin: 18px 0 10px 2px; }
.module-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 16px;
  margin-bottom: 10px;
}
.module-card {
  display: flex;
  gap: 16px;
  background: #ffffff;
  border-radius: var(--radius-card);
  padding: 20px;
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-primary);
}
.module-card-disabled { border-left: 4px solid #C3C9D1; opacity: 0.85; }
.module-card-icon { font-size: 26px; color: var(--color-primary); width: 34px; flex-shrink: 0; padding-top: 4px; }
.module-card-disabled .module-card-icon { color: #9AA2AC; }
.module-card-body { flex: 1; }
.module-card-title-row { display: flex; align-items: center; justify-content: space-between; }
.module-card-title-row h4 { font-weight: 600; margin: 0; }
.module-card-tagline { color: var(--color-ink-secondary); font-size: 13.5px; margin: 6px 0 12px 0; }
.badge-available {
  background: var(--color-success); color: #fff; font-size: 11px; font-weight: 600;
  padding: 3px 8px; border-radius: 10px; text-transform: uppercase; letter-spacing: 0.03em;
}
.badge-soon {
  background: #9AA2AC; color: #fff; font-size: 11px; font-weight: 600;
  padding: 3px 8px; border-radius: 10px; text-transform: uppercase; letter-spacing: 0.03em;
}

/* ---------- Coming soon panel ---------- */

.coming-soon { text-align: center; padding: 46px 20px; color: var(--color-ink-secondary); }
.coming-soon-icon { font-size: 34px; color: #9AA2AC; margin-bottom: 14px; }

/* ---------- Transcriptomics submodules ---------- */

.submodule-intro h3 { font-weight: 600; margin-bottom: 6px; }
.submodule-desc { color: var(--color-ink-secondary); max-width: 900px; margin-bottom: 12px; font-size: 13.5px; line-height: 1.5; }
/* For a .submodule-desc sitting bare between two box()es (not inside one
   itself) - lines its text up with the boxes' own content padding
   (.box-body's 18px) instead of starting flush at the column edge. */
.desc-aligned { padding-left: 18px; }

.section-tag { margin-bottom: 10px; }
.badge-section {
  background: var(--color-surface-blue); color: var(--color-primary-dark);
  font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 4px;
}

.empty-note {
  color: var(--color-ink-secondary);
  background: #F4F6F9;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
  padding: 14px;
  font-size: 13.5px;
  line-height: 1.5;
}
.empty-note svg, .empty-note .fa { margin-right: 6px; color: var(--color-primary); }

/* Small SaaS-style hover tooltip - a circular info icon next to a field
   label that reveals a dark floating card on hover/focus. Pure CSS, no JS
   tooltip library. See mod_pp_field_hint() in R/mod_preprocessing.R. */
.field-label-with-hint {
  display: flex; align-items: center; gap: 6px;
  font-weight: 600; font-size: 14px; color: var(--color-ink);
  margin-bottom: 4px;
}
.field-hint {
  position: relative;
  display: inline-flex;
  color: var(--color-ink-secondary);
  cursor: help;
  outline: none;
}
.field-hint svg, .field-hint .fa { width: 13px; height: 13px; }
.field-hint-box {
  position: absolute;
  bottom: 130%;
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: 250px;
  background: var(--color-ink);
  color: #fff;
  font-weight: 400;
  font-size: 12.5px;
  line-height: 1.45;
  padding: 8px 10px;
  border-radius: 8px;
  box-shadow: 0 10px 24px -8px rgba(8, 15, 35, 0.35);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 40;
  pointer-events: none;
}
.field-hint-box::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--color-ink);
}
.field-hint:hover .field-hint-box, .field-hint:focus .field-hint-box {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

.box-header .box-title svg, .box-header .box-title .fa { margin-right: 6px; color: var(--color-primary); }

.filter-section-header {
  font-weight: 700; color: var(--color-primary-dark); font-size: 12.5px;
  text-transform: uppercase; letter-spacing: 0.04em; margin: 4px 0 10px 0;
}
.filter-section-header svg, .filter-section-header .fa { margin-right: 5px; color: var(--color-primary); }

.upload-step-label {
  font-weight: 700; color: var(--color-primary-dark); font-size: 12.5px;
  text-transform: uppercase; letter-spacing: 0.04em; margin: 4px 0 10px 0;
}

.table-dims { color: var(--color-ink-secondary); font-size: 12.5px; padding-top: 8px; }
.table-toolbar { text-align: right; margin-bottom: 8px; }

/* ---------- Figure gallery ---------- */

.figure-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
.figure-card { background: #ffffff; border: 1px solid var(--color-border); border-radius: var(--radius-card); padding: 10px; }
.figure-img { width: 100%; height: auto; border-radius: 3px; cursor: zoom-in; transition: opacity 0.15s ease; }
.figure-img:hover { opacity: 0.85; }
.figure-caption { font-size: 11.5px; color: #8A929C; margin-top: 6px; word-break: break-all; }

/* ---------- navlistPanel (submodule nav) ---------- */

.nav-pills > li > a { color: #33393F; border-radius: var(--radius-sm); }
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
  background-color: var(--color-primary); color: #ffffff;
}
.nav-stacked > li + li { margin-top: 4px; }
.nav-pills > li.disabled > a {
  color: #9AA2AC; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; cursor: default; padding-top: 14px;
}

/* ---------- DataTables ---------- */

.dataTables_wrapper { width: 100%; max-width: 100%; overflow-x: auto; }
table.dataTable thead th { border-bottom: 2px solid var(--color-border) !important; white-space: nowrap; }
.dataTables_wrapper .dataTables_filter input { border-radius: var(--radius-sm); }
.dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter, .dataTables_wrapper .dataTables_paginate { font-size: 12.5px; }

/* ---------- ArthOChat shortcuts ---------- */

.arthochat-shortcut {
  display: flex; align-items: center; gap: 10px;
  background: var(--color-surface-blue); border: 1px solid #D4E2F3; border-radius: var(--radius-card);
  padding: 10px 14px; margin-bottom: 14px; color: var(--color-primary-dark); font-size: 13px;
}
.arthochat-shortcut svg, .arthochat-shortcut .fa { color: var(--color-primary); flex-shrink: 0; }
.arthochat-shortcut span { flex: 1 1 auto; }

.arthochat-shortcut-compact {
  display: block; padding: 16px;
  background: #ffffff; border: 1px solid var(--color-border); border-radius: var(--radius-card);
}
.arthochat-shortcut-compact svg, .arthochat-shortcut-compact .fa { color: var(--color-primary); }
.arthochat-shortcut-compact strong { color: var(--color-ink); }
.arthochat-shortcut-compact p { margin: 6px 0 12px 0; font-size: 12.5px; line-height: 1.5; color: var(--color-ink-secondary); }

.pipeline-status-strip { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.pipeline-status-chip {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600;
  padding: 5px 12px; border-radius: 999px; border: 1px solid transparent;
}
.pipeline-status-chip.status-done { background: var(--color-success-surface); color: #1E7A3C; border-color: #C3E6CD; }
.pipeline-status-chip.status-pending { background: var(--color-warning-surface); color: #8A5A00; border-color: #F2DDB0; }
.pipeline-status-chip.status-neutral { background: #EEF1F4; color: var(--color-ink-secondary); border-color: var(--color-border); }

/* ---------- Evidence checklist (Biomarker Card: "Evidence Status") ----------
   Long sentence-length labels ("Biological evidence (pathway/GO/disease/
   drug/PPI/expression/literature)") wrap badly as pill chips, so this is a
   plain two-column checklist instead: icon + label per row, no pill background. */
.evidence-tier-banner { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.evidence-tier-count { font-size: 12.5px; color: var(--color-ink-secondary); }

.evidence-checklist { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 0 28px; }
.evidence-checklist-item {
  display: flex; align-items: flex-start; gap: 9px; font-size: 13px; line-height: 1.45;
  padding: 8px 0; border-bottom: 1px solid var(--color-border);
}
.evidence-checklist-item:last-child { border-bottom: none; }
.evidence-checklist-item svg, .evidence-checklist-item .fa { flex-shrink: 0; margin-top: 2px; font-size: 13px; }
.evidence-checklist-item.is-done { color: var(--color-ink); }
.evidence-checklist-item.is-done svg, .evidence-checklist-item.is-done .fa { color: var(--color-success); }
.evidence-checklist-item.is-pending { color: var(--color-ink-secondary); }
.evidence-checklist-item.is-pending svg, .evidence-checklist-item.is-pending .fa { color: var(--color-warning); }
.evidence-checklist-item.is-neutral { color: var(--color-ink-secondary); font-style: italic; }
.evidence-checklist-item.is-neutral svg, .evidence-checklist-item.is-neutral .fa { color: #B8C0CC; }

/* ---------- Feature Selection: sticky run bar ----------
   Pins the "Run Female"/"Run Male" buttons to the top of their box - not
   fixed to the viewport, so it still respects the box's own bounds - so
   they stay reachable while scrolling down through the data-source and
   group settings below, instead of sitting off-screen at the bottom of a
   long form. Same position: sticky; top: 12px; convention as
   .pipeline-rail/.omics-sidebar-col elsewhere in this file. */
.fs-run-bar {
  position: sticky;
  top: 12px;
  z-index: 5;
  background: #ffffff;
  padding-bottom: 12px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--color-border);
}

/* ---------- Feature Selection: female/male result pair rows ----------
   Each LASSO/Random Forest/SVM-RFE/Overlap tab shows a Female box next to a Male
   box, each gated behind its own Run button via conditionalPanel (rendered as
   .shiny-panel-conditional, hidden with display:none until clicked). Fixed
   Bootstrap column(6)/column(6) reserved half the row for a sex that hadn't
   been run yet, leaving it permanently blank instead of letting the sex that
   HAS run use that space. Flexbox instead: a hidden (display:none) conditional
   panel drops out of the flex layout entirely, so a lone visible result gets
   flex:1 all to itself and fills the row; once both are visible they split it
   evenly again, same as the old 50/50 columns. */
.fs-pair-row {
  display: flex;
  width: 100%;
  gap: 20px;
  align-items: flex-start;
}
.fs-pair-row > .shiny-panel-conditional {
  flex: 1 1 0;
  min-width: 0;
}

/* =============================================================================
   APP SHELL: header, primary navigation, sidebar, stepper, right-column panels
   ============================================================================= */

/* ---------- Header ---------- */

.app-header {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 10px 20px;
  background: #ffffff;
  border-bottom: 1px solid var(--color-border);
}
.app-header-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  font-size: 17px;
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}
.app-header-search {
  flex: 1 1 auto;
  max-width: 520px;
  margin: 0 auto;
  position: relative;
}
.app-header-search input {
  width: 100%;
  padding: 7px 14px 7px 34px;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  font-size: 13px;
  background: var(--color-page-bg);
  height: 34px;
}
.app-header-search input:focus {
  outline: none;
  border-color: var(--color-primary);
  background: #ffffff;
}
.app-header-search-icon {
  position: absolute;
  left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--color-ink-secondary);
  font-size: 12px;
  pointer-events: none;
}
.app-header-search-kbd {
  position: absolute;
  right: 10px; top: 50%; transform: translateY(-50%);
  font-size: 10.5px;
  color: var(--color-ink-secondary);
  background: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  padding: 1px 5px;
  pointer-events: none;
}
.app-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  margin-left: auto;
}
.app-header-actions .btn-primary {
  padding: 6px 14px;
  font-size: 13px;
  border-radius: 999px;
}
.app-header-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: #ffffff;
  color: var(--color-ink-secondary);
  cursor: pointer;
}
.app-header-icon-btn:hover { background: var(--color-page-bg); }
.app-header-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--color-primary);
  color: #ffffff;
  font-size: 12.5px;
  font-weight: 700;
  flex-shrink: 0;
}

/* ---------- Primary navigation (navbarPage's own markup) ---------- */

/* White bar (was a solid blue fill) so the item text can be black at rest -
   black-on-blue and blue-on-hover would both fail to read against a blue
   bar itself. Text: black by default, blue on hover/focus - the same
   hover-turns-blue treatment already used everywhere else in this design
   system (.sidebar-nav-item, .tx-menu-wrap tabs, module cards). */
.navbar {
  background: #ffffff !important;
  border: none;
  border-bottom: 1px solid var(--color-border);
  border-radius: 0;
  margin-bottom: 0;
  min-height: 44px;
}
.navbar-default .navbar-nav {
  display: flex;
  gap: 2px;
  padding: 5px 14px;
}
.navbar-default .navbar-nav > li > a {
  color: var(--color-ink) !important;
  font-size: 13.5px;
  font-weight: 500;
  padding: 8px 14px;
  border-radius: 999px;
  background: transparent !important;
  display: flex;
  align-items: center;
  gap: 6px;
}
.navbar-default .navbar-nav > li > a svg, .navbar-default .navbar-nav > li > a .fa {
  font-size: 12px;
}
.navbar-default .navbar-nav > li > a:hover,
.navbar-default .navbar-nav > li > a:focus {
  color: var(--color-primary) !important;
  background: var(--color-surface-blue) !important;
}
.navbar-default .navbar-nav > li.active > a,
.navbar-default .navbar-nav > li.active > a:hover,
.navbar-default .navbar-nav > li.active > a:focus {
  color: var(--color-primary) !important;
  background: var(--color-surface-blue) !important;
  font-weight: 600;
}
.navbar-default .navbar-brand, .navbar-default .navbar-header { display: none; }

/* ---------- Left sidebar ---------- */

.omics-sidebar {
  padding: 18px 14px;
}
.omics-sidebar-heading {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--color-ink-secondary);
  text-transform: uppercase;
  margin: 4px 4px 10px 4px;
}
.sidebar-nav {
  list-style: none;
  margin: 0 0 20px 0;
  padding: 0;
}
.sidebar-nav-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 12px;
  margin-bottom: 2px;
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  border-left: 3px solid transparent;
  cursor: pointer;
}
.sidebar-nav-item svg, .sidebar-nav-item .fa {
  color: var(--color-ink-secondary);
  font-size: 12.5px;
  width: 14px;
}
.sidebar-nav-item:hover {
  background: var(--color-page-bg);
  color: var(--color-ink);
  text-decoration: none;
}
.sidebar-nav-item.active {
  background: var(--color-surface-blue);
  color: var(--color-primary);
  border-left-color: var(--color-primary);
  font-weight: 600;
}
.sidebar-nav-item.active svg, .sidebar-nav-item.active .fa { color: var(--color-primary); }

.sidebar-quicklinks {
  border-top: 1px solid var(--color-border);
  padding-top: 14px;
  margin-bottom: 20px;
}
.sidebar-quicklinks a {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 12px;
  font-size: 13px;
  color: var(--color-ink-secondary);
  text-decoration: none;
}
.sidebar-quicklinks a:hover { color: var(--color-primary); text-decoration: none; }
.sidebar-quicklinks a svg, .sidebar-quicklinks a .fa { font-size: 12px; width: 14px; }

@media (min-width: 992px) {
  .omics-sidebar-col {
    position: sticky;
    top: 12px;
    align-self: flex-start;
  }
}

/* Right-column rail (ArthOChat shortcut + Pipeline summary card), used by
   Preprocessing and WGCNA next to a main column whose content - a long
   batch-correction results panel, a 6-step WGCNA wizard - can run much
   taller than the rail itself. Pinning it the same way the app's own left
   sidebar already is (position: sticky, not fixed: it still respects its
   own column's bounds, so it can never visually intrude into the main
   column next to it) keeps it in view without it ever needing to grow
   into or past the main content. max-height + overflow-y is the safety
   valve for the rare case where the rail's OWN content (ArthOChat card +
   a long multi-step Pipeline summary) is taller than the viewport - it
   scrolls internally instead of spilling past the fold. */
@media (min-width: 992px) {
  .pipeline-rail {
    position: sticky;
    top: 12px;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
  }
}

/* ---------- Secondary tab navigation (tx_menu tabsetPanel) ---------- */

.tx-menu-wrap .nav-tabs {
  border-bottom: 1px solid var(--color-border);
}
.tx-menu-wrap .nav-tabs > li > a {
  border: none !important;
  background: transparent !important;
  border-radius: 0 !important;
  color: var(--color-ink-secondary);
  font-size: 13.5px;
  font-weight: 500;
  padding: 10px 4px;
  margin-right: 22px;
  border-bottom: 2px solid transparent !important;
}
.tx-menu-wrap .nav-tabs > li.active > a,
.tx-menu-wrap .nav-tabs > li.active > a:hover,
.tx-menu-wrap .nav-tabs > li.active > a:focus {
  color: var(--color-primary);
  border-bottom: 2px solid var(--color-primary) !important;
  font-weight: 600;
}
.tx-menu-wrap .nav-tabs > li > a:hover { color: var(--color-ink); background: transparent !important; }

/* ---------- Data-source selection card ---------- */

.data-source-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
}
.data-source-option.selected { background: var(--color-surface-blue); border-color: #D4E2F3; }
.data-source-option input[type="radio"] { margin-top: 3px; accent-color: var(--color-primary); }
.data-source-option-title { font-weight: 600; font-size: 13.5px; color: var(--color-ink); }
.data-source-option-desc { font-size: 12.5px; color: var(--color-ink-secondary); margin-top: 2px; }
.data-source-callout {
  display: flex;
  gap: 10px;
  background: var(--color-surface-blue);
  border: 1px solid #D4E2F3;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-top: 14px;
  font-size: 12.5px;
  color: var(--color-primary-dark);
  line-height: 1.5;
}
.data-source-callout svg, .data-source-callout .fa { color: var(--color-primary); flex-shrink: 0; margin-top: 2px; }

/* ---------- Scale check panel ---------- */

.scale-check-panel { padding-top: 2px; }
.scale-check-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  background: var(--color-page-bg);
  border-radius: var(--radius-sm);
  margin-top: 12px;
}
.scale-check-row + .scale-check-row { margin-top: 8px; }
.scale-check-label { font-size: 12.5px; color: var(--color-ink-secondary); font-weight: 500; }
.scale-check-value { font-size: 13.5px; font-weight: 700; color: var(--color-ink); }
.badge-scale {
  display: inline-block;
  background: var(--color-success-surface);
  color: #1E7A3C;
  border: 1px solid #C3E6CD;
  font-size: 11.5px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
}
.badge-scale.linear { background: var(--color-warning-surface); color: #8A5A00; border-color: #F2DDB0; }

/* Differential expression: up/down-regulated gene count chips. */
.badge-up, .badge-down {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 700;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.badge-up { background: #FEF2F2; color: #B91C1C; border-color: #FBD5D5; }
.badge-down { background: var(--color-surface-blue); color: var(--color-primary-dark); border-color: #D4E2F3; }

/* Header: always-visible "which dataset is active" badge - neutral grey for
   this app's own preloaded example, blue (matching .badge-down) for anyone's
   own uploaded/merged/corrected data, so the two are never ambiguous no
   matter which page you're on. */
.header-dataset-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.header-dataset-badge.is-default { background: var(--color-page-bg); color: var(--color-ink-secondary); border-color: var(--color-border); }
.header-dataset-badge.is-custom { background: var(--color-surface-blue); color: var(--color-primary-dark); border-color: #D4E2F3; }

.dge-ref-list { padding-left: 20px; margin: 0 0 14px 0; }
.dge-ref-list li { margin-bottom: 10px; font-size: 13px; line-height: 1.55; color: var(--color-ink-secondary); }
.dge-ref-list li strong { color: var(--color-ink); }

/* ---------- Pipeline summary (right column vertical timeline) ---------- */

.pipeline-summary-list { list-style: none; margin: 4px 0 16px 0; padding: 0; }
.pipeline-summary-step {
  display: flex;
  gap: 12px;
  position: relative;
  padding-bottom: 20px;
}
.pipeline-summary-step:last-child { padding-bottom: 0; }
.pipeline-summary-step::before {
  content: "";
  position: absolute;
  left: 11px; top: 26px; bottom: 0;
  width: 1px;
  background: var(--color-border);
}
.pipeline-summary-step:last-child::before { display: none; }
.pipeline-summary-dot {
  flex-shrink: 0;
  width: 24px; height: 24px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  background: #EEF1F4;
  color: var(--color-ink-secondary);
  border: 2px solid #ffffff;
  box-shadow: 0 0 0 1px var(--color-border);
  z-index: 1;
}
.pipeline-summary-step.state-current .pipeline-summary-dot {
  background: var(--color-primary);
  color: #ffffff;
  box-shadow: 0 0 0 1px var(--color-primary);
}
.pipeline-summary-step.state-done .pipeline-summary-dot {
  background: var(--color-success);
  color: #ffffff;
  box-shadow: 0 0 0 1px var(--color-success);
}
.pipeline-summary-title { font-size: 13.5px; font-weight: 600; color: var(--color-ink); }
.pipeline-summary-step.state-future .pipeline-summary-title { color: var(--color-ink-secondary); font-weight: 500; }
.pipeline-summary-step.state-current .pipeline-summary-title { color: var(--color-primary-dark); }
.pipeline-summary-sub { font-size: 12px; color: var(--color-ink-secondary); margin-top: 1px; }
.pipeline-summary-footer {
  border-top: 1px solid var(--color-border);
  padding-top: 12px;
  margin-top: 4px;
}
.pipeline-summary-footer a {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-primary);
  text-decoration: none;
}
.pipeline-summary-footer a:hover { text-decoration: underline; }

/* ---------- Responsive ---------- */

@media (max-width: 991px) {
  .omics-sidebar { border-bottom: 1px solid var(--color-border); margin-bottom: 16px; }
  .sidebar-nav { display: flex; overflow-x: auto; gap: 4px; }
  .sidebar-nav-item { border-left: none; border-bottom: 3px solid transparent; white-space: nowrap; }
  .sidebar-nav-item.active { border-left: none; border-bottom-color: var(--color-primary); }
}
@media (max-width: 768px) {
  .app-header { flex-wrap: wrap; }
  .app-header-search { order: 3; max-width: 100%; margin: 8px 0 0 0; }
  .module-grid, .figure-gallery { grid-template-columns: 1fr; }
}

/* ---------- ArthOChat slide-out drawer ----------
   Mounted once, outside navbarPage (see ui.R) - toggled purely via a CSS
   class (.open) added/removed by plain onclick JS (ARTHOCHAT_DRAWER_OPEN_JS
   in global.R) or shinyjs::runjs() server-side, so opening it never
   navigates away from whatever module/tab is currently showing. */

.chat-drawer {
  position: fixed;
  top: 0;
  right: 0;
  width: 420px;
  max-width: 92vw;
  height: 100vh;
  background: #ffffff;
  box-shadow: -8px 0 28px rgba(15, 23, 42, 0.16);
  z-index: 1050;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.25s ease;
}
.chat-drawer.open { transform: translateX(0); }

.chat-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
}
.chat-drawer-header svg, .chat-drawer-header .fa { color: var(--color-primary); margin-right: 6px; }
.chat-drawer-close {
  background: transparent;
  border: none;
  color: var(--color-ink-secondary);
  font-size: 15px;
  cursor: pointer;
  padding: 5px 9px;
  border-radius: 6px;
}
.chat-drawer-close:hover { background: var(--color-page-bg); color: var(--color-ink); }

.chat-drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.chat-drawer-body .submodule-desc { flex-shrink: 0; }
.chat-drawer-body > .html-fill-container,
.chat-drawer-body > div:last-child { flex: 1 1 auto; min-height: 0; }

.chat-drawer-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.35);
  z-index: 1040;
}
#arthochat_drawer.open ~ #arthochat_drawer_backdrop { display: block; }

@media (max-width: 600px) {
  .chat-drawer { width: 100vw; max-width: 100vw; }
}

/* ---------- Home page ---------- */

@keyframes home-fade-up {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Full-page scroll story background (see HOME_STORY_CANVAS_JS in ui.R): one
   canvas, pinned to the viewport with position:sticky while its wrapper
   scrolls past underneath, cross-fading between generic visualization
   styles as the user moves through the page. Cards/boxes throughout the
   page keep their own opaque backgrounds (unchanged) and sit above it at
   z-index 1, so this only ever shows through the gaps and under plain page
   text - never competing with actual content. Sticky (not fixed) so it can
   never bleed into any other tab: it stops taking part in layout, and
   therefore stops being visible, the moment its own wrapper scrolls out of
   the document, which only happens on the Home tab. */
.home-page-root { position: relative; }
.home-story-canvas {
  position: sticky;
  top: 0;
  height: 100vh;
  width: 100%;
  margin-bottom: -100vh;
  z-index: 0;
  pointer-events: none;
  filter: blur(3px);
  opacity: 0.4;
}
.home-story-content { position: relative; z-index: 1; }
@media (max-width: 767px) {
  .home-story-canvas { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .home-story-canvas { opacity: 0.26; filter: blur(4px); }
}

/* Light SaaS hero: a near-white surface with a whisper of blue/teal tint,
   not a saturated color block - color comes from the accent chips, icons
   and buttons, not from filling the whole hero with it. */
.home-hero {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, #FBFDFF 0%, #F1F6FE 55%, #EFFAF8 100%);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  padding: 48px 40px;
  margin: 6px 0 26px 0;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.home-hero-pattern {
  position: absolute;
  top: 0; right: 0;
  width: 45%; height: 100%;
  pointer-events: none;
  opacity: 0.6;
  background-image: radial-gradient(circle, rgba(37,99,235,0.14) 1.4px, transparent 1.4px);
  background-size: 22px 22px;
  -webkit-mask-image: linear-gradient(to left, black, transparent);
  mask-image: linear-gradient(to left, black, transparent);
}
.home-hero-grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: 32px;
  align-items: center;
}
@media (max-width: 991px) {
  .home-hero-grid { grid-template-columns: 1fr; }
}
.home-hero-content {
  max-width: 640px;
  animation: home-fade-up 0.5s ease both;
}

/* Soft blurred color blobs behind everything - abstract, subtle chrome
   rather than a fake product photo. Barely-there on the light hero. */
.home-hero-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  pointer-events: none;
  z-index: 0;
}
.home-hero-orb-1 {
  width: 320px; height: 320px; right: -70px; top: -90px;
  background: radial-gradient(circle, rgba(37,99,235,0.14), transparent 70%);
}
.home-hero-orb-2 {
  width: 260px; height: 260px; right: 90px; bottom: -110px;
  background: radial-gradient(circle, rgba(20,184,166,0.12), transparent 70%);
}

/* Conceptual, dataset-independent gene-filtering funnel (see
   HOME_HERO_CANVAS_JS in ui.R): a wide field of points narrowing to a
   handful of glowing "candidates" - not real data, sitting behind both the
   hero text AND the foreground .home-hero-flow steps. A left-to-right mask
   keeps it out from directly behind the headline/CTAs; only a hairline
   blur is applied so the funnel/network detail actually reads instead of
   dissolving into a haze. */
.home-hero-canvas {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 1;
  pointer-events: none;
  opacity: 0.92;
  filter: blur(0.4px);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 20%, black 46%, black 100%);
  mask-image: linear-gradient(to right, transparent 0%, transparent 20%, black 46%, black 100%);
}
@media (max-width: 767px) {
  .home-hero-canvas { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .home-hero-canvas { opacity: 0.55; }
}

.home-hero-brand-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.home-hero-logo {
  display: block;
  height: 34px;
  width: auto;
}

.home-hero-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--color-primary-dark); background: var(--color-surface-blue);
  border: 1px solid #D4E2F3;
  font-size: 12px; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase;
  padding: 5px 12px; border-radius: 999px; margin-bottom: 0;
}
.home-hero-live-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #22C55E; display: inline-block;
  animation: home-pulse 2s infinite;
}
@keyframes home-pulse {
  0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.45); }
  70% { box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); }
  100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}
.home-hero-title {
  color: var(--color-ink); font-size: 36px; font-weight: 800; letter-spacing: -0.02em;
  line-height: 1.18; margin: 0 0 14px 0;
}
.home-hero-title-word {
  display: inline-block;
  opacity: 0;
  animation: home-word-pop 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes home-word-pop {
  from { opacity: 0; transform: translateY(10px) scale(0.94); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .home-hero-title-word { opacity: 1; animation: none; }
}
.home-hero-subtitle {
  color: var(--color-ink-secondary); font-size: 14.5px; line-height: 1.6;
  max-width: 620px; margin: 0 0 24px 0;
}
.home-hero-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 22px; }
/* actionButton() always adds "btn-default" alongside whatever class= is
   passed (Shiny's own base class), and AdminLTE's own gloss gradient on
   .btn-primary otherwise wins the cascade - !important + an explicit
   background-image reset make this button render as actually designed
   regardless of both. The light hero needs no color inversion here: the
   app's normal .btn-primary (solid blue, white text) already reads fine
   on this background. */
.home-hero-actions .btn-primary {
  background-color: var(--color-primary) !important; background-image: none !important;
  border-color: var(--color-primary) !important; color: #ffffff !important;
}
.home-hero-actions .btn-primary:hover, .home-hero-actions .btn-primary:focus {
  background-color: var(--color-primary-dark) !important; border-color: var(--color-primary-dark) !important;
}
/* :not(.btn-primary) - excludes the "Open Transcriptomics" button above,
   which carries both "btn-default" and "btn-primary" at once. */
.home-hero-actions .btn-default:not(.btn-primary) {
  background: #ffffff !important; background-image: none !important;
  border-color: var(--color-border) !important; color: var(--color-ink) !important;
}
.home-hero-actions .btn-default:not(.btn-primary):hover {
  background: var(--color-surface-blue) !important; border-color: #C7D7EE !important; color: var(--color-ink) !important;
}
.home-hero-meta-row {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: 20px; padding-top: 16px;
  border-top: 1px solid var(--color-border);
}
.home-hero-meta-chip {
  display: flex; align-items: flex-start; gap: 10px;
  flex: 1 1 240px; max-width: 320px;
  background: #ffffff; border: 1px solid var(--color-border);
  border-radius: 10px; padding: 10px 14px;
}
.home-hero-meta-chip svg, .home-hero-meta-chip .fa {
  flex-shrink: 0; margin-top: 3px; color: var(--color-primary); font-size: 13px;
}
.home-hero-meta-label {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--color-ink-secondary); margin-bottom: 2px;
}
.home-hero-meta-value { font-size: 12.5px; color: var(--color-ink); font-weight: 500; line-height: 1.4; }
@media (max-width: 576px) {
  .home-hero-meta-chip { max-width: 100%; flex: 1 1 100%; }
}

/* ---------- Hero foreground flow: what happens when you click an omics
   module, on top of the blurred funnel canvas. Opaque cards + a
   connecting line, deliberately sharp so it stays legible over the busy
   backdrop. ---------- */
.home-hero-flow {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 10px;
  animation: home-fade-up 0.6s ease both;
  animation-delay: 0.1s;
}
.home-hero-flow-step {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 14px 16px;
  box-shadow: 0 8px 20px -14px rgba(15, 23, 42, 0.22);
  backdrop-filter: blur(6px);
}
.home-hero-flow-step:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 28px; bottom: -11px;
  width: 2px; height: 11px;
  background: var(--color-border);
}
.home-hero-flow-num {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--color-primary); color: #ffffff;
  font-size: 11px; font-weight: 700;
}
.home-hero-flow-body { min-width: 0; }
.home-hero-flow-title {
  display: flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 700; color: var(--color-ink);
}
.home-hero-flow-title svg, .home-hero-flow-title .fa { color: var(--color-primary); font-size: 13px; }
.home-hero-flow-blurb {
  font-size: 12px; color: var(--color-ink-secondary); line-height: 1.5;
  margin: 4px 0 0 0;
}
@media (max-width: 991px) {
  .home-hero-flow { margin-top: 8px; }
}

/* Functional hero search - sets the same `header_search_submit` input
   R/ui_shell.R's own header search box sets, so Enter here reuses
   server.R's existing observeEvent(input$header_search_submit, ...)
   instead of adding a second code path. */
.home-hero-search-label {
  color: var(--color-ink-secondary); font-size: 11.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  margin: 24px 0 8px 0;
}
.home-hero-search { position: relative; max-width: 460px; margin-bottom: 12px; }
.home-hero-search input {
  width: 100%; padding: 11px 68px 11px 40px;
  border-radius: 10px; border: 1px solid var(--color-border);
  background: #ffffff; color: var(--color-ink); font-size: 13.5px;
}
.home-hero-search input::placeholder { color: #94A3B8; }
.home-hero-search input:focus {
  outline: none; border-color: var(--color-primary); background: #ffffff;
  box-shadow: 0 0 0 3px rgba(37,99,235,0.12);
}
.home-hero-search-icon {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--color-ink-secondary); font-size: 13px;
}
.home-hero-search-kbd {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-size: 10px; color: var(--color-ink-secondary);
  border: 1px solid var(--color-border); border-radius: 5px; padding: 2px 6px;
}
.home-hero-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; }
.home-hero-pill {
  font-size: 11.5px; font-weight: 600; color: var(--color-primary-dark);
  background: var(--color-surface-blue); border: 1px solid #D4E2F3;
  padding: 5px 12px; border-radius: 999px; text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease;
}
.home-hero-pill:hover, .home-hero-pill:focus {
  background: #E0EBFC; color: var(--color-primary-dark); text-decoration: none;
}

/* ---------- Goal band: the platform's value proposition, in one place ---------- */
.home-goal-band {
  position: relative;
  z-index: 1;
  display: flex; align-items: flex-start; gap: 18px;
  background: #ffffff; border: 1px solid var(--color-border); border-radius: var(--radius-card);
  padding: 22px 26px;
  margin-bottom: 30px;
}
.home-goal-icon {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex-shrink: 0;
  background: var(--color-surface-blue); color: var(--color-primary);
  border-radius: 12px; font-size: 18px;
}
.home-goal-eyebrow {
  font-size: 11px; font-weight: 700; color: var(--color-primary);
  text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 4px;
}
.home-goal-title { font-size: 18px; font-weight: 800; color: var(--color-ink); margin: 0 0 8px 0; letter-spacing: -0.01em; }
.home-goal-text { color: var(--color-ink-secondary); font-size: 13.5px; line-height: 1.6; max-width: 760px; margin: 0 0 12px 0; }
.home-goal-chat-link {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600; color: var(--color-primary-dark); text-decoration: none;
}
.home-goal-chat-link:hover { text-decoration: underline; }
@media (max-width: 576px) {
  .home-goal-band { flex-direction: column; padding: 18px 20px; }
}

.home-stats-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin-bottom: 30px;
}
.home-stat-card {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  background: #ffffff; border: 1px solid var(--color-border); border-radius: var(--radius-card);
  padding: 16px 18px;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.home-stat-card:hover,
.home-stat-card:focus-within {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px -12px rgba(15, 23, 42, 0.18);
  border-color: #C7D7EE;
  z-index: 3;
}
.home-stat-card:focus-within { outline: none; }
.home-stat-card:focus-visible {
  outline: 2px solid var(--color-primary); outline-offset: 2px;
}
.home-stat-icon {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex-shrink: 0;
  background: var(--color-surface-blue); color: var(--color-primary);
  border-radius: 10px; font-size: 16px;
}
.home-stat-value { font-size: 20px; font-weight: 800; color: var(--color-ink); letter-spacing: -0.01em; }
.home-stat-label { font-size: 12px; color: var(--color-ink-secondary); font-weight: 500; }

/* Hover/focus-revealed detail popover - CSS-only, no JS. Absolutely
   positioned so revealing it never shifts the grid or neighboring cards. */
.home-stat-detail {
  position: absolute;
  left: 0; right: 0; top: calc(100% + 8px);
  background: #ffffff; border: 1px solid var(--color-border); border-radius: 10px;
  padding: 10px 12px;
  font-size: 11.5px; line-height: 1.45; color: var(--color-ink-secondary); font-weight: 400;
  box-shadow: 0 14px 28px -14px rgba(15, 23, 42, 0.3);
  opacity: 0; transform: translateY(-4px);
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.home-stat-card:hover .home-stat-detail,
.home-stat-card:focus-within .home-stat-detail {
  opacity: 1; transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .home-stat-detail { transition: none; }
}

/* ---------- Feature grid (reuses .info-card's existing look) ---------- */
/* .home-features-section only adds position:relative - an anchor for the
   absolutely-positioned backdrop canvas below - it introduces no border,
   background or padding of its own, so the section's existing spacing and
   layout are untouched. */
.home-features-section { position: relative; }
.home-features-section .page-header,
.home-features-section .home-features-grid {
  position: relative;
  z-index: 1;
}

/* Cycling scientific-visualization backdrop (see HOME_FEATURES_CANVAS_JS
   in ui.R) - single-hue blue, never a new color; heavy blur + low opacity
   keep it strictly atmospheric behind the four opaque feature cards. */
.home-features-canvas {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.22;
  filter: blur(6px);
}
@media (max-width: 767px) {
  .home-features-canvas { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .home-features-canvas { opacity: 0.14; }
}

.home-features-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
  margin-bottom: 8px;
}
.home-feature-card {
  min-height: 0;
  margin-bottom: 0;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.home-feature-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px -12px rgba(15, 23, 42, 0.18);
  border-color: #C7D7EE;
}
.home-feature-kicker {
  display: inline-block;
  font-size: 11px; font-weight: 700; color: var(--color-primary);
  text-transform: uppercase; letter-spacing: 0.03em;
  margin: -4px 0 8px 0;
}

/* Sex-design band: a tinted callout (not just another plain section) so the three designs read as
   a deliberate, central platform feature rather than a footnote - per reviewer feedback that this
   was previously buried in a small hero tagline chip. */
.home-sexdesign-band {
  background: var(--color-surface-blue);
  border: 1px solid #D4E2F3;
  border-radius: var(--radius-card);
  padding: 26px 28px 22px 28px;
  margin-bottom: 30px;
}
.home-sexdesign-band .home-feature-card { background: #ffffff; }
@media (max-width: 576px) {
  .home-sexdesign-band { padding: 18px 20px; }
}

/* ---------- Omics modules grid (Home page only) ----------
   Transcriptomics is first in MODULE_REGISTRY's Single-/Multi-omics
   filter, so it's also the first card here - give it a lightly tinted
   background and a stronger border/shadow so it reads as the module that's
   actually usable today, without touching .module-card itself (shared with
   the plain Modules tab grid, left exactly as-is). */
.home-modules-grid .module-card:first-child {
  border-color: #BFD6F7;
  border-left-width: 4px;
  background: linear-gradient(180deg, var(--color-surface-blue) 0%, #ffffff 60%);
  box-shadow: 0 14px 30px -20px rgba(37, 99, 235, 0.45);
}

/* ---------- FAQ accordion (native <details>/<summary>, no JS needed) ---------- */

.home-faq-list {
  max-width: 780px;
  margin: 0 0 8px 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.home-faq-item {
  background: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  padding: 0 18px;
}
.home-faq-item summary {
  list-style: none;
  cursor: pointer;
  padding: 15px 0;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  font-weight: 600; font-size: 14px; color: var(--color-ink);
}
.home-faq-item summary::-webkit-details-marker { display: none; }
.home-faq-icon {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--color-surface-blue); color: var(--color-primary);
  font-size: 11px;
  transition: transform 0.2s ease;
}
.home-faq-item[open] .home-faq-icon { transform: rotate(45deg); }
.home-faq-answer { padding: 0 0 16px 0; color: var(--color-ink-secondary); font-size: 13px; line-height: 1.6; }
.home-faq-answer p { margin: 0; }

/* ---------- Closing CTA banner ---------- */

/* Deep navy-to-blue, not the hero's bright primary-to-primary-dark - a
   moodier closing band reads as a deliberate, restrained accent rather
   than a second identical wall of bright color. */
.home-cta-banner {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, var(--color-ink) 0%, var(--color-primary-dark) 100%);
  border-radius: var(--radius-card);
  padding: 36px 40px;
  margin: 30px 0 22px 0;
  text-align: center;
}
.home-cta-pattern {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.4;
  background-image: radial-gradient(circle, rgba(255,255,255,0.3) 1.4px, transparent 1.4px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(ellipse at center, black, transparent 75%);
  mask-image: radial-gradient(ellipse at center, black, transparent 75%);
}
.home-cta-content { position: relative; max-width: 560px; margin: 0 auto; }
.home-cta-content h3 { color: #ffffff; font-size: 22px; font-weight: 800; margin: 0 0 8px 0; }
.home-cta-content p { color: rgba(255,255,255,0.85); font-size: 13.5px; margin: 0 0 20px 0; }
.home-cta-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-bottom: 4px; }
.home-cta-content .btn-primary {
  background-color: #ffffff !important; background-image: none !important;
  border-color: #ffffff !important; color: var(--color-primary-dark) !important;
}
.home-cta-content .btn-primary:hover, .home-cta-content .btn-primary:focus {
  background-color: #ffffff !important; border-color: #ffffff !important; opacity: 0.92;
}
/* :not(.btn-primary) - actionButton() always carries "btn-default" as its
   own base class regardless of class=; excluding .btn-primary keeps this
   from re-winning the tie against the rule above on source order (same
   pattern as .home-hero-actions). */
.home-cta-content .btn-default:not(.btn-primary) {
  background: rgba(255,255,255,0.12) !important; background-image: none !important;
  border-color: rgba(255,255,255,0.42) !important; color: #ffffff !important;
}
.home-cta-content .btn-default:not(.btn-primary):hover {
  background: rgba(255,255,255,0.22) !important; color: #ffffff !important;
}
.home-cta-subtext {
  color: rgba(255,255,255,0.65) !important; font-size: 11.5px !important;
  margin: 14px 0 0 0 !important;
}

/* ---------- Footer ---------- */

.home-footer {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  padding: 20px 4px 30px 4px;
  border-top: 1px solid var(--color-border);
  text-align: center;
}
.home-footer-credit { color: var(--color-ink-secondary); font-size: 12.5px; }
.home-footer-version {
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  color: var(--color-ink-secondary); opacity: 0.75;
}

@media (max-width: 768px) {
  .home-hero { padding: 32px 22px; }
  .home-hero-content { max-width: 100%; }
  .home-hero-title { font-size: 27px; }
  .home-hero-search { max-width: 100%; }
  .home-cta-banner { padding: 28px 22px; }
}

/* ---------------------------------------------------------------------------
   Data Exploration tab (mod_preprocessing.R) - scoped to this one tab only,
   via the .explore-* classes below plus the .pp-outer-row:has(...) rule
   right after them. Nothing here changes .card/.box/valueBox's shared,
   app-wide look - every rule is qualified by one of these classes (or the
   :has() condition below) so every other module, and every other tab of
   this same module (Preprocessing/Merge datasets/Batch correction, which
   keep their existing 8/4 split next to the ArthOChat rail), renders
   exactly as before.
   --------------------------------------------------------------------------- */

/* Data Exploration's own results (before/after plots, stats table, quality
   check, reproducibility record) are heavier than the other 3 tabs' and
   were reading as squeezed into the 8-of-12-column .pp-tabs-col next to the
   4-of-12-column ArthOChat rail (.pp-rail-col) - the same box+column split
   every other tab here keeps. Rather than shrinking that split app-wide,
   .pp-tabs-col claims the full row's width and .pp-rail-col hides ONLY
   while the active inner tab is "Explore" (mod_preprocessing.R's
   explore_tab_ui carries its own arthochat_shortcut_ui() in that case, next
   to its Filters box - the same placement mod_dge.R uses - so ArthOChat
   access isn't lost, just relocated for this one tab). */
.pp-outer-row:has(> .pp-tabs-col .tab-pane.active[data-value="Explore"]) > .pp-tabs-col {
  width: 100% !important;
}
.pp-outer-row:has(> .pp-tabs-col .tab-pane.active[data-value="Explore"]) > .pp-rail-col {
  display: none !important;
}

/* Numeric stat tiles at the top of the tab: valueBox()'s default AdminLTE
   number size is sized for the rest of the app's short metrics, but reads
   as oversized/dominant once several sit in a row on a wider canvas here -
   restrained down to the requested ~28-36px "prominent but restrained"
   range, and set to wrap instead of clip so a longer value (e.g. "Linear/
   log expression") never overflows its tile. */
.explore-stats-row .small-box h3,
.explore-stats-row .info-box-number {
  font-size: 30px;
  line-height: 1.2;
  overflow-wrap: break-word;
  word-break: break-word;
  white-space: normal;
}
.explore-stats-row .small-box p,
.explore-stats-row .info-box-text {
  font-size: 13px;
  overflow-wrap: break-word;
  white-space: normal;
}

/* ---------------------------------------------------------------------------
   Methylomics DMP/DMR tabs (mod_methyl_dmp.R, mod_methyl_dmr.R) - scoped to
   the .methyl-stats-row wrapper those two files put around their own
   valueBox() summary rows only. Same problem and same fix as
   .explore-stats-row above: on the real preloaded/uploaded matrix these
   counts run into six figures (e.g. "169,248"), which overflowed
   valueBox()'s default AdminLTE number size. Nothing here touches
   .small-box/.info-box's shared, app-wide rules above - every other
   valueBox() in the app (including elsewhere in Methylomics, e.g. QC/
   Normalization) renders exactly as before.
   --------------------------------------------------------------------------- */
.methyl-stats-row .small-box h3,
.methyl-stats-row .info-box-number {
  font-size: 26px;
  line-height: 1.2;
  overflow-wrap: break-word;
  word-break: break-word;
  white-space: normal;
}
.methyl-stats-row .small-box p,
.methyl-stats-row .info-box-text {
  font-size: 12.5px;
  overflow-wrap: break-word;
  white-space: normal;
}

/* Data processing status panel - replaces a long sentence crammed into a
   valueBox with a proper headline + explanation + facts layout. Color
   varies by state (green = already normalized, blue = not normalized yet,
   amber = unknown) without implying "not normalized" is itself a problem -
   it's a decision, not a warning. */
.explore-status-panel { overflow-wrap: break-word; }
.explore-status-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 8px; }
.explore-status-icon { font-size: 22px; margin-top: 3px; flex-shrink: 0; }
.explore-status-eyebrow {
  display: block; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--color-ink-secondary); margin-bottom: 2px;
}
.explore-status-headline { font-size: 20px; font-weight: 800; letter-spacing: -0.01em; color: var(--color-ink); }
.explore-status-detail { color: var(--color-ink-secondary); font-size: 13.5px; line-height: 1.6; max-width: 900px; margin: 0 0 14px 0; }
.explore-status-good .explore-status-icon,
.explore-status-good .explore-status-headline { color: var(--color-success); }
.explore-status-good { border-left: 4px solid var(--color-success); }
.explore-status-info .explore-status-icon,
.explore-status-info .explore-status-headline { color: var(--color-primary); }
.explore-status-info { border-left: 4px solid var(--color-primary); }
.explore-status-unknown .explore-status-icon,
.explore-status-unknown .explore-status-headline { color: var(--color-warning); }
.explore-status-unknown { border-left: 4px solid var(--color-warning); }

.explore-status-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px 20px;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
}
.explore-status-fact { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.explore-status-fact-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-ink-secondary); }
.explore-status-fact-value { font-size: 13.5px; font-weight: 600; color: var(--color-ink); overflow-wrap: break-word; }

/* Distribution + boxplot cards: full-width figures with a compact
   diagnostic panel beside the main plot (see item 22's ~65/35 split). */
.explore-dist-card { overflow-wrap: break-word; }
.explore-diag-card { background: var(--color-page-bg); }
.explore-diag-row {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--color-border);
  font-size: 12.5px;
}
.explore-diag-row:last-of-type { border-bottom: none; }
.explore-diag-label { color: var(--color-ink-secondary); }
.explore-diag-value { font-weight: 600; color: var(--color-ink); text-align: right; }

/* Small classed replacement for the unclassed <h5> "Before/After
   normalisation" sub-labels in the before/after comparison panel. */
.explore-subhead { font-size: 13.5px; font-weight: 700; color: var(--color-ink); margin: 0 0 8px 0; }

/* .card-title's shared display:flex rule (used elsewhere in the app, e.g.
   the Merge datasets tab, without issue) collapses to zero width in these
   two Explore-tab cards specifically - a layout bug that also stretches
   the following <p class="submodule-desc"> to ~5x its real height,
   reading as a large blank/dark gap. Falling back to inline content (no
   flex) for just these two card-titles fixes both; the icon gets its
   spacing from margin instead of flex's gap. Scoped to .explore-dist-card/
   .explore-diag-card only - every other .card-title in the app (Merge
   datasets, etc.) keeps the shared flex rule, unchanged. */
.explore-dist-card > .card-title,
.explore-diag-card > .card-title {
  display: block;
}
.explore-dist-card > .card-title svg,
.explore-dist-card > .card-title .fa,
.explore-diag-card > .card-title svg,
.explore-diag-card > .card-title .fa {
  margin-right: 8px;
}

@media (max-width: 992px) {
  .explore-status-facts { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
}

/* Header "Analysis records" button + run-count badge (session provenance panel) */
.app-header-records-btn {
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 13px;
  border: 1px solid var(--color-border);
  background: #ffffff;
  color: var(--color-ink-secondary);
}
.app-header-records-btn:hover { background: var(--color-page-bg); }
.app-header-badge {
  display: inline-block;
  min-width: 18px;
  padding: 0 6px;
  margin-left: 6px;
  border-radius: 999px;
  background: var(--color-primary, #2563EB);
  color: #ffffff;
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
}

/* ---------- Guided "Start an analysis" workflow (R/workflow_guide.R).
   Reuses the hero flow-step cards (home tracker) and the pipeline-summary
   dots and state colours (guide bar); only layout and state tweaks here. ---------- */
.home-hero-flow.wf-tracker { gap: 6px; }
.home-hero-flow.wf-tracker .home-hero-flow-step { padding: 8px 14px; }
.home-hero-flow.wf-tracker .home-hero-flow-step:not(:last-child)::after { bottom: -7px; height: 7px; }
.wf-tracker .wf-state-future .home-hero-flow-num { background: #EEF1F4; color: var(--color-ink-secondary); }
.wf-tracker .wf-state-future .home-hero-flow-title { color: var(--color-ink-secondary); font-weight: 600; }
.wf-tracker .wf-state-done .home-hero-flow-num { background: var(--color-success); }
.wf-tracker .wf-state-current { border-color: var(--color-primary); box-shadow: 0 0 0 1px var(--color-primary); }
.wf-clickable { cursor: pointer; }

.wf-bar { margin: 12px 16px 4px 16px; }
.wf-bar-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.wf-bar-exit { font-size: 13px; }
.wf-bar-steps { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 6px 0 12px 0; }
.wf-bar-steps .pipeline-summary-step { padding-bottom: 0; align-items: center; gap: 6px; }
.wf-bar-steps .pipeline-summary-step::before { display: none; }
.wf-bar-steps .pipeline-summary-title { font-size: 12.5px; }
.wf-mode-list { margin: 4px 0 8px 0; padding-left: 18px; font-size: 13px; color: var(--color-ink-secondary); }
.wf-bar-links { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0; }
.wf-bar-actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; margin-top: 10px; }
.wf-note-error { color: var(--color-danger, #DC2626); }

/* ---------------------------------------------------------------------------
   Step-driven Transcriptomics shell: the sidebar grouped by the nine workflow
   steps is the only in-page navigation, so it carries the status too (the top
   guide bar no longer repeats it). Built from the shared --color-* tokens, so
   dark mode follows without a second rule set. */

.sidebar-nav-dynamic { margin-top: 2px; }
.sidebar-step-group { margin: 0 0 2px 0; padding: 0; list-style: none; }

.sidebar-step-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px 4px 12px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-ink-secondary);
}
/* Squared-off rather than round pills: reads as a numbered index in a tool, not as a badge. */
.sidebar-step-num {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10.5px;
  font-weight: 700;
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-ink-secondary);
}
.sidebar-step-header.sm-state-done .sidebar-step-num { background: var(--color-success); border-color: var(--color-success); color: #ffffff; }
.sidebar-step-header.sm-state-stale .sidebar-step-num { background: var(--color-warning); border-color: var(--color-warning); color: #ffffff; }
.sidebar-step-header.sm-state-ready .sidebar-step-num { background: transparent; border-color: var(--color-primary); color: var(--color-primary); }
.sidebar-step-name { min-width: 0; overflow-wrap: anywhere; }

.sidebar-step-item { gap: 8px; padding-left: 22px; }
.sidebar-step-label { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.sidebar-step-item.sm-state-locked { opacity: 0.72; }

/* Square status tags rather than round chips, to match the step index. */
.sm-state-pill {
  flex: 0 0 auto;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid var(--color-border);
  color: var(--color-ink-secondary);
  background: transparent;
}
.sm-state-pill.sm-state-done { color: var(--color-success); border-color: var(--color-success); }
.sm-state-pill.sm-state-stale { color: var(--color-warning); border-color: var(--color-warning); }
.sm-state-pill.sm-state-ready { color: var(--color-primary); border-color: var(--color-primary); }

.sidebar-step-empty {
  padding: 2px 12px 6px 22px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--color-ink-secondary);
}

.sidebar-nav-foot { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--color-border); }
.sidebar-add-item { font-weight: 600; color: var(--color-primary); }
.sidebar-add-item svg, .sidebar-add-item .fa { color: var(--color-primary); }

/* Breadcrumb in place of the bare page title. */
.page-header-crumbed h2 { display: none; }
.page-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 13px;
  color: var(--color-ink-secondary);
}
.page-breadcrumb .crumb-sep { color: var(--color-ink-secondary); opacity: 0.6; }
.page-breadcrumb .crumb-link { color: var(--color-primary); text-decoration: none; cursor: pointer; }
.page-breadcrumb .crumb-link:hover { text-decoration: underline; }
.page-breadcrumb .crumb-leaf { color: var(--color-ink); font-weight: 600; }

/* Contrast and sex design are set once in the guide; every page states them read-only. */
.wf-settings-banner {
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-blue);
  border: 1px solid var(--color-border);
  color: var(--color-ink-secondary);
  font-size: 12.5px;
}
.wf-settings-banner svg, .wf-settings-banner .fa { color: var(--color-ink-secondary); font-size: 11px; }

/* Step 4 preset picker and the resolved run order. */
.wf-preset-blurb { display: block; font-size: 12px; color: var(--color-ink-secondary); line-height: 1.4; }
/* The guide bar sits above the navbar, so an unbounded plan list (the "Full" preset lists all 16
   analyses) pushed the navbar and the page itself below the fold. The bar is full width with a lot
   of empty space on the right, so these lists flow into columns across it instead of running down
   the left edge, with a height cap as a safety net on narrow windows. */
.wf-plan-list {
  margin: 2px 0 0 0;
  padding-left: 0;
  font-size: 12.5px;
}
.wf-plan-list { list-style: none; padding-left: 0; }
.wf-plan-list li { display: flex; align-items: baseline; gap: 7px; margin-bottom: 0; }
.wf-plan-num {
  flex: 0 0 auto;
  min-width: 17px;
  height: 17px;
  border-radius: 3px;
  border: 1px solid var(--color-border);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--color-ink-secondary);
}
.wf-plan-title { min-width: 0; overflow-wrap: anywhere; }
.wf-bar .wf-mode-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 2px 28px;
  max-height: 132px;
  overflow-y: auto;
}

/* Step 4's four presets stack into a tall column by default; across the bar's width they fit in
   one row and save most of that height. Shiny's own rule caps .shiny-input-container at 300px,
   which is what forced the single column - these groups opt out of that width. */
#wf_preset.shiny-input-container,
#wf_analyses.shiny-input-container { width: 100%; max-width: 100%; }
#wf_preset .shiny-options-group {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 2px 24px;
  margin-top: 2px;
}
#wf_preset .radio { margin: 0; }
#wf_preset .radio label { padding-right: 6px; }
#wf_analyses .shiny-options-group {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 0 24px;
}
.wf-plan-step {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-ink-secondary);
}

/* The horizontal tx_menu strip is type = "hidden"; the wrapper keeps its spacing. */
.tx-menu-hidden > .tabbable > .nav { display: none !important; }
.tx-menu-hidden .tab-content { padding-top: 4px; }

@media (max-width: 1100px) {
  .sidebar-step-item { padding-left: 12px; }
  .sm-state-pill { display: none; }
  .sidebar-step-group { display: block; }
}

/* The nine-step strip folded into the guide bar: the sidebar is still the always-on tracker, and
   this opens the whole route on demand without taking permanent vertical space. */
.wf-steps-toggle { margin: 4px 0 8px 0; }
.wf-steps-toggle > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--color-primary);
  padding: 3px 8px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface-blue);
}
.wf-steps-toggle > summary::-webkit-details-marker { display: none; }
.wf-steps-toggle > summary::after { content: "\25be"; font-size: 11px; }
.wf-steps-toggle[open] > summary::after { content: "\25b4"; }
.wf-steps-toggle > summary:hover { border-color: var(--color-primary); }
.wf-steps-toggle > summary svg, .wf-steps-toggle > summary .fa { color: var(--color-primary); font-size: 11px; }
.wf-steps-toggle .wf-bar-steps { margin-top: 8px; }

/* Custom Analysis Design: the entry point above the step tracker, and its own page. */
.sidebar-nav-design { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--color-border); }
.sidebar-design-item {
  font-weight: 600;
  color: var(--color-primary);
  background: var(--color-surface-blue);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
}
.sidebar-design-item:hover { background: var(--color-surface-blue); border-color: var(--color-primary); }
.sidebar-design-item svg, .sidebar-design-item .fa { color: var(--color-primary); }
.wf-design-intro { margin-bottom: 12px; }
/* Under the breadcrumb rather than above the navbar: no side gutters needed here. */
.wf-bar { margin: 10px 0 6px 0; }

/* Section tabs on the same row as the app header, rather than a second bar under it.
   Structure is untouched - the navbar is pulled up by exactly its own height (47px) so the two
   together occupy the header's 55px, and inset past the brand on the left and the header buttons
   on the right. Only above 1100px, where brand + tabs + buttons (~1050px) actually fit; below that
   they stay stacked as before. */
@media (min-width: 1100px) {
  /* The navbar is pulled up over this row. z-index is per subtree, so the header has to sit ABOVE
     the navbar for its logo and buttons to show at all - and then its own background would cover
     the tabs, so the row's background moves to the navbar and the header only paints its content.
     pointer-events lets clicks through the header's empty middle to the tabs underneath. */
  .app-header {
    position: relative;
    z-index: 3;
    background: transparent !important;
    border-bottom: none !important;
    pointer-events: none;
  }
  .app-header-brand, .app-header-actions { pointer-events: auto; }
  .navbar {
    position: relative;
    z-index: 2;
    margin-top: -47px;
    margin-bottom: 0;
    min-height: 0;
    /* The navbar now carries the row's background and bottom rule, since the header gave them up. */
    background: var(--color-surface, #ffffff) !important;
    border: none !important;
    border-bottom: 1px solid var(--color-border) !important;
    box-shadow: none !important;
    pointer-events: none;
  }
  .navbar .nav.navbar-nav { pointer-events: auto; }
  .navbar > .container-fluid,
  .navbar > .container {
    padding-left: 160px;
    padding-right: 204px;
  }
}
/* The step explanation on module pages: folded until clicked, so the guide stays a thin controller. */
.wf-body-toggle { margin: 6px 0 4px 0; }
.wf-body-toggle > summary { font-weight: 500; }
/* The design editor folds behind its own title, so the page stays a one-line summary when closed. */
.wf-design-toggle > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 6px; }
.wf-design-toggle > summary::-webkit-details-marker { display: none; }
.wf-design-toggle > summary::after { content: "\25be"; font-size: 11px; color: var(--color-ink-secondary); }
.wf-design-toggle[open] > summary::after { content: "\25b4"; }
.wf-design-summary { font-weight: 400; font-size: 12.5px; color: var(--color-ink-secondary); }

/* Start-an-analysis layer picker: four named omics buttons rather than a vague radio pair. */
.wf-pick-label { font-weight: 600; margin: 4px 0 6px 0; }
.wf-pick-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
.wf-pick-btn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 12px 14px; text-align: left;
  background: var(--color-surface-blue);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-sm);
  color: var(--color-ink);
}
.wf-pick-btn:hover { border-color: var(--color-primary); }
.wf-pick-btn svg, .wf-pick-btn .fa { color: var(--color-primary); font-size: 14px; }
.wf-pick-name { font-weight: 600; font-size: 13.5px; }
.wf-pick-sub { font-size: 11.5px; color: var(--color-ink-secondary); }
.wf-pick-btn-plain { border-left-color: var(--color-border); background: transparent; }
.wf-pick-btn-plain svg, .wf-pick-btn-plain .fa { color: var(--color-ink-secondary); }

/* An error that carries its own fix: the hover says what to do, no extra panel or button. */
.wf-note-error {
  border-left: 3px solid var(--color-danger, #DC2626);
  background: rgba(220, 38, 38, 0.06);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  margin: 4px 0;
}
.wf-note-hinted { cursor: help; }
.wf-note-hinted::after { content: " \24d8"; color: var(--color-ink-secondary); font-size: 11px; }

/* Step strip in the page header, beside the breadcrumb: where you are, always on screen. */
.wf-header-steps { margin-top: 6px; }
.wf-header-steps .wf-bar-steps { margin: 0; gap: 4px 14px; }
.wf-header-steps .pipeline-summary-title { font-size: 11.5px; }
.wf-header-steps .pipeline-summary-dot { width: 17px; height: 17px; font-size: 10px; border-radius: 3px; }
@media (max-width: 1200px) {
  .wf-header-steps .wf-bar-steps { overflow-x: auto; flex-wrap: nowrap; }
}

/* ArthOMix mark in the header brand slot, on the same row as and just left of the Home tab. */
.app-header-logo { display: block; height: 38px; width: auto; }
.wf-bar-head-actions { display: flex; gap: 12px; align-items: center; }
.wf-design-inputs { margin-bottom: 10px; }
.wf-design-inputs .wf-bar-links { margin-top: 6px; }

/* Design page: the choices on the left, the resulting pipeline down the right edge. */
.wf-design-layout { display: flex; gap: 22px; align-items: flex-start; }
.wf-design-main { flex: 1 1 auto; min-width: 0; }
.wf-design-aside {
  flex: 0 0 272px;
  border-left: 1px solid var(--color-border);
  padding-left: 18px;
  align-self: stretch;
}
.wf-aside-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-ink-secondary);
  margin-bottom: 6px;
}
.wf-design-aside .wf-plan-list li { margin-bottom: 3px; }
@media (max-width: 1100px) {
  .wf-design-layout { display: block; }
  .wf-design-aside { border-left: none; padding-left: 0; margin-top: 14px; }
}
