:root {
  --biolitica-primary: #abc837;
  --biolitica-primary-rgb: 171, 200, 55;
  --biolitica-primary-strong: #8fa92f;
  --biolitica-ink: #1a1a1a;
  --biolitica-panel: #242424;
  --biolitica-surface: #f8fafc;

  /* BIO-1036 · la ÚNICA entrada nueva al sistema que la ratificación del 2026-08-20 abrió, y la
   * razón es que no añade superficie: la RETIRA. Estas tintas ya estaban cableadas a mano en las
   * reglas de insignia de estado de más abajo —siete declaraciones—, precisamente por no existir
   * acá. Tokenizarlas no crea trabajo nuevo: paga deuda.
   *
   * «Tinta oscura» es lo que son: el tono que va sobre relleno claro con contraste suficiente para
   * texto, no el tono de marca. Los valores son EXACTAMENTE los que estaban cableados, sin ajustar
   * ninguno: el eslabón mueve de sitio la decisión, no la re-decide.
   *
   * La ratificación las nombra como «los cuatro tonos que la etapa usa». Enumerados, los siete
   * literales son SEIS valores distintos: los cuatro semánticos más dos aceros neutros que la lista
   * no menciona (`empty`/`queued` y los desenlaces informativos de dominio). Se tokenizan los seis
   * porque colapsarlos a cuatro cambiaría dos colores vivos, y eso sería re-decidir. Queda dicho en
   * el cuerpo del PR. */
  --biolitica-status-ink-success: #216e39;
  --biolitica-status-ink-info: #0b5cad;
  --biolitica-status-ink-warning: #8a5a00;
  --biolitica-status-ink-danger: #b42318;
  --biolitica-status-ink-neutral: #344054;
  --biolitica-status-ink-informational: #475467;
}

.product-ui-shell {
  --tblr-primary: var(--biolitica-primary);
  --tblr-primary-rgb: var(--biolitica-primary-rgb);
  --tblr-link-color: #315f1f;
  --tblr-body-bg: var(--biolitica-surface);
  background: var(--biolitica-surface);
}

.product-ui-shell .page-title {
  letter-spacing: -0.01em;
}

.product-ui-nav {
  border-right: 1px solid rgba(26, 26, 26, 0.08);
}

.product-ui-brand {
  padding: 1rem 0.75rem;
}

.product-ui-brand-link,
.product-ui-mobile-brand {
  display: inline-flex;
  align-items: center;
  border-radius: 0.75rem;
  background: #ffffff;
}

.product-ui-brand-link {
  padding: 0.35rem 0.5rem;
}

.product-ui-brand-logo,
.product-ui-mobile-brand-logo {
  display: block;
  height: auto;
  max-width: 100%;
}

.product-ui-header {
  border-bottom: 3px solid var(--biolitica-primary);
  box-shadow: 0 0.25rem 1rem rgba(26, 26, 26, 0.06);
}

/* Login split panel (BIO-863): the decorative aside of the public sign-in surface. It lives here,
   not in an inline `style=`, because ADR-034 R6 forbids color literals in `style=` attributes and
   no Tabler/Bootstrap utility expresses a three-stop gradient. Same colors as before, now read
   from the brand tokens above. */
.product-ui-login-aside {
  background: linear-gradient(
    145deg,
    var(--biolitica-panel) 0%,
    var(--biolitica-ink) 55%,
    var(--biolitica-primary) 180%
  );
}

.product-ui-login-kicker {
  color: var(--biolitica-primary);
  letter-spacing: 0.08em;
}

/* Context selector (BIO-644): top-left "where am I?" switch at the head of the sidebar. */
.product-ui-context {
  width: 100%;
  padding: 0.75rem 0.25rem;
}

.product-ui-context-toggle {
  width: 100%;
}

.product-ui-context-brand {
  display: inline-flex;
  align-items: center;
  border-radius: 0.75rem;
  background: #ffffff;
  padding: 0.3rem 0.45rem;
}

.product-ui-context-logo {
  display: block;
  height: auto;
  max-width: 100%;
}

.product-ui-context-kicker {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.1;
}

.product-ui-context-current {
  line-height: 1.15;
}

.product-ui-context-menu {
  min-width: 15rem;
}

.product-ui-nav-subheader {
  letter-spacing: 0.04em;
}

.product-ui-mobile-brand {
  padding: 0.25rem 0.35rem;
}

.product-ui-avatar {
  background: var(--biolitica-primary);
  color: var(--biolitica-ink);
  font-weight: 700;
}

.product-ui-card {
  min-height: 100%;
  border-color: rgba(26, 26, 26, 0.08);
  box-shadow: 0 0.5rem 1.25rem rgba(26, 26, 26, 0.05);
}

.product-ui-summary-link {
  display: inline-flex;
  align-items: baseline;
  gap: 0.25rem;
  color: inherit;
  text-decoration: none;
}

.product-ui-summary-link:hover,
.product-ui-summary-link:focus {
  color: var(--tblr-link-color);
  text-decoration: underline;
  text-underline-offset: 0.2rem;
}

.product-ui-metadata-header {
  flex-wrap: wrap;
}

.product-ui-metadata-content {
  min-width: 0;
  flex: 1 1 14rem;
}

.product-ui-metadata-action {
  flex: 0 0 auto;
}

.product-ui-wrap-anywhere {
  overflow-wrap: anywhere;
}

/* Issue 406 (inventario del recorrido del operador): el texto del badge es BLANCO para todo
   estado, así que un estado sin regla de tono propia quedaba blanco sobre el fondo de la página —
   medido en el Tablero de producción, «Sin ejecución» a contraste 1.05. Al badge llegan diecisiete
   fuentes de estado y el guard de BIO-778 sólo enumera tres enums, así que el remedio va a la
   CLASE: el tono neutro es el fondo por defecto, y una regla específica de abajo lo sobreescribe
   (gana por especificidad, `.product-ui-shell .product-ui-status-<token>`). */
.product-ui-status {
  color: #ffffff;
  background-color: var(--biolitica-status-ink-neutral);
  font-weight: 700;
  text-transform: none;
}

.product-ui-shell .product-ui-status-complete,
.product-ui-shell .product-ui-status-completed,
.product-ui-shell .product-ui-status-succeeded,
.product-ui-shell .product-ui-status-success,
.product-ui-shell .product-ui-status-ready {
  background-color: var(--biolitica-status-ink-success);
}

/* BIO-778: in-flight states — WorkerJob `claimed`/`assigned` and NicheJob `dispatched`
   (picked up / dispatched, about to run) — same active blue as running, reusing the
   existing tone (no new color). */
.product-ui-shell .product-ui-status-running,
.product-ui-shell .product-ui-status-processing,
.product-ui-shell .product-ui-status-claimed,
.product-ui-shell .product-ui-status-assigned,
.product-ui-shell .product-ui-status-dispatched {
  background-color: var(--biolitica-status-ink-info);
}

/* BIO-778: `paused` is a held execution needing attention — the same amber as
   pending/warning. */
.product-ui-shell .product-ui-status-partial,
.product-ui-shell .product-ui-status-pending,
.product-ui-shell .product-ui-status-paused,
.product-ui-shell .product-ui-status-missing,
.product-ui-shell .product-ui-status-blocked,
.product-ui-shell .product-ui-status-warning {
  background-color: var(--biolitica-status-ink-warning);
}

.product-ui-shell .product-ui-status-failed,
.product-ui-shell .product-ui-status-error {
  background-color: var(--biolitica-status-ink-danger);
}

/* BIO-778: `not_started` (ProjectExecution/NicheJob) and `queued` (WorkerJob) are
   waiting-not-yet-begun states — neutral steel, same as empty/none. */
.product-ui-shell .product-ui-status-empty,
.product-ui-shell .product-ui-status-unknown,
.product-ui-shell .product-ui-status-none,
.product-ui-shell .product-ui-status-no_execution,
.product-ui-shell .product-ui-status-not_started,
.product-ui-shell .product-ui-status-queued {
  background-color: var(--biolitica-status-ink-neutral);
}

/* Domain terminal outcomes (BIO-573): a finding, not a defect — informational
   steel tone, deliberately distinct from failed-red and warning-amber.
   BIO-778: `cancelled` is a deliberate terminal stop (not a failure) — same
   informational steel-2. */
.product-ui-shell .product-ui-status-not_modelable,
.product-ui-shell .product-ui-status-not_applicable,
.product-ui-shell .product-ui-status-cancelled {
  background-color: var(--biolitica-status-ink-informational);
}

.product-ui-shell .badge.bg-primary {
  background-color: var(--biolitica-status-ink-info) !important;
  color: #ffffff !important;
}

.product-ui-empty-state {
  padding-block: 2rem;
}

.product-ui-shell .btn-primary {
  --tblr-btn-bg: var(--biolitica-primary);
  --tblr-btn-border-color: var(--biolitica-primary-strong);
  --tblr-btn-hover-bg: var(--biolitica-primary-strong);
  --tblr-btn-hover-border-color: var(--biolitica-primary-strong);
  --tblr-btn-color: var(--biolitica-ink);
}

/* BIO-955 — the SECONDARY control was the only half of the pair the shell never branded.
 *
 * The rule above re-marks `btn-primary` to solid brand green; nothing did the equivalent for the
 * secondary, so `btn-outline-secondary` stayed on Tabler's library default — transparent fill,
 * 1px grey border, centered label. Next to a solid green primary that reads as an empty box
 * rather than a control, which is what the 2026-08-05 visual gate saw on «Editar proyecto».
 *
 * Scope is BY CLASS, not by instance: the enumeration over `templates/product_ui/` counts 49
 * secondary/outline usages across 21 files. Writing the rule for one button would have left 48
 * asymmetric.
 *
 * The HIERARCHY is deliberately preserved (the eslabón's point 3): the fill stays a tint, never
 * solid, so the primary keeps winning the page. What changes is legibility as a control — a
 * surface, a brand-tinted border and ink-colored text instead of grey-on-transparent. Expressed
 * through the same `--tblr-btn-*` custom properties as the primary and the same brand tokens
 * (directiva #06/#40: the theme in force, not a new palette). */
.product-ui-shell .btn-outline-secondary,
.product-ui-shell .btn-secondary {
  --tblr-btn-bg: #ffffff;
  --tblr-btn-color: var(--biolitica-ink);
  --tblr-btn-border-color: rgba(26, 26, 26, 0.22);
  --tblr-btn-hover-bg: rgba(var(--biolitica-primary-rgb), 0.14);
  --tblr-btn-hover-color: var(--biolitica-ink);
  --tblr-btn-hover-border-color: var(--biolitica-primary-strong);
  --tblr-btn-active-bg: rgba(var(--biolitica-primary-rgb), 0.22);
  --tblr-btn-active-color: var(--biolitica-ink);
  --tblr-btn-active-border-color: var(--biolitica-primary-strong);
  box-shadow: 0 1px 2px rgba(26, 26, 26, 0.08);
}

/* BIO-955 (segunda mitad) — a stack of actions is a stack of BUTTONS, not a stack of panels.
 *
 * The visual gate of the first half failed, and measuring the DOM said why. `d-grid` is the
 * Bootstrap idiom for "column of full-width buttons", but on a card carrying `h-100` (so it
 * matches the height of its neighbour) the grid container ends up much taller than its rows.
 * With rows left implicit and `align-content` defaulting to `stretch`, the free space is split
 * EQUALLY between the rows, and grid items stretch into them: measured on
 * `pages/projects/detail.html`, «Editar proyecto» rendered 199px tall against a 41px button.
 * A 199px anchor with a vertically centered label IS the "empty box" the gate reported —
 * whatever colour it is. The chromatic half was true but partial.
 *
 * Why the primary looked fine: it is wrapped in a `<form>` (`detail.html:90-96`), so the FORM
 * absorbed the stretch and the button kept its own height. Nothing about the primary was
 * healthy — it was accidentally shielded, which is why the bug was invisible in the other
 * consumer too (`pages/executions/status.html:103`, whose children happen to be all forms).
 *
 * `align-content: start` restores the intent: rows sized to their content, packed at the top.
 * Measured live in production before committing — 199px → 40px, both controls equal. Fixed on
 * the CONTAINER and by class, so the latent case stops being latent. */
.product-ui-shell .card-body.d-grid {
  align-content: start;
}

/* The outline family that already carries the brand hue keeps it; only its border weight is
 * lifted so it reads as a control at the same distance as the pair above. */
.product-ui-shell .btn-outline-primary {
  --tblr-btn-border-color: var(--biolitica-primary-strong);
  --tblr-btn-color: #315f1f;
  --tblr-btn-hover-bg: var(--biolitica-primary);
  --tblr-btn-hover-color: var(--biolitica-ink);
  --tblr-btn-hover-border-color: var(--biolitica-primary-strong);
}

.product-ui-shell .nav-link.active,
.product-ui-shell .nav-item.active .nav-link {
  color: var(--biolitica-ink);
  font-weight: 700;
}

.product-ui-shell .nav-link.active::before,
.product-ui-shell .nav-item.active .nav-link::before {
  background: var(--biolitica-primary);
}

@media print {
  @page {
    margin: 12mm;
  }

  .product-ui-shell {
    background: #ffffff;
  }

  /* BIO-886. `form` a secas estuvo en esta lista y borraba de la impresión toda pantalla
     cuyo contenido ES un form — Configuración perdía sus seis bloques, porque Sujeto y
     Método comparten un solo <form> (normado en product_ui/project_configuration.py). Un
     selector de ELEMENTO no puede distinguir el form que es cromo del que es contenido: la
     hoja lo estaba adivinando. Ahora lo LEE — cada form de cromo declara `d-print-none` en
     su markup, que ya está en esta misma enumeración y ya es la convención de Tabler. La
     clasificación vive pinada en product_ui/tests/test_print_sheet.py. */
  .product-ui-nav,
  .product-ui-header,
  .page-header,
  .d-print-none,
  .navbar,
  .btn-list {
    display: none !important;
  }

  .product-ui-shell .page,
  .product-ui-shell .page-wrapper,
  .product-ui-shell .page-body,
  .product-ui-shell .container-xl {
    display: block !important;
    width: 100% !important;
    max-width: none !important;
    margin: 0 !important;
    padding: 0 !important;
  }

  .product-ui-card,
  .card,
  .list-group-item,
  .product-ui-stage-block {
    box-shadow: none !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .table-responsive {
    overflow: visible !important;
  }

  .table {
    font-size: 10px;
  }

  .badge {
    border: 1px solid #cccccc;
  }
}
/* ── BIO-1005 · el rail vertical de pasos de Configuración ───────────────────────────────
 *
 * `product-ui-configuration-steps` existía en el markup desde BIO-874 y no tenía NI UNA regla:
 * dos apariciones en todo el repo, las dos en `configuration.html`. El rail vertical necesita
 * estilos reales, y por ADR-034 R6 viven acá, con los tokens de `:root`, nunca inline.
 *
 * El rail se lee de arriba abajo y SIN numeración: dice qué necesita cada paso, no qué posición
 * ocupa. Los sub-pasos de Método se sangran y comparten una guía vertical, que es lo que hace
 * legible que son partes de una cosa y no cinco hermanos más.
 */
.product-ui-configuration-rail .product-ui-configuration-steps {
  gap: 0.125rem;
}

/* BIO-1037: `.product-ui-rail-group` entra acá porque el encabezado del grupo DEJÓ DE LLEVAR
 * `.nav-link` — con esa clase Bootstrap lo seguía enumerando como pestaña del tablist, que es el
 * defecto. Lo que se movió es de dónde le llega la forma, no la forma: mismos tokens, misma
 * geometría, ninguna entrada nueva. `text-decoration: none` sí es nueva y es la única cosa que
 * `.nav-link` daba y ninguna regla de acá cubría; sobre `.nav-link` es un no-op. */
.product-ui-configuration-rail .nav-link,
.product-ui-configuration-rail .product-ui-rail-group {
  display: flex;
  text-decoration: none;
  /* BIO-1019: `wrap` para que la descripción del paso caiga en su PROPIA línea. La alternativa
   * era anidar la etiqueta y el estado en un contenedor nuevo, y eso mueve el markup que la
   * batería de 1018 asevera. Envolver deja label + detalle + estado en la primera línea y la
   * descripción en la segunda, sin tocar ni un nodo existente. */
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  border-radius: 0.375rem;
  padding: 0.5rem 0.75rem;
  color: var(--biolitica-ink);
  text-align: left;
}

/* BIO-1033 / BIO-1035 · el tratamiento de CABECERA de las tarjetas de la etapa.
 *
 * El mockup pone un párrafo explicativo DENTRO de la cabecera de cada tarjeta, y la cabecera de la
 * biblioteca vigente es una fila horizontal centrada. Esa forma no se hereda: se construye — y
 * afecta a las nueve tarjetas de la etapa, que es por lo que vive acá y no en una plantilla.
 *
 * `align-items: flex-start` es la diferencia con la fila vigente: con un título de una línea y una
 * nota de dos, centrar verticalmente deja el título flotando a media altura del bloque. El título y
 * su nota se agrupan para que la acción de la derecha se separe del PAR y no sólo del título.
 *
 * Cero entradas nuevas: es composición y espaciado del sistema vigente. La forma de tarjeta la da
 * `.product-ui-card`, que ya existía y que la zona de veredicto ya usaba — el resto de la etapa la
 * gana ahora, que es lo que hace verdadera «el mismo tratamiento que el resto». */
.product-ui-stage-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.product-ui-stage-card-heading {
  min-width: 0;
}

.product-ui-stage-card-note {
  margin-top: 0.2rem;
  max-width: 72ch;
  font-size: 0.875rem;
  line-height: 1.5;
  color: rgba(26, 26, 26, 0.6);
}

/* BIO-1036 · la caja de «hueco declarado», como COMPONENTE con nombre y no como token.
 *
 * La ratificación la separa a propósito de la lista de tokens: no es un valor, es una pieza — y
 * lleva un argumento de producto encima, que es DECLARAR la ausencia en vez de taparla. Una zona
 * de la pantalla que no ofrece controles porque no los hay se dice; dejarla en blanco se lee como
 * un error de carga.
 *
 * Con los valores del sistema vigente y CERO entradas nuevas: el borde discontinuo es la única
 * señal propia, y es forma, no color. Se declara acá para que el próximo hueco la reuse en vez de
 * volver a inventarla en su plantilla. */
.product-ui-declared-gap {
  border: 2px dashed rgba(26, 26, 26, 0.16);
  border-radius: 0.5rem;
  padding: 1rem;
  color: rgba(26, 26, 26, 0.6);
  font-size: 0.875rem;
  line-height: 1.55;
}

/* BIO-1025 · la barra de guardado, ahora una POR PANEL y al pie del panel.
 *
 * Antes no era una barra: era un `<div class="mb-3">` con un párrafo y un botón, sin caja, sin
 * borde y sin quedarse a la vista. `position: sticky; bottom: 0` la mantiene alcanzable mientras el
 * operador recorre un panel largo, que es el caso que la motiva —Entrenamiento tiene 24 controles.
 *
 * La sombra ASCENDENTE que el mockup pide no tiene entrada en el sistema y la decisión ratificada
 * de BIO-1036 dice «no ahora — se usa la sombra vigente». Así que se usa la de `.product-ui-card`,
 * la misma que ya eleva las tarjetas de esta pantalla: se cablea la MISMA declaración en vez de
 * inventar un valor nuevo. Cuando el sistema gane una escala de sombra, este sitio es uno.
 *
 * `justify-content: space-between` pone la nota a la izquierda y el botón a la derecha; cuando la
 * barra no lleva botón —los cuatro pasos que no son del formulario compartido— la nota ocupa la
 * fila y no queda un hueco a la derecha, porque no hay segundo hijo que separar. */
.product-ui-save-bar {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-block: 1rem;
  padding: 0.7rem 1rem;
  background: #ffffff;
  border: 1px solid rgba(26, 26, 26, 0.08);
  border-radius: 0.5rem;
  box-shadow: 0 0.5rem 1.25rem rgba(26, 26, 26, 0.05);
}

.product-ui-save-bar .product-ui-save-bar-scope {
  color: rgba(26, 26, 26, 0.6);
  font-size: 0.875rem;
}

/* BIO-1024 · la columna se queda a la vista.
 *
 * `position: sticky` sobre el <nav>, no sobre la <ul>: el rótulo «Pasos» tiene que viajar con la
 * lista, y anclar la lista dejaría el encabezado atrás. El desplazamiento superior es el del
 * mockup. En pantallas donde la rejilla colapsa a una columna, Bootstrap apila `col-lg-*` y el
 * menú vuelve a ir arriba — que es el comportamiento correcto ahí: pegar una banda de siete
 * renglones en un móvil se comería la pantalla. */
@media (min-width: 992px) {
  .product-ui-configuration-layout .product-ui-configuration-rail {
    position: sticky;
    top: 1rem;
  }
}

/* El rótulo de la columna. Existía sólo como `aria-label`: un nombre que sólo oye un lector de
 * pantalla deja sin encabezado a todos los demás. Tenue y en versalitas porque nombra la columna,
 * no compite con los pasos. */
.product-ui-configuration-rail-title {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(26, 26, 26, 0.55);
  margin-bottom: 0.5rem;
}

/* La marca de estado: `status-dot` de Tabler, el componente pequeño que la biblioteca ya trae
 * (decisión ratificada de BIO-1036 — cero entradas nuevas al sistema). Sólo se le da posición:
 * el tono lo pone `.status-{success|warning|danger|secondary}` de la propia biblioteca.
 *
 * `flex: 0 0 auto` y el margen superior la alinean con la primera línea del rótulo en vez de
 * centrarla contra un bloque de dos líneas, que es lo que la descarrilaba cuando el paso lleva
 * descripción. `aria-hidden` va en la plantilla: el estado ya viaja como TEXTO en la misma fila,
 * así que anunciarlo dos veces sería leerlo dos veces. */
.product-ui-configuration-rail .product-ui-rail-mark {
  flex: 0 0 auto;
  margin-top: 0.45rem;
}

.product-ui-configuration-rail .product-ui-configuration-substeps .product-ui-rail-mark {
  --tblr-status-size: 0.375rem;
  margin-top: 0.4rem;
}

.product-ui-configuration-rail .nav-link:hover,
.product-ui-configuration-rail .product-ui-rail-group:hover {
  background: rgba(var(--biolitica-primary-rgb), 0.12);
}

/* BIO-1036 · el paso activo pasa de pastilla verde SÓLIDA a tarjeta blanca elevada.
 *
 * **Es la única vez en este lote en que cede el sistema y no el diseño**, y la razón está escrita
 * para no tener que reconstruirla: en un menú de siete renglones —cinco pasos, tres de ellos
 * anidados— con una columna de marcas de estado a la izquierda, un relleno saturado en el renglón
 * activo pesa demasiado y COMPITE con las marcas, que son el otro portador de estado. La tarjeta
 * elevada resuelve «dónde estoy» sin gastar el color de marca, y deja el verde para lo que sí es
 * estado.
 *
 * Sin entradas nuevas: blanco, el mismo borde tenue de las tarjetas y la misma sombra de
 * `.product-ui-card`. El rótulo se distingue por peso, no por color. */
.product-ui-configuration-rail .nav-link.active {
  background: #ffffff;
  color: var(--biolitica-ink);
  font-weight: 600;
  border: 1px solid rgba(26, 26, 26, 0.08);
  box-shadow: 0 0.125rem 0.375rem rgba(26, 26, 26, 0.07);
}

/* El grupo (Método) no es un destino en sí: apunta a su primer sub-paso. Se marca como
 * encabezado y no como paso para que no compita visualmente con sus hijos. */
.product-ui-configuration-rail .product-ui-rail-group {
  font-weight: 600;
  text-transform: none;
}

.product-ui-configuration-rail .product-ui-rail-label {
  flex: 1 1 auto;
}

/* La guía vertical de los sub-pasos: es lo que dice «esto es una parte de lo de arriba».
 * `border-inline-start` y no `border-left` porque el eje lógico sobrevive un locale RTL. */
.product-ui-configuration-rail .product-ui-configuration-substeps {
  /* BIO-1037: la lista dejó de ser `.nav` —esa clase era la mitad del defecto, porque el
   * `closest()` con que Bootstrap resuelve el ámbito de una pestaña casa `.nav` igual que
   * `[role="tablist"]`—, así que lo que `.nav` le daba se declara acá: caja flexible en columna,
   * sin viñetas y sin los márgenes que el reboot pone a un `<ul>`. */
  display: flex;
  flex-direction: column;
  list-style: none;
  margin-block: 0;
  margin-inline-start: 0.75rem;
  padding-inline-start: 0.5rem;
  border-inline-start: 2px solid rgba(var(--biolitica-primary-rgb), 0.35);
  gap: 0.125rem;
}

.product-ui-configuration-rail .product-ui-configuration-substeps .nav-link {
  font-size: 0.9375rem;
  padding-block: 0.375rem;
}

/* El estado derivado. Se pinta como texto tenue y no como badge de color: un badge compite con
 * el badge de errores que ya vive en la misma línea.
 *
 * BIO-1019: este comentario decía «Dos formas, no cuatro — ver `_step_status` para las dos que el
 * mockup pide y que hoy no tienen fuente veraz». Caducó: BIO-1018 añadió `complete` con su fuente
 * (la atribución de bloqueadores del veredicto) y `empty` ya existía, así que hoy son TRES. La que
 * sigue sin fuente veraz es la de Contexto —«3 sugeridas · sin revisar»—, que depende de BIO-1011
 * y quedó en standby; y ésa no es un estado sino un DETALLE, que es otra clase (ver abajo). */
.product-ui-configuration-rail .product-ui-rail-status {
  flex: 0 0 auto;
  font-size: 0.8125rem;
  font-weight: 400;
  color: rgba(26, 26, 26, 0.6);
  white-space: nowrap;
}

.product-ui-configuration-rail .nav-link.active .product-ui-rail-status {
  color: rgba(26, 26, 26, 0.75);
}

.product-ui-configuration-rail .product-ui-rail-status-errors {
  color: var(--tblr-danger, #d63939);
  font-weight: 500;
}

/* BIO-1019 · el DETALLE descriptivo: cuánto tiene el paso, no si está bien.
 *
 * Deliberadamente NO reusa `product-ui-rail-status`, y no es cosmética: BIO-1018 estableció que
 * `complete` es la única insignia que AFIRMA y por eso la única con regla de supresión. Un conteo
 * no afirma nada sobre la corrección del paso, así que compartir la clase invitaría a leerlo —y a
 * tratarlo en el código— como si afirmara. Se distingue por peso, no por color: el color en esta
 * línea ya lo usa el estado de errores y competirían. */
.product-ui-configuration-rail .product-ui-rail-detail {
  flex: 0 0 auto;
  font-size: 0.8125rem;
  font-weight: 500;
  color: rgba(26, 26, 26, 0.55);
  white-space: nowrap;
}

/* La descripción del paso: segunda línea, ancho completo, y por debajo de todo lo demás en peso
 * visual — es orientación, no estado. `flex-basis: 100%` es lo que la baja de línea sin envolver
 * el resto en un contenedor nuevo. */
.product-ui-configuration-rail .product-ui-rail-description {
  flex: 1 0 100%;
  font-size: 0.8125rem;
  font-weight: 400;
  line-height: 1.3;
  color: rgba(26, 26, 26, 0.55);
}

.product-ui-configuration-rail .nav-link.active .product-ui-rail-description,
.product-ui-configuration-rail .nav-link.active .product-ui-rail-detail {
  color: rgba(26, 26, 26, 0.75);
}

/* BIO-1006 — the method-parameter groups. Six collapsible <details>, one per question the
   parameters answer, replacing a flat alphabetical list of 24 controls.

   Native <details>/<summary>, no JS: the disclosure has to work before any script runs, the same
   reason the rail resolves its open group server-side. A group holding an errored control gets
   `open` from the server (see `ProjectEditForm.method_param_groups`), so a validation error is
   never hidden behind a collapsed heading.

   Colours come from the theme tokens (ADR-034 R6), never hard-coded: `--tblr-border-color` and
   `--tblr-secondary` with the same literal fallbacks the rest of this file uses. */
.product-ui-method-param-group {
  border: 1px solid var(--tblr-border-color, #e6e7e9);
  border-radius: 4px;
}

.product-ui-method-param-group-summary {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.625rem 0.875rem;
  cursor: pointer;
  font-weight: 500;
}

.product-ui-method-param-group[open] .product-ui-method-param-group-summary {
  border-block-end: 1px solid var(--tblr-border-color, #e6e7e9);
}

.product-ui-method-param-group-label {
  flex: 1 1 auto;
}

/* The derived counter. Quiet on purpose: it reports coverage, it is not the heading. */
.product-ui-method-param-group-count {
  flex: 0 0 auto;
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--tblr-secondary, #6c7a91);
  white-space: nowrap;
}

.product-ui-method-param-group-body {
  padding: 0.875rem 0.875rem 0.25rem;
}

/* The explained empty state of Método·Entrenamiento — the state every new project starts in
   since PR #1169. `border-inline-start` and not `border-left` so the accent survives an RTL
   locale, same criterion as the rail's sub-step guide. */
.product-ui-method-param-empty {
  border-inline-start: 3px solid var(--tblr-border-color, #e6e7e9);
  padding: 0.25rem 0 0.25rem 0.75rem;
  color: var(--tblr-secondary, #6c7a91);
}

/* BIO-1029 · el subtítulo del grupo: la frase que dice qué pregunta contesta el conjunto.
 * Tamaño y tinta de texto secundario, consumidos del sistema — nunca un literal. */
.product-ui-method-param-group-heading {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.product-ui-method-param-group-subtitle {
  font-weight: 400;
  font-size: 0.875em;
  color: var(--tblr-secondary, #6c757d);
}

/* BIO-1259 · la marca del encabezado PÚBLICO tenía caja cero, y la causa no era la que se supuso.
 *
 * Medido sobre la hoja del tema que la página carga de verdad (Tabler 1.4.0 por CDN, traída con
 * `curl` porque el navegador la bloquea por CORS): NO existe ninguna regla `.navbar-brand img` ni
 * la variable `--tblr-navbar-brand-image-height`. Lo que sí existe, y es lo que gana, es el reboot
 * `img{max-width:100%;height:auto}`.
 *
 * `height:auto` es una DECLARACIÓN CSS, así que pisa al atributo `height="32"` del HTML — los
 * presentational hints tienen la prioridad más baja de la cascada. Y `logo.svg` no tiene
 * dimensiones intrínsecas (sólo `viewBox`, medido sobre el fichero servido en producción), así que
 * `auto` no resuelve contra nada y la caja colapsa a 0×0.
 *
 * Por eso el shell del operador NO lo sufre y no hay que tocarlo: su marca es un `.webp` con
 * píxeles intrínsecos y con `width`/`height` explícitos, así que `auto` sí resuelve.
 *
 * El arreglo va acá y no en la plantilla: `base.html` carga esta hoja DESPUÉS del CDN, y el
 * selector descendiente `(0,2,0)` gana al `img` del reboot `(0,0,1)` sin depender de nada externo.
 * Un `style="height:32px"` en el marcado habría tapado el síntoma dejando la clase muerta.
 *
 * `2rem` es exactamente lo que el tema quiere para una marca —`.navbar-brand-image{height:2rem}`—
 * y lo mismo que el atributo pedía; `width:auto` deja que el `viewBox` fije la proporción. */
.product-ui-public-brand .product-ui-public-logo {
  height: 2rem;
  width: auto;
}
