/* itsfriday - estilos compartidos. Diseño SaaS moderno, baja densidad visual
   (docs/01-foundation/01-architecture-foundations.md, "UX específica"). */

:root {
  --if-bg: #f6f7fb;
  --if-surface: #ffffff;
  --if-border: #e3e5ec;
  --if-text: #212529;
  --if-text-muted: #6b7280;
  --if-icon-muted: #c1c5cd;
  --if-focus: #4f46e5;
  --if-highlight-bg: #eef2ff;
  --if-danger: #dc2626;
  --if-success: #16a34a;
  --if-sidebar-width: 220px;
  --if-panel-width: 480px;
  --if-topbar-height: 56px;

  /* Fase 10, ítem 77: escala de tamaños de letra — antes cada componente
     declaraba su propio valor suelto (0.7rem/0.75rem/0.8rem/0.8125rem/
     0.9rem/0.975rem), con varios casi-duplicados sin ninguna razón de
     diseño (0.8rem vs 0.8125rem, 0.9rem vs el base 0.9375rem) que solo
     reflejaban cuándo se escribió cada regla, no una decisión. Reducida a
     5 pasos con nombre; --if-font-base es el tamaño por defecto del body. */
  --if-font-xs: 0.7rem;
  --if-font-sm: 0.75rem;
  --if-font-md: 0.8125rem;
  --if-font-base: 0.9375rem;
  --if-font-lg: 0.975rem;

  /* Escala de border-radius — mismo criterio: "6px" y "0.375rem" (el mismo
     valor, escrito distinto) o "0.4rem"/"0.375rem" (casi iguales, sin razón
     de ser distintos) convivían sueltos. Las esquinas exactas que igualan
     el ancho de un borde de color (ítems 42/60, 5px) NO forman parte de
     esta escala a propósito: ahí el valor importa por sí mismo (debe
     calzar con ese ancho), no por quedar "parejo" con otro componente. */
  --if-radius-xs: 0.25rem;
  --if-radius-sm: 0.375rem;
  --if-radius-md: 0.5rem;
  --if-radius-lg: 0.625rem;
  --if-radius-xl: 0.75rem;
  --if-radius-pill: 999px;
}

body {
  background-color: var(--if-bg);
  font-size: var(--if-font-base);
}

/* Foco visible siempre, nunca solo color (accesibilidad de teclado) */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--if-focus);
  outline-offset: 2px;
}

/* Fase 10, ítem 59: la barra superior prevalece por sobre los paneles
   laterales — fija, de ancho completo, con el z-index más alto de los tres
   (por encima del panel lateral izquierdo, 1020, y el de propiedades,
   1050) para que nunca quede tapada por ninguno; ambos paneles arrancan
   `var(--if-topbar-height)` más abajo en vez de superponerse con ella
   (ver `.if-sidebar`/`.if-panel` más abajo). Antes vivía dentro de
   `.if-app-shell` (en flujo normal, empujada por los paneles) — ahora es
   su propia capa fija, así que el contenido necesita su propio
   `padding-top` para no quedar tapado (`.if-app-shell`, más abajo). */
.if-topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--if-topbar-height);
  display: flex;
  align-items: center;
  background: var(--if-surface);
  border-bottom: 1px solid var(--if-border);
  z-index: 1060;
}

/* Fase 10, ítem 82: Bootstrap ubica sus modales (clase `.modal`, usados por
   #participants-modal y por los popups de `app-prompt.js`: "Nueva columna",
   "Nuevo grupo", confirmar/avisar) en z-index 1055, y su fondo oscuro
   (`.modal-backdrop`) en 1050 — ambos por debajo de la barra superior de
   arriba (1060), que es opaca. Como el margen superior por defecto del
   modal (1.75rem ≈ 28px) es menor que la altura de la barra (56px), la
   parte de arriba del modal, incluido su título, quedaba dibujada detrás de
   la barra y se veía cortada. Subidos por sobre la barra pero por debajo de
   los popups propios de la app (ColorPicker/PersonaPicker/LabelValuePicker,
   1080), que ya conviven con un modal abierto (p.ej. el selector de color
   dentro de "Nueva etiqueta") y deben seguir ganando. */
.modal {
  z-index: 1070;
}

.modal-backdrop {
  z-index: 1065;
}

/* Fase 10, ítem 55: la barra superior ya no reutiliza `.if-main` para su
   contenido interno — ese límite de 1100px dejaba el botón de
   notificaciones/usuario/"Salir" atrapados lejos del borde derecho real de
   la pantalla, con un hueco vacío hasta el final del viewport. Ocupa todo
   el ancho disponible (dentro de lo que empuja el panel lateral), con el
   mismo padding horizontal que ya tenía `.if-main` para que el contenido de
   abajo siga alineado. */
.if-topbar-inner {
  width: 100%;
  padding: 0 1rem;
}

/* Fase 10, ítem 81: logo en vez del texto "itsfriday" — el archivo original
   (2172x724px) es mucho más grande que el espacio real de la barra
   superior (56px de alto); se limita por altura y el ancho se ajusta solo
   (misma proporción, ~3:1) para que no se vea borroso ni desbordado. Fase
   10, ítem 101: 20% más grande (28px → 34px, sigue holgado dentro de los
   56px de alto de la barra) — pedido explícito del usuario. */
.if-brand-logo {
  display: block;
  height: 34px;
  width: auto;
}

/* Fase 10, ítem 98: mismo logo en la pantalla de login (antes, ítem 81, se
   dejó afuera a propósito — esa pantalla no tiene barra superior, solo un
   <h1> de texto centrado). Más grande que `.if-brand-logo` porque aquí es
   el elemento principal de la página, no un ícono chico junto a otros
   controles de una barra. Fase 10, ítem 101: 20% más grande (40px → 48px),
   mismo pedido que la barra superior. */
.if-login-logo {
  display: block;
  height: 80px;
  width: auto;
  margin-left: auto;
  margin-right: auto;
}

.if-main {
  max-width: 1100px;
  /* Fase 10, ítem 54: antes centrado (`margin: 0 auto`) — tenía sentido
     cuando el ancho disponible era toda la pantalla, pero desde que el
     panel lateral (ítem 53) empuja el contenido con `margin-left` en
     `.if-app-shell`, centrar además `.if-main` dentro de ese espacio ya
     angosto dejaba un hueco vacío a la izquierda en vez de arrancar pegado
     al panel. Pegado a la izquierda, con el máximo ancho intacto. */
  margin: 0;
  padding: 1.5rem 1rem 3rem;
}

/* Fase 10, ítem 57: el usuario confirmó que quería el scroll de Kanban
   hasta el borde REAL de la pantalla, no solo hasta el borde derecho del
   área de contenido (1100px) — `width: 100%` del ítem 56 no alcanzaba
   porque sigue acotado por el `max-width` de `.if-main` de arriba, su
   ancestro. Fase 10, ítem 94: ampliado a las tres vistas del tablero (antes
   solo Kanban) — la fila de botones (Tabla/Kanban/Dashboard, Nuevo ítem,
   etc.) se queda corta contra el límite de 1100px en vez de llegar al
   borde real en Tabla/Dashboard, mismo problema de fondo que motivó el
   ítem 57 para Kanban. Clase aplicada mientras hay un tablero abierto
   (app.js, `setMainWide()`, `mountActiveView()`) — las páginas que no son
   del tablero (Tableros, Mi trabajo, Usuarios, Cuenta) no lo pidieron y
   conservan el límite de 1100px. */
.if-main.if-main-wide {
  max-width: none;
}

/* Fase 10, ítem 53: panel lateral mostrable/ocultable — "empuja" el
   contenido (header + main, envueltos en .if-app-shell) en vez de
   superponerse, para no tapar nada al abrirse. Mismo z-index que no
   compita con los popups propios (ColorPicker/PersonaPicker, z-index 1030)
   ni con los modales de Bootstrap (1055+) — el panel va debajo de ambos. */
.if-sidebar {
  position: fixed;
  /* Fase 10, ítem 59: empieza debajo de la barra superior (ítem 58 lo hacía
     empezar desde arriba de la pantalla, quedando detrás de ella). */
  top: var(--if-topbar-height);
  left: 0;
  bottom: 0;
  width: var(--if-sidebar-width);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 1rem 0.75rem;
  background: var(--if-surface);
  border-right: 1px solid var(--if-border);
  overflow-y: auto;
  z-index: 1020;
  transition: transform 0.2s ease;
}

.if-sidebar.if-sidebar-hidden {
  transform: translateX(-100%);
}

/* Fase 10, ítem 77: sin font-size propio (antes 0.9rem) — a 0.6px del
   tamaño base del body (0.9375rem), la diferencia no se notaba y no
   respondía a ninguna decisión de diseño documentada; hereda el base. */
.if-sidebar-link {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--if-radius-sm);
  color: var(--if-text);
  text-decoration: none;
}

.if-sidebar-link:hover {
  background: var(--if-bg);
}

.if-sidebar-link i {
  width: 1.1rem;
  text-align: center;
  color: var(--if-text-muted);
}

/* Fase 10, ítem 93: submenú colapsable de tableros bajo "Tableros" — el
   link principal (flex-grow-1, en app.html) sigue ocupando el resto de la
   fila como antes; el botón de colapsar es un elemento aparte, al final. */
.if-sidebar-group {
  display: flex;
  flex-direction: column;
}

.if-sidebar-collapse-toggle {
  flex-shrink: 0;
  border: 0;
  background: none;
  color: var(--if-text-muted);
  padding: 0.25rem 0.6rem;
  border-radius: var(--if-radius-sm);
  cursor: pointer;
}

.if-sidebar-collapse-toggle:hover {
  background: var(--if-bg);
}

.if-sidebar-collapse-toggle i {
  transition: transform 0.15s ease;
}

/* Colapsado: el triangulito apunta a la derecha en vez de hacia abajo. */
.if-sidebar-collapse-toggle.is-collapsed i {
  transform: rotate(-90deg);
}

.if-sidebar-sublist {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

/* Alineado bajo el texto de "Tableros", no bajo su ícono (0.75rem de
   padding + 1.1rem de ícono + 0.6rem de separación del link que imita). */
.if-sidebar-sublink {
  padding-left: 2.45rem;
  font-size: var(--if-font-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Fase 10, ítem 67: variante para un <button> (abre un modal, no navega a
   una ruta propia como el resto de los ítems del menú) con la misma
   apariencia que un <a class="if-sidebar-link"> — reset de los estilos por
   defecto de <button>. */
.if-sidebar-link-btn {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* Fase 10, ítem 78: el nombre/rol de usuario de la barra superior reutiliza
   el mismo estilo de link que el menú lateral, pero ahí vive en una fila
   flex horizontal junto a otros botones (notificaciones, Salir) — el
   `width: 100%` de ".if-sidebar-link-btn" (pensado para una columna) le
   haría ocupar todo el espacio sobrante de esa fila. */
#user-name-button {
  width: auto;
}

.if-app-shell {
  margin-left: var(--if-sidebar-width);
  /* Fase 10, ítem 59: la barra superior pasó a ser una capa fija fuera del
     flujo normal (`.if-topbar`, arriba) — sin este padding, el contenido
     arrancaría tapado debajo de ella. */
  padding-top: var(--if-topbar-height);
  transition: margin-left 0.2s ease, margin-right 0.2s ease;
}

.if-app-shell.if-app-shell-full {
  margin-left: 0;
}

/* Fase 10, ítem 58: mismo criterio que el panel lateral izquierdo (arriba)
   — el panel de propiedades "empuja" el contenido en vez de superponerse
   encima con un fondo oscuro (`.if-panel-backdrop`, ítem 58, eliminado),
   para que ambos convivan visibles y la barra superior (con su botón
   hamburger propio, siempre a la izquierda del de notificaciones) nunca
   quede tapada. */
.if-app-shell.if-app-shell-panel-open {
  margin-right: var(--if-panel-width);
}

.if-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 3rem 1rem;
  color: var(--if-text-muted);
  text-align: center;
}

/* Fase 10, ítem 60: mismo criterio ya resuelto para el borde de color de
   los grupos en la vista Tabla (ítem 42) — un `border-radius` uniforme
   (0.5rem, antes compartido con `.if-item-row`) mayor que el ancho del
   borde de color (5px, ítem 46) producía el mismo efecto "muesca": el
   radio alcanzaba a curvar también el lado donde el borde se encuentra con
   el fondo de la tarjeta, no solo la punta exterior. Radio de las esquinas
   izquierdas igual al ancho del borde: un cuarto de círculo limpio, sin
   comerse nada del fondo. Las esquinas derechas conservan el radio
   original (0.5rem) — ahí no hay borde de color que redondear. */
.if-board-card {
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
  border-top-right-radius: var(--if-radius-md);
  border-bottom-right-radius: var(--if-radius-md);
}

/* Fase 10, ítem 135: el enlace al tablero ahora es solo el nombre/
   subtítulo, no la tarjeta entera (los botones de acción viven fuera de
   él) — sin estilo propio más que heredar el color de texto normal, igual
   que antes tenía `.if-board-card` cuando era el propio `<a>`. */
.if-board-card-link {
  color: inherit;
}

/* Fase 10, ítem 135: título de sección ("Activos"/"Archivados") más
   grande y centrado que `.if-field-label` (pensado para etiquetas de
   campo chicas, alineadas a la izquierda), con una línea a cada lado —
   técnica estándar de flexbox: el texto entre dos pseudo-elementos que se
   reparten el espacio sobrante (`flex: 1`) con un borde inferior. */
.if-board-section-title {
  display: flex;
  align-items: center;
  text-align: center;
  font-size: 1.1rem;
  font-weight: 500;
  color: var(--if-text-muted);
  margin: 1.75rem 0 1rem;
}

.if-board-section-title:first-child {
  margin-top: 0;
}

.if-board-section-title::before,
.if-board-section-title::after {
  content: '';
  flex: 1 1 auto;
  border-bottom: 1px solid var(--if-border);
}

.if-board-section-title::before {
  margin-right: 0.85rem;
}

.if-board-section-title::after {
  margin-left: 0.85rem;
}

.if-item-row {
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.if-item-row:hover,
.if-item-row:focus-visible {
  border-color: var(--if-focus);
}

/* Fase 10, ítem 179: "Mi trabajo" como matriz — 1 columna de etiqueta +
   3 columnas de rol RACI, auto-flow implícito (sin `grid-row` en ningún
   lado, mismo criterio que el Roadmap, ítem 116/165): el encabezado (4
   elementos) y cada fila de rango de fecha (4 elementos más) cierran solos
   apenas se llenan las 4 columnas de `grid-template-columns`.
   `overflow:auto` para que las 3 columnas de contenido no se aprieten en
   una pantalla angosta (scroll horizontal) y para que se recorte a las
   esquinas redondeadas del ítem 180.
   Fase 10, ítem 180: `gap:1px` + `background:var(--if-border)` en el
   contenedor, con cada celda pintando su propio fondo encima — truco
   habitual para líneas divisorias finas sin dibujar un borde por celda:
   el único píxel de separación entre celdas deja ver el gris del fondo del
   contenedor, formando las líneas internas de la grilla. El `border` +
   `border-radius` del contenedor dan el borde exterior, también gris y
   redondeado, a pedido explícito del usuario. */
.if-mywork-grid {
  display: grid;
  grid-template-columns: 8rem repeat(3, minmax(14rem, 1fr));
  gap: 1px;
  background: var(--if-border);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-lg);
  overflow: auto;
}

.if-mywork-corner,
.if-mywork-col-header {
  background: var(--if-surface);
}

.if-mywork-corner {
  position: sticky;
  left: 0;
}

.if-mywork-col-header {
  font-size: var(--if-font-base);
  font-weight: 700;
  color: var(--if-text-muted);
  padding: 0.5rem 0.25rem;
  text-align: center;
}

.if-mywork-row-header {
  font-size: var(--if-font-base);
  font-weight: 700;
  color: var(--if-text-muted);
  padding: 0.5rem 0.5rem;
  white-space: nowrap;
  position: sticky;
  left: 0;
}

.if-mywork-cell {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.5rem;
}

/* Fase 10, ítem 180: un color pastel suave por fila (rango de fecha), a
   pedido explícito del usuario — mismo tono en el encabezado de esa fila y
   en sus 3 celdas, para que se lea como una franja completa de un vistazo,
   no solo una etiqueta de texto a la izquierda. */
.if-mywork-row-vencidos {
  background: #fee2e2;
}

.if-mywork-row-hoy {
  background: #fef9c3;
}

.if-mywork-row-esta-semana {
  background: #dcfce7;
}

.if-mywork-row-sin-fecha {
  background: #e5e7eb;
}

.if-group-heading {
  /* Fase 10, ítem 38: en negrita (600 -> 700) a pedido — antes competía
     poco con el resto del texto.
     Fase 10, ítem 159: tamaño igualado al del título de grupo del Roadmap
     (`--if-font-base`, antes `--if-font-lg`) y sin mayúsculas forzadas —
     ambos a pedido explícito del usuario, sin tocar peso ni color. */
  font-size: var(--if-font-base);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--if-text-muted);
  margin: 1.25rem 0 0.5rem;
}

/* Fila de encabezado de grupo con controles del mantenedor (Fase 10, ítem 6).
   Fase 10, ítem 73: el margin-top que separaba un grupo del anterior
   (1.25rem) se movió a ".if-group-dropzone" (ver arriba) — ahora el único
   espacio "de arriba" de este elemento es el padding fijo del recuadro que
   lo envuelve, para que sea igual al de los otros tres lados. El
   margin-bottom (separación con la tabla) se mantiene igual: ya eran 8px,
   el mismo valor que el nuevo padding del recuadro. */
.if-group-heading-row {
  margin: 0 0 0.5rem;
}

.if-group-heading-row .if-group-heading {
  margin: 0;
}

/* Fase 10, ítem 39: mismo gris que el ícono de arrastre de fila (#c1c5cd,
   ítem 38) — Bootstrap pintaba este botón con el color de enlace (azul) por
   ser `btn-link`, sin relación visual con el otro control "secundario" de
   la fila de encabezado del grupo. */
.if-group-menu-trigger {
  color: var(--if-icon-muted);
}

.if-group-menu-trigger:hover,
.if-group-menu-trigger:focus {
  color: var(--if-focus);
}

/* Fase 10, ítem 45: menú de columna (⋮) en el encabezado de la vista Tabla
   — mismo criterio de color que el menú de grupo (arriba): un `btn-link`
   fuerza su propio color de enlace, sin relación con el gris apagado que ya
   usa el resto del encabezado (`.if-table th`). */
.if-column-menu-trigger {
  color: var(--if-icon-muted);
}

.if-column-menu-trigger:hover,
.if-column-menu-trigger:focus {
  color: var(--if-focus);
}

/* Mantenedor de etiquetas (Fase 10, ítems 4 y 5) */
.if-label-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--if-border);
}

.if-label-row:last-child {
  border-bottom: none;
}

/* Fase 10, ítem 99: flechitas "Subir"/"Bajar" para reordenar etiquetas a
   mano — una encima de la otra (como un stepper), no lado a lado, para no
   agregar más ancho a una fila ya con bastantes controles. */
.if-label-move-buttons {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  line-height: 1;
}

.if-label-move-buttons button {
  color: var(--if-icon-muted);
  padding: 0;
  font-size: var(--if-font-xs);
}

.if-label-move-buttons button:hover:not(:disabled) {
  color: var(--if-focus);
}

.if-label-move-buttons button:disabled {
  opacity: 0.3;
  cursor: default;
}

/* Botón-swatch que reemplaza <input type="color"> (Fase 10, ítem 13) */
.if-color-swatch-btn {
  width: 1.75rem;
  height: 1.75rem;
  min-width: 1.75rem;
  border-radius: var(--if-radius-sm);
  border: 1px solid var(--if-border);
  padding: 0;
  cursor: pointer;
}

.if-color-swatch-btn:focus-visible {
  outline: 2px solid var(--if-focus);
  outline-offset: 1px;
}

/* Variante compacta para filas de encabezado de grupo (Fase 10, ítem 27),
   junto a los demás botones-icono de esa fila (renombrar/reordenar/eliminar). */
.if-color-swatch-btn-sm {
  width: 1.1rem;
  height: 1.1rem;
  min-width: 1.1rem;
  border-radius: var(--if-radius-xs);
}

/* Popup del selector de color propio de la app (Fase 10, ítem 13):
   gradiente de saturación/brillo + barra de tono + hex + colores sugeridos,
   en vez del selector nativo del sistema operativo. */
.if-colorpicker-popup {
  position: fixed;
  z-index: 1080;
  width: 15rem;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-lg);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.18);
  padding: 0.75rem;
}

.if-colorpicker-sv {
  position: relative;
  width: 100%;
  height: 9rem;
  border-radius: var(--if-radius-md);
  cursor: crosshair;
  touch-action: none;
  overflow: hidden;
}

.if-colorpicker-sv-white,
.if-colorpicker-sv-black {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.if-colorpicker-sv-white {
  background: linear-gradient(to right, var(--if-surface), rgba(255, 255, 255, 0));
}

.if-colorpicker-sv-black {
  background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
}

.if-colorpicker-sv-thumb {
  position: absolute;
  width: 0.8rem;
  height: 0.8rem;
  border-radius: 50%;
  border: 2px solid var(--if-surface);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.if-colorpicker-hue {
  position: relative;
  height: 0.75rem;
  margin-top: 0.6rem;
  border-radius: var(--if-radius-pill);
  cursor: pointer;
  touch-action: none;
  background: linear-gradient(to right, red, #ff0, lime, cyan, blue, magenta, red);
}

.if-colorpicker-hue-thumb {
  position: absolute;
  top: 50%;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background: var(--if-surface);
  border: 2px solid var(--if-surface);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.if-colorpicker-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.if-colorpicker-swatches-head {
  justify-content: space-between;
  margin-bottom: 0.4rem;
}

.if-colorpicker-preview {
  width: 1.5rem;
  height: 1.5rem;
  min-width: 1.5rem;
  border-radius: var(--if-radius-sm);
  border: 1px solid var(--if-border);
}

.if-colorpicker-swatches {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 0.35rem;
}

.if-colorpicker-swatch {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.1);
  padding: 0;
  cursor: pointer;
}

.if-colorpicker-swatch:focus-visible {
  outline: 2px solid var(--if-focus);
  outline-offset: 1px;
}

/* Panel de detalle: slide-over lateral, manipulación directa sin navegar a
   otra pantalla. Fase 10, ítem 58: ya NO se superpone con un fondo oscuro
   (`.if-panel-backdrop` existía antes, eliminada junto con el div que la
   usaba en detail-panel.js) — `.if-app-shell.if-app-shell-panel-open` le
   hace espacio empujando el contenido, así que el panel convive visible en
   vez de bloquear el resto de la pantalla. */
/* Fase 10, ítem 59: empieza debajo de la barra superior (antes arrancaba en
   top:0, quedando detrás de ella) y sin sombra — una sombra implica estar
   "flotando" por encima del resto; se pidió que se vea al mismo nivel que
   el contenido, así que se reemplaza por el mismo borde plano que ya usa
   el panel lateral izquierdo (`.if-sidebar`, `border-right`), aquí como
   `border-left`, para seguir marcando dónde empieza sin dar sensación de
   elevación. */
.if-panel {
  position: fixed;
  top: var(--if-topbar-height);
  right: 0;
  bottom: 0;
  width: var(--if-panel-width);
  max-width: 100vw;
  background: var(--if-surface);
  border-left: 1px solid var(--if-border);
  z-index: 1050;
  display: flex;
  flex-direction: column;
}

.if-panel-header {
  border-bottom: 1px solid var(--if-border);
  padding: 1rem 1.25rem;
}

/* Fase 10, ítem 105: botón "Eliminar ítem", a propósito muy sutil (solo un
   ícono, gris como el resto de los controles secundarios del panel) junto
   al botón de cerrar — en rojo solo al pasar el mouse/enfocar, para que no
   compita visualmente mientras no se está por usar. */
.if-panel-delete-btn {
  color: var(--if-icon-muted);
}

.if-panel-delete-btn:hover,
.if-panel-delete-btn:focus-visible {
  color: var(--if-danger);
}

.if-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 1.25rem;
}

/* Fase 10, ítem 113: pestañas "Detalles"/"Actividad" al principio del
   cuerpo del panel (debajo del nombre, que vive en .if-panel-header) —
   solo el espacio entre las pestañas y su contenido; el resto del look
   es el de Bootstrap (.nav-tabs/.tab-content) sin tocar. */
.if-panel-tabs {
  margin-bottom: 1rem;
}

.if-field-label {
  font-size: var(--if-font-md);
  font-weight: 600;
  color: var(--if-text-muted);
  margin-bottom: 0.25rem;
}

/* Fase 10, ítem 108: campo tipo "URL" — el <input> y los botones "Copiar"/
   "Abrir" en la misma línea, pedido explícito del usuario (panel de
   detalle, CellFields.render() con opts.withUrlActions). En la vista
   Tabla no se usa este contenedor (sin esos botones ahí), solo el
   <input> suelto. */
.if-url-field {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.if-url-field input {
  flex: 1 1 auto;
  min-width: 0;
}

.if-url-action-btn {
  flex-shrink: 0;
}

/* Fase 10, ítem 166: campo "período" — dos <input type="month"> (inicio/fin)
   lado a lado con un separador visual entre ambos, reemplaza los 3
   controles del modelo anterior (ítem 117: escala + año + período dentro
   del año). Mismo ancho para los dos lados (flex: 1 1 0), el separador no
   crece. */
.if-period-field {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.if-period-start,
.if-period-end {
  flex: 1 1 0;
  min-width: 0;
}

.if-period-separator {
  flex: 0 0 auto;
  color: var(--if-text-muted);
}

/* Fase 10, ítem 111: tipo de columna "json" — vista previa de solo lectura
   en la vista Tabla (un <span> truncado, sin control editable) y el
   editor completo en el panel de detalle (textarea + estadísticas). */
.if-json-preview {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--bs-font-monospace, monospace);
  font-size: 0.85em;
}

.if-json-textarea {
  width: 100%;
  resize: vertical;
  font-family: var(--bs-font-monospace, monospace);
  font-size: 0.85em;
}

.if-json-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

/* Fase 10, ítem 88: tabla de selección de tipo en "Nueva columna" (campo
   "radio-table" de AppPrompt.form(), antes un <select> con el atributo
   pegado al nombre del tipo, "Estado (agrupable por)") — una fila por
   tipo, su propia columna de atributos (hoy solo "Agrupable por", pensada
   para sumar más adelante sin cambiar la tabla). */
.if-type-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--if-font-md);
}

.if-type-table th {
  text-align: left;
  font-weight: 600;
  color: var(--if-text-muted);
  font-size: var(--if-font-xs);
  text-transform: uppercase;
  padding: 0.35rem 0.5rem;
  border-bottom: 1px solid var(--if-border);
}

.if-type-table td {
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--if-border);
}

.if-type-table tbody tr:hover {
  background: var(--if-bg);
}

.if-type-table tbody tr:last-child td {
  border-bottom: none;
}

.if-type-table-radio-cell {
  width: 2rem;
}

.if-type-table label {
  cursor: pointer;
  display: block;
}

/* Fase 10, ítem 90: separación entre insignias cuando un tipo tiene más de
   un atributo (ítem 89, p. ej. "Estado": Agrupable por + Color) — antes
   quedaban pegadas, sin espacio entre `<span>` consecutivos en el HTML. */
.if-type-attr-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.if-type-attr-badge {
  display: inline-block;
  background: var(--if-highlight-bg);
  color: var(--if-focus);
  font-size: var(--if-font-xs);
  padding: 0.1rem 0.5rem;
  border-radius: var(--if-radius-pill);
}

.if-save-indicator {
  font-size: var(--if-font-md);
  color: var(--if-text-muted);
  min-height: 1.2em;
}

.if-save-indicator.is-error {
  color: var(--if-danger);
}

.if-save-indicator.is-saved {
  color: var(--if-success);
}

.if-comment {
  border-bottom: 1px solid var(--if-border);
  padding: 0.75rem 0;
}

/* Vista Tabla (Fase 5): tabla real por grupo, con celdas editables inline.
   Fase 10, ítem 74: Bootstrap pone `margin-bottom: 1rem` a toda `.table` por
   defecto (pensado para separar una tabla suelta del contenido que sigue) —
   sumado al padding parejo de ".if-group-dropzone" (ítem 73), hacía que el
   margen de abajo del recuadro fuera tres veces más grande que los otros
   tres lados (24px vs 8px), aunque el CSS de ese recuadro ya era correcto.
   Anulado acá: la tabla no debe aportar ningún margen propio, solo el
   padding del recuadro que la envuelve. */
.if-table {
  background: var(--if-surface);
  margin-bottom: 0;
  border: 1px solid var(--if-border);
  border-collapse: separate;
  border-spacing: 0;
  /* Fase 10, ítem 42: el ítem 41 había sacado el color a una franja ::before
     flotante (ancho fijo de 5px) para que su radio no "mordiera" el borde
     de la tabla — pero una franja tan angosta fuerza, por la propia
     geometría de border-radius, un radio máximo efectivo de la mitad de su
     ancho (2.5px aprox.), casi imperceptible: se veía sin redondeo. Se
     volvió al enfoque del ejemplo aportado por el usuario — el radio va
     directo en la esquina superior/inferior IZQUIERDA de la tabla (no las
     4 esquinas, a diferencia del ítem 40), con un valor IGUAL al ancho del
     borde de color (5px, ver BORDER_LEFT_WIDTH_PX en board-table.js) en vez
     de uno bastante mayor (0.75rem/12px, ítem 40) — ese desajuste de
     tamaños era lo que producía el efecto "muesca": un radio mucho más
     grande que el borde alcanza a curvar también el lado donde el borde de
     color se encuentra con el fondo de la celda (el lado "interior"), no
     solo la punta exterior. Un radio igual al ancho del borde arma un
     cuarto de círculo limpio, que solo se nota como la punta redondeada del
     borde de color, sin comerse nada del fondo de la celda. Las esquinas
     derechas quedan rectas (no se declaran), ya que ahí no hay borde de
     color que redondear. */
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
  overflow: hidden;
  /* Fase 10, ítem 24: cada grupo es un <table> aparte, pero con el mismo
     número de columnas y el mismo ancho que el resto — sin table-layout:
     fixed, cada tabla ajustaba sus columnas según SU PROPIO contenido,
     desalineando los bordes entre un grupo y otro. Los anchos por columna
     van en el <th> (mismo valor para toda columna en todo grupo, ver
     board-table.js), salvo "Nombre" que queda flexible a propósito. */
  table-layout: fixed;
}

/* Fase 10, ítem 119: "ancho compacto" — anula, a propósito, las dos reglas
   que el ítem 24 (arriba) fijó para que las columnas midan igual en todo
   grupo del tablero: con `table-layout: auto` y `width: auto`, cada
   columna vuelve a medir según su propio contenido (lo que el usuario
   pidió — nada estirado "a la fuerza" para llenar el ancho disponible), a
   costa de que, si hay más de un grupo, sus columnas puedan no alinearse
   entre sí exactamente igual que en modo normal — el mismo problema que
   el ítem 24 corrigió, ahora aceptado a cambio del ancho compacto. */
.if-table-compact {
  width: auto;
  table-layout: auto;
}

.if-table th {
  font-size: var(--if-font-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--if-text-muted);
  /* Fase 10, ítem 146: blanco (`--if-surface`) a pedido explícito, en vez
     del gris claro `--if-bg` que usaba antes (visible en la franja del
     encabezado de la captura del usuario). */
  background: var(--if-surface);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}

.if-table td {
  vertical-align: middle;
}

/* Fase 10, ítem 160: mismo tamaño que el nombre de actividad (ítem 159,
   `--if-row-open-btn`) para los controles de edición de celda (`<input>`/
   `<select>` de CellFields.render(), siempre con las clases `-sm` de
   Bootstrap) — antes se quedaban con el tamaño por defecto de Bootstrap
   para `.form-control-sm`/`.form-select-sm`, más grande que el resto del
   texto de la fila. Cubre los tres controles de una columna "Período", el
   número de "Avance", y cualquier <input> de texto/fecha/número genérico;
   no toca los botones-chip de Estado/Desplegable/Persona
   (`.if-value-picker-btn`), que son un diseño aparte, no un `<input>`. */
.if-table .form-control,
.if-table .form-select {
  font-size: var(--if-font-sm);
}

/* Fase 10, ítem 65: celdas de texto plano (sin <select>/botón propio) en
   columnas de ancho fijo (p. ej. "Correo" junto a "Rol" en la tabla de
   Usuarios) — sin esto, un valor largo desborda visualmente sobre la
   columna siguiente en vez de respetar el ancho de su propia celda. */
.if-table-truncate {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Fase 10, ítem 66: columnas angostas de contenido centrado (avatar, color,
   acciones de la tabla de Usuarios) — por defecto una <td> alinea su
   contenido a la izquierda, dejando el espacio sobrante de una columna
   angosta todo del lado derecho en vez de parejo. */
.if-table-center {
  text-align: center;
}

/* La celda de avatar es más angosta que el resto (solo un círculo de 28px,
   ítem 26) — el padding horizontal estándar de Bootstrap (0.5rem) sobraba
   de ambos lados a la vez, dejando un hueco visible antes de "Nombre". */
.if-table-avatar-cell {
  padding-left: 0.25rem;
  padding-right: 0.25rem;
}

.if-row-open-btn {
  font-weight: 500;
  text-decoration: none;
  color: inherit;
  /* Fase 10, ítem 159: igualado al tamaño del nombre de actividad del
     Roadmap (`--if-font-sm`) — a pedido explícito, sin tocar peso ni
     color. Esta clase es compartida entre el nombre de ítem de la vista
     Tabla y el de la tarjeta de Kanban (board-table.js/board-kanban.js),
     así que un solo cambio cubre ambas vistas. */
  font-size: var(--if-font-sm);
}

.if-row-open-btn:hover,
.if-row-open-btn:focus-visible {
  color: var(--if-focus);
}

/* Insignia de iniciales de usuario (Fase 10, ítem 26) */
.if-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  min-width: 1.75rem;
  border-radius: 50%;
  font-size: var(--if-font-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}

/* "Sin asignar" en la celda Responsable (Fase 10, ítem 31): mismo tamaño
   que .if-avatar, pero neutro en vez de un color de usuario real.
   Fase 10, ítem 77: era 0.8rem, no el mismo tamaño que .if-avatar (0.7rem)
   que dice el propio comentario de arriba — ahora sí coinciden. */
.if-avatar-empty {
  background: var(--if-bg);
  border: 1px dashed var(--if-border);
  color: var(--if-text-muted);
  font-size: var(--if-font-xs);
}

/* Botón que envuelve el círculo de iniciales en la celda Responsable (ítem
   31): solo el círculo es visible/clicable, sin ningún <select> al lado. */
.if-avatar-btn {
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  border-radius: 50%;
  line-height: 0;
}

.if-avatar-btn:focus-visible {
  outline: 2px solid var(--if-focus);
  outline-offset: 2px;
}

/* Fase 10, ítem 174: ninguna de las dos (a diferencia de los `<input>`/
   `<select>` de Bootstrap, que ya se ven atenuados solos) es un `.btn` de
   Bootstrap, así que su estado `disabled` no tenía ningún estilo propio —
   un usuario "lector" las veía idénticas a como se ven cuando sí se puede
   editar. */
.if-avatar-btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

/* Popup de PersonaPicker (Fase 10, ítem 31): lista de participantes para
   elegir como responsable, mismo patrón de popup que ColorPicker (ítem 13). */
.if-persona-picker-popup {
  position: fixed;
  z-index: 1080;
  width: 14rem;
  max-height: 16rem;
  overflow-y: auto;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-lg);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.18);
  padding: 0.35rem;
}

.if-persona-picker-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  border: none;
  background: transparent;
  padding: 0.4rem 0.5rem;
  border-radius: var(--if-radius-sm);
  cursor: pointer;
  text-align: left;
}

.if-persona-picker-item:hover,
.if-persona-picker-item:focus-visible {
  background: var(--if-bg);
}

.if-persona-picker-item.is-selected {
  background: var(--if-highlight-bg);
  font-weight: 600;
}

/* Botón-chip de columnas "Estado"/"Desplegable" (Fase 10, ítem 35): luce
   igual que el <select> coloreado que reemplaza (ítem 22) cuando está
   cerrado, pero abre LabelValuePicker en vez de un <select> nativo — el
   navegador no muestra de forma confiable el color de cada <option>. */
.if-value-picker-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  border: 1px solid var(--if-border);
  background: var(--if-surface);
  border-radius: var(--if-radius-sm);
  padding: 0.25rem 0.5rem;
  cursor: pointer;
}

.if-value-picker-btn:hover {
  border-color: var(--if-focus);
}

.if-value-picker-btn:focus-visible {
  outline: 2px solid var(--if-focus);
  outline-offset: 1px;
}

/* Fase 10, ítem 174: ver el comentario junto a .if-avatar-btn:disabled. */
.if-value-picker-btn:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

.if-value-picker-caret {
  font-size: var(--if-font-xs);
  opacity: 0.6;
  flex-shrink: 0;
}

.if-value-chip {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--if-font-md);
}

.if-value-chip-empty {
  color: var(--if-text-muted);
}

/* Popup de LabelValuePicker (Fase 10, ítem 35): lista de valores posibles
   (etiquetas de Estado/Desplegable) para elegir, mismo patrón de popup que
   ColorPicker (ítem 13) y PersonaPicker (ítem 31). */
.if-label-value-picker-popup {
  position: fixed;
  z-index: 1080;
  width: 12rem;
  max-height: 16rem;
  overflow-y: auto;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-lg);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.18);
  padding: 0.35rem;
}

.if-label-value-picker-item {
  display: flex;
  align-items: center;
  width: 100%;
  border: none;
  background: transparent;
  padding: 0.35rem 0.4rem;
  border-radius: var(--if-radius-sm);
  cursor: pointer;
  text-align: left;
}

.if-label-value-picker-item:hover,
.if-label-value-picker-item:focus-visible {
  background: var(--if-bg);
}

.if-label-value-picker-item.is-selected {
  background: var(--if-highlight-bg);
}

.if-label-value-picker-item .if-value-chip {
  padding: 0.15rem 0.6rem;
  border-radius: var(--if-radius-pill);
  font-weight: 600;
  background: var(--if-bg);
}

.if-label-value-picker-item .if-value-chip-empty {
  border: 1px dashed var(--if-border);
}

/* Arrastrar una fila a otro grupo, o para reordenar dentro de uno (Fase 10,
   ítems 34 y 36) */
.if-drag-handle-cell {
  text-align: center;
}

.if-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.75rem;
  border-radius: var(--if-radius-xs);
  /* Fase 10, ítem 38: gris más suave que --if-text-muted en reposo — el
     ícono es solo un "agarre" secundario, no debería competir visualmente
     con el contenido real de la fila. Sigue cambiando a if-focus al pasar
     el mouse, para que se note que es interactivo. */
  color: var(--if-icon-muted);
  cursor: grab;
}

.if-drag-handle:hover {
  color: var(--if-focus);
  background: var(--if-bg);
}

.if-drag-handle:active {
  cursor: grabbing;
}

/* Ítem 36: mismo tratamiento que ".if-kanban-column"/".is-drag-over" (más
   abajo) para que arrastrar en Tabla y en Kanban se sienta igual — un borde
   de 1px reservado pero transparente por defecto (para no desplazar nada
   al aparecer) que pasa a gris + fondo suave mientras se arrastra algo
   encima. Sin fila "atenuada" en el origen: Kanban tampoco lo hace, solo
   resalta el destino.
   Fase 10, ítem 73: rediseñado para que el recuadro tenga SIEMPRE (en
   reposo y arrastrando, sin ninguna diferencia de layout entre ambos
   estados) el mismo padding de 0.5rem en los cuatro lados — a pedido
   explícito: ni la tabla debía moverse, ni el recuadro debía animarse al
   aparecer (los ítems 70/72 sí cambiaban margin/padding/margin-top al
   entrar en ".is-drag-over", con transición, lo opuesto a esto). Sin ese
   padding fijo siempre reservado, no hay forma de que los 4 márgenes sean
   iguales sin mover algo al aparecer el feedback. Como contrapartida, la
   tabla queda con 0.5rem de indentación permanente respecto al full-bleed
   del ítem 24/43 — a pedido también, para que ese padding tenga espacio
   y no quede pegado al panel lateral. */
.if-group-dropzone {
  border: 1px solid transparent;
  border-radius: var(--if-radius-xl);
  padding: 0.5rem;
  /* Separación entre un grupo y el anterior — antes era el margin-top de
     .if-group-heading-row (ver más abajo), movido acá para que ya no
     interfiera con el padding parejo de arriba. */
  margin-top: 1.25rem;
}

.if-group-dropzone.is-drag-over {
  border-color: var(--if-text-muted);
  background: var(--if-bg);
}

/* Línea de inserción (ítem 36): dónde quedaría la fila arrastrada si se
   suelta ahora, dentro de un grupo. En los <td> (no el <tr>) porque
   box-shadow en una fila de tabla no se pinta de forma confiable en todos
   los navegadores cuando hay border-collapse de por medio.
   Fase 10, ítem 68: 1px gris (antes 3px del color de foco, if-focus) — ya
   no compite con el borde morado del propio grupo (".if-group-dropzone",
   ítem 36) cuando ambos feedbacks están visibles a la vez. */
tr.if-drop-indicator-before > td {
  box-shadow: inset 0 1px 0 0 var(--if-text-muted);
}

tr.if-drop-indicator-after > td {
  box-shadow: inset 0 -1px 0 0 var(--if-text-muted);
}

/* Fase 10, ítem 124: misma línea de inserción, pero para listas en <div>
   (modal "Gestionar columnas") en vez de <table> — sin problema de
   border-collapse acá, así que el box-shadow va directo en la fila. */
.if-label-row.if-drop-indicator-before {
  box-shadow: inset 0 1px 0 0 var(--if-text-muted);
}

.if-label-row.if-drop-indicator-after {
  box-shadow: inset 0 -1px 0 0 var(--if-text-muted);
}

/* Fase 10, ítem 125: columna tipo "Avance" — número arriba, barra de
   progreso delgada debajo (misma celda compacta de la vista Tabla). */
.if-progress-cell {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

/* Fase 10, ítem 68: separación entre la línea y el borde del panel del
   grupo — la línea de arriba se arma por acumulación del box-shadow de
   CADA <td> de la fila (para que se vea continua entre columnas), así que
   no hay dónde insertar un margen sin cortarla; en vez de eso, se "apaga"
   con un degradado el primer tramo de la celda extrema (izquierda en la
   primera celda, derecha en la última) dejándola transparente ahí y gris
   recién después del hueco — el box-shadow de esa celda se anula para que
   no se sume por debajo del degradado. */
tr.if-drop-indicator-before > td:first-child,
tr.if-drop-indicator-after > td:first-child {
  box-shadow: none;
  background-image: linear-gradient(to right, transparent 0 0.75rem, var(--if-text-muted) 0.75rem 100%);
  background-repeat: no-repeat;
  background-size: 100% 1px;
}

tr.if-drop-indicator-before > td:first-child { background-position: top; }
tr.if-drop-indicator-after > td:first-child { background-position: bottom; }

tr.if-drop-indicator-before > td:last-child,
tr.if-drop-indicator-after > td:last-child {
  box-shadow: none;
  background-image: linear-gradient(to right, var(--if-text-muted) 0 calc(100% - 0.75rem), transparent calc(100% - 0.75rem) 100%);
  background-repeat: no-repeat;
  background-size: 100% 1px;
}

tr.if-drop-indicator-before > td:last-child { background-position: top; }
tr.if-drop-indicator-after > td:last-child { background-position: bottom; }

.if-filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  padding: 0.75rem;
}

.if-filter-field {
  min-width: 10rem;
}

/* Fase 10, ítem 104: "Borrar filtros" — a diferencia de los demás
   `.if-filter-field` (etiqueta arriba, control abajo), es solo un botón;
   `align-self: flex-end` lo baja a la altura de los `<input>`/`<select>`
   de las demás, en vez de quedar pegado arriba del todo. */
.if-filter-clear-field {
  min-width: auto;
  align-self: flex-end;
}

/* Vista Kanban (Fase 6): columnas por estado, tarjetas arrastrables */
/* Fase 10, ítem 56: antes sin `width`/`min-height` propios, el contenedor
   se achicaba a su propio contenido (ancho = suma de columnas, alto = la
   columna más alta) — la barra de scroll horizontal quedaba pegada
   directamente bajo las tarjetas, sin llegar al borde derecho del área de
   contenido, con un hueco vacío enorme debajo sin usar. `width: 100%` hace
   que el scroll llegue hasta el borde derecho igual que el resto del
   contenido (toolbar, título); `min-height` estira el contenedor hacia
   abajo — como las columnas (`.if-kanban-column`) ya tenían `align-items`
   por defecto (stretch) y su propio `max-height: 70vh`, crecen con el
   contenedor hasta ese tope en vez de quedar con un alto fijo chico, y el
   scroll termina mucho más abajo, cerca del final del espacio disponible,
   en vez de pegado a las tarjetas. Valor aproximado (barra superior +
   encabezado del tablero + barra de herramientas) — ajustar si no calza
   exacto al verlo en el navegador. */
.if-kanban-board {
  display: flex;
  gap: 0.75rem;
  width: 100%;
  min-height: calc(100vh - 230px);
  overflow-x: auto;
  padding-bottom: 0.5rem;
}

.if-kanban-column {
  flex: 0 0 16rem;
  width: 16rem;
  background: var(--if-surface);
  /* Fase 10, ítem 147: 1px a pedido explícito (antes 2px). */
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  /* Fase 10, ítem 96: sin esto, el fondo de color del encabezado
     (`.if-kanban-column-header`, un rectángulo liso) sobresalía por encima
     de las esquinas redondeadas del borde — mismo efecto "muesca" ya
     corregido para `.if-table` (ítem 42). La primera columna ("Sin estado")
     no mostraba el problema solo porque no tiene color de fondo (bucket.color
     es null), nada que recortar. */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  max-height: 70vh;
  transition: border-color 0.1s ease, background-color 0.1s ease;
}

.if-kanban-column.is-drag-over {
  border-color: var(--if-focus);
  background: var(--if-highlight-bg);
}

.if-kanban-column-header {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  /* Fase 10, ítem 159: igualado al tamaño del título de grupo del Roadmap
     (`--if-font-base`, antes `--if-font-md`) — a pedido explícito, sin
     tocar peso ni color. */
  font-size: var(--if-font-base);
  font-weight: 600;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--if-border);
}

/* Fase 10, ítem 97: el contador se movió junto al nombre (antes al extremo
   derecho) para liberar ese extremo para el menú "⋮" del grupo — el título
   necesita poder truncarse (nombres largos de grupo/etiqueta) para que el
   contador y el menú nunca queden empujados fuera de la columna. */
.if-kanban-column-title {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.if-kanban-dropzone {
  flex: 1;
  overflow-y: auto;
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-height: 6rem;
}

.if-kanban-card {
  /* Fase 10, ítem 133: sin esto, con muchas tarjetas en una columna, los
     elementos flex (`.if-kanban-dropzone` es "display:flex; flex-direction:
     column") se ENCOGEN para caber en el espacio disponible en vez de
     desbordar y dejar que el contenedor haga scroll (flex-shrink:1 es el
     valor por defecto) — el usuario lo describió exacto: "las tarjetas
     comienzan a disminuir su altura... se aplastan tanto que ya deja de
     verse el texto". flex-shrink:0 las deja siempre a su alto natural,
     para que sea `overflow-y:auto` (ya presente en `.if-kanban-dropzone`)
     el que entre a jugar, no el aplastamiento.
  */
  flex-shrink: 0;
  background: var(--if-bg);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-sm);
  padding: 0.5rem 0.6rem;
  cursor: grab;
  /* Fase 10, ítem 127: "relative" ancla el avatar/la barra de avance de
     abajo a la tarjeta misma, no al tablero; "hidden" recorta la barra de
     avance (que va de borde a borde) a las esquinas redondeadas de la
     tarjeta, en vez de sobresalir en cuadrado por fuera de ellas. */
  position: relative;
  overflow: hidden;
}

.if-kanban-card:focus-within {
  border-color: var(--if-focus);
}

/* Fase 10, ítem 127: avatar de "Responsable" (si el tablero tiene una
   columna "persona") en la esquina inferior derecha de la tarjeta — mismo
   círculo que ya usa la celda "Responsable" de la vista Tabla
   (UserAvatar.render()), solo que superpuesto en vez de en su propia
   celda. Con espacio para la barra de avance (si también está presente,
   `.if-kanban-card-has-progress` le agrega padding abajo): el avatar no se
   mueve con ese padding (sigue "position: absolute" sobre la tarjeta
   entera), así que su propio margen inferior es el que lo separa de la
   barra, no el padding de la tarjeta. */
.if-kanban-card-avatar {
  position: absolute;
  right: 0.4rem;
  bottom: 0.4rem;
}

.if-kanban-card-has-progress {
  padding-bottom: 0.9rem;
}

/* Fase 10, ítem 161: espacio reservado a la derecha para el avatar
   (`.if-kanban-card-avatar`, ancho 1.75rem + 0.4rem de separación del
   borde = 2.15rem desde el borde de la caja de padding, el mismo punto
   donde arranca el círculo) — sin esto, un nombre largo que ajusta a dos
   líneas podía terminar su última línea justo debajo del avatar, con el
   texto tapado por el círculo en vez de quedar al lado. */
.if-kanban-card-has-avatar {
  padding-right: 2.3rem;
}

.if-kanban-card-has-progress .if-kanban-card-avatar {
  bottom: 0.55rem;
}

/* Barra de avance pegada al borde inferior de la tarjeta (de punta a
   punta, no solo del texto) — mismas clases `.progress`/`.progress-bar`
   de Bootstrap que ya usan la celda de la vista Tabla y el resumen del
   Dashboard (mismo color, sin CSS propio para eso), solo reposicionada. */
.if-kanban-card-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0.25rem;
  border-radius: 0;
}

/* Fase 10, ítem 128: etiqueta(s) de "estado"/"categoría" entre el nombre y
   la barra de avance — en flujo normal (no "absolute" como el avatar/la
   barra), empuja hacia abajo en vez de superponerse al nombre. */
.if-kanban-card-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 0.35rem;
}

.if-kanban-card-badge {
  font-size: var(--if-font-xs);
  font-weight: 500;
}

/* Vista Roadmap (Fase 10, ítem 116, v1): carta Gantt — columna de
   etiquetas fija a la izquierda (`position: sticky`) y la grilla de
   tiempo scrolleando horizontalmente al lado, dentro de un único
   contenedor (`.if-roadmap-scroll`) para que el scroll vertical mueva
   ambas partes juntas sin sincronizar nada a mano. */
.if-roadmap-scroll {
  overflow-x: auto;
  padding-bottom: 0.5rem;
}

.if-roadmap-grid {
  display: grid;
  align-items: stretch;
  /* Fase 10, ítem 148: marco exterior a pedido explícito, mismo ancho/color
     que las líneas interiores de separación (`var(--if-border)`, 1px) que
     ya usan `.if-roadmap-unit-header`/`.if-roadmap-item-label`/etc. */
  border: 1px solid var(--if-border);
  /* Fase 10, ítem 149: sin esto, un contenedor de grid en bloque se estira
     al 100% de `.if-roadmap-scroll` (su ancho "auto" normal) en vez de
     ajustarse a la suma real de sus columnas (`grid-template-columns`,
     todas con px fijos) — cuando el contenido entra sin necesitar scroll,
     eso dejaba una franja en blanco a la derecha de la última columna de
     período, con el borde del ítem 148 dibujado al final de esa franja en
     vez de pegado al borde real de la última columna (el bug reportado).
     `max-content` fuerza al contenedor a medir exactamente lo que ocupan
     sus columnas, igual en el caso con scroll (contenido más ancho que el
     viewport) que sin él. */
  width: max-content;
}

.if-roadmap-corner {
  position: sticky;
  left: 0;
  z-index: 2;
  /* Fase 10, ítem 148: blanco a pedido explícito (antes `--if-bg`, el gris
     claro de fondo de toda la app). */
  background: var(--if-surface);
  border-bottom: 1px solid var(--if-border);
}

.if-roadmap-unit-header {
  padding: 0.4rem 0.3rem;
  font-size: var(--if-font-xs);
  font-weight: 600;
  color: var(--if-text-muted);
  text-align: center;
  /* Fase 10, ítem 148: blanco explícito a pedido del usuario — antes no
     declaraba fondo propio y dejaba ver el gris de la página detrás. */
  background: var(--if-surface);
  border-bottom: 1px solid var(--if-border);
  border-left: 1px solid var(--if-border);
  white-space: nowrap;
}

.if-roadmap-group-heading {
  /* Fase 10, ítem 144: igual truco que `.if-roadmap-item-label` — el área
     de grid de este elemento ya arranca en x:0 (`grid-column:1 / -1`), así
     que `position:sticky;left:0` mantiene el nombre del grupo a la vista
     sin recortar el fondo coloreado del resto de la fila (que sigue
     ocupando todo el ancho real, por si algún día se agrega contenido ahí). */
  position: sticky;
  left: 0;
  z-index: 1;
  padding: 0.5rem 0.5rem 0.3rem;
  font-weight: 600;
  background: var(--if-surface);
  /* Fase 10, ítem 149: mismo borde sutil que separa el resto de las filas
     (header de columnas, filas de ítem) — faltaba acá, dejando un salto sin
     línea entre el título de grupo y la primera fila de ese grupo. */
  border-bottom: 1px solid var(--if-border);
}

.if-roadmap-item-label {
  /* Fase 10, ítem 140: columna fija explícita — sin esto, el grid de CSS
     auto-ubica cada par etiqueta+track en la siguiente celda libre en vez
     de una fila propia, y pares de ítems distintos terminaban compartiendo
     la misma fila visual (el bug reportado de "etiquetas repetidas"/barras
     amontonadas en una sola línea). */
  grid-column: 1;
  position: sticky;
  left: 0;
  z-index: 1;
  /* Fase 10, ítem 148: blanco a pedido explícito (antes `--if-bg`). */
  background: var(--if-surface);
  /* Fase 10, ítem 149: padding horizontal mayor a pedido explícito — el
     `<button class="btn btn-link p-0">` de board-roadmap.js tenía `p-0`
     (Bootstrap, `padding:0 !important`), que anulaba por completo este
     `padding`; se quitó `p-0` del botón para que este valor rija de verdad
     (antes el texto quedaba pegado al borde izquierdo del todo). */
  padding: 0.4rem 0.75rem;
  /* Fase 10, ítem 145: más chico que el título de grupo (que se queda en
     --if-font-base) para que la jerarquía grupo → actividad se note de un
     vistazo, no solo por el peso de fuente del título. */
  font-size: var(--if-font-sm);
  border-bottom: 1px solid var(--if-border);
  text-align: left;
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.if-roadmap-item-label-blank {
  cursor: default;
}

.if-roadmap-track {
  /* Fase 10, ítem 140: ver el comentario en `.if-roadmap-item-label` —
     ocupa el resto de la fila (todas las columnas de unidades de tiempo)
     en vez de una sola celda, tanto en modo apilado (una fila por ítem)
     como aplanado (una fila por grupo, con varias barras superpuestas). */
  grid-column: 2 / -1;
  position: relative;
  min-height: 2.25rem;
  border-bottom: 1px solid var(--if-border);
  background-image: repeating-linear-gradient(
    to right,
    var(--if-border) 0,
    var(--if-border) 1px,
    transparent 1px,
    transparent 100%
  );
  background-repeat: repeat-x;
}

.if-roadmap-bar {
  position: absolute;
  top: 0.35rem;
  height: 1.55rem;
  border: none;
  border-radius: var(--if-radius-sm);
  font-size: var(--if-font-xs);
  padding: 0 0.4rem;
  display: flex;
  align-items: center;
  overflow: hidden;
  cursor: grab;
  /* Fase 10, ítem 153: sin esto, arrastrar una barra en un celular/tablet
     también intenta scrollear `.if-roadmap-scroll` al mismo tiempo (el
     gesto de arrastre horizontal es ambiguo para el navegador si no se le
     dice explícitamente que esta zona no es para gestos de scroll). */
  touch-action: none;
}

.if-roadmap-bar span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Fase 10, ítem 169 (adorno agregado en el ítem 170): manijas de
   redimensión en los bordes de una barra. `touch-action:none` por el mismo
   motivo que la barra misma (ítem 153): sin esto, arrastrar la manija en
   un celular/tablet compite con el scroll horizontal de la grilla. */
.if-roadmap-resize-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 8px;
  cursor: ew-resize;
  touch-action: none;
  flex-shrink: 0;
}

.if-roadmap-resize-handle[data-resize-edge="start"] {
  left: 0;
}

.if-roadmap-resize-handle[data-resize-edge="end"] {
  right: 0;
}

/* Fase 10, ítem 170: a pedido del usuario (imagen de referencia), un
   pequeño trazo vertical siempre visible en cada manija — sin esto, el
   ítem 169 las dejaba invisibles en reposo (solo aparecían al pasar el
   mouse por la barra), una afordancia demasiado sutil para notar que el
   borde se puede arrastrar. `background: currentColor` en vez de un blanco
   fijo: la manija hereda el mismo `color` de contraste que ya calcula
   `colorStyle()` para el texto del nombre (ítem 116/141, vía
   `ColorPicker.readableTextColor()`), así que el trazo se ve bien tanto
   sobre un color de grupo oscuro (trazo claro) como uno claro (trazo
   oscuro) sin duplicar esa fórmula de contraste acá. */
.if-roadmap-resize-handle::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 3px;
  height: 55%;
  border-radius: 2px;
  background: currentColor;
  opacity: 0.55;
}

.if-roadmap-bar:hover .if-roadmap-resize-handle,
.if-roadmap-bar:focus-visible .if-roadmap-resize-handle {
  background: rgba(255, 255, 255, 0.2);
}

.if-roadmap-bar:hover .if-roadmap-resize-handle::before,
.if-roadmap-bar:focus-visible .if-roadmap-resize-handle::before {
  opacity: 0.9;
}

.if-roadmap-bar:hover,
.if-roadmap-bar:focus-visible {
  outline: 2px solid var(--if-focus);
  outline-offset: 1px;
}

.if-roadmap-bar:active {
  cursor: grabbing;
}

/* Fase 10, ítem 153: chip "Sin fecha" — mismo tamaño/posición que una
   barra real (misma fila), pero con estilo neutro de "placeholder
   arrastrable" en vez de un color de grupo, ya que todavía no tiene ningún
   período asignado. */
.if-roadmap-unassigned-chip {
  position: absolute;
  left: 0;
  top: 0.35rem;
  height: 1.55rem;
  padding: 0 0.5rem;
  display: flex;
  align-items: center;
  font-size: var(--if-font-xs);
  color: var(--if-text-muted);
  background: var(--if-surface);
  border: 1px dashed var(--if-border);
  border-radius: var(--if-radius-sm);
  cursor: grab;
  touch-action: none;
  /* Fase 10, ítem 154: al arrastrarlo se redimensiona para previsualizar el
     ancho real de la barra que resultará (puede quedar angosto si la
     unidad de su escala es corta frente al resto de la línea de tiempo) —
     mismo recorte que ya usa `.if-roadmap-bar` para su texto. */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.if-roadmap-unassigned-chip:hover,
.if-roadmap-unassigned-chip:focus-visible {
  border-color: var(--if-focus);
  color: var(--if-text);
  outline: none;
}

.if-roadmap-unassigned-chip:active {
  cursor: grabbing;
}

/* Fase 10, ítem 153: feedback visual mientras se arrastra (barra o chip) —
   despegada ligeramente del resto con una sombra, por encima de cualquier
   otra barra con la que se pueda cruzar en el camino. */
.if-roadmap-bar.is-dragging,
.if-roadmap-unassigned-chip.is-dragging {
  opacity: 0.85;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  z-index: 2;
}

/* Fase 10, ítem 134: barra flotante de acciones en bloque (vista Tabla) —
   fija al viewport, centrada abajo, por encima del resto del contenido
   (incluida la tabla scrolleada) pero por debajo de un modal (si se abre
   "Mover a grupo" encima, ver z-index de Bootstrap). */
.if-bulk-actions-bar {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  z-index: 1030;
  display: flex;
  align-items: center;
  gap: 1rem;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.18);
  padding: 0.6rem 0.75rem 0.6rem 1rem;
}

.if-kanban-empty {
  margin: 0;
  text-align: center;
}

.if-skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--if-surface);
  padding: 0.5rem 1rem;
  z-index: 2000;
}

.if-skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* Búsqueda global (Fase 6b) */
.if-search {
  width: 14rem;
}

.if-search-results {
  position: absolute;
  top: calc(100% + 0.25rem);
  right: 0;
  width: 20rem;
  max-width: 90vw;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
  z-index: 1030;
}

.if-search-result {
  background: none;
  border: none;
  border-bottom: 1px solid var(--if-border);
  padding: 0.5rem 0.75rem;
}

.if-search-result:last-child {
  border-bottom: none;
}

.if-search-result:hover,
.if-search-result:focus-visible {
  background: var(--if-bg);
}

/* Dashboard por tablero (Fase 8) */
.if-dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
}

.if-dashboard-card {
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  padding: 1rem;
}

.if-dashboard-card-wide {
  grid-column: 1 / -1;
}

/* Chart.js con maintainAspectRatio:false necesita un contenedor con altura
   CSS fija (no solo el atributo height del <canvas>) — si no, el canvas y su
   contenedor compiten por tamaño y crecen sin parar. */
.if-chart-wrap {
  position: relative;
  height: 220px;
}

.if-chart-wrap-wide {
  height: 260px;
}

/* Panel de notificaciones (Fase 10, ítem 7) */
.if-notifications-panel {
  position: absolute;
  top: calc(100% + 0.25rem);
  right: 0;
  width: 22rem;
  max-width: 90vw;
  max-height: 70vh;
  overflow-y: auto;
  background: var(--if-surface);
  border: 1px solid var(--if-border);
  border-radius: var(--if-radius-md);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
  z-index: 1030;
}

/* Fase 10, ítem 163: `.if-notification` pasó de ser el único elemento
   clicable (un <button> entero) a un contenedor con dos partes — el
   cuerpo (`.if-notification-body`, clicable solo en el formato genérico;
   en el enriquecido de asignación son sus hijos los que lo son) y la fila
   de acciones (`.if-notification-actions`, siempre presente). */
.if-notification {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.4rem;
  border-bottom: 1px solid var(--if-border);
  padding: 0.5rem 0.5rem 0.5rem 0.75rem;
}

.if-notification:last-child {
  border-bottom: none;
}

.if-notification.is-unread {
  background: var(--if-highlight-bg);
}

.if-notification-body {
  flex: 1 1 auto;
  min-width: 0;
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 0;
}

button.if-notification-body:hover,
button.if-notification-body:focus-visible {
  background: var(--if-bg);
}

/* Fase 10, ítem 163: fecha/hora sola en su propia línea — mismo tamaño que
   el resto del texto de la notificación (`.if-notification-line`), a
   pedido explícito ("mismo tamaño de fuente en todas las líneas"); solo el
   color la distingue como metadato. */
.if-notification-datetime {
  font-size: var(--if-font-sm);
  color: var(--if-text-muted);
}

.if-notification-indent {
  margin-top: 0.15rem;
  padding-left: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

.if-notification-line {
  font-size: var(--if-font-sm);
  font-weight: 400;
}

/* Fase 10, ítem 163: "como los links del menú lateral" — mismo criterio
   exacto que `.if-sidebar-link` (`color: var(--if-text)`, sin subrayado,
   fondo sutil al pasar el mouse/enfocar), reemplaza el primer intento del
   ítem 162 (negrita + azul subrayado de Bootstrap `.btn-link`, lo que el
   usuario pidió explícitamente sacar). */
.if-notification-link {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 0.15rem 0.35rem;
  margin: 0 -0.35rem;
  border-radius: var(--if-radius-sm);
  color: var(--if-text);
  text-decoration: none;
}

.if-notification-link:hover,
.if-notification-link:focus-visible {
  background: var(--if-bg);
}

/* Fase 10, ítem 163: tres botones (leída/no leída/eliminar) — mismo gris
   apagado que `.if-drag-handle` (ítem 38) para no competir visualmente con
   el contenido de la notificación, con color de foco al pasar el mouse. */
.if-notification-actions {
  flex-shrink: 0;
  display: flex;
  gap: 0.15rem;
}

.if-notification-action-btn {
  background: none;
  border: none;
  padding: 0.25rem;
  border-radius: var(--if-radius-sm);
  color: var(--if-icon-muted);
  font-size: var(--if-font-sm);
  line-height: 1;
}

.if-notification-action-btn:hover,
.if-notification-action-btn:focus-visible {
  background: var(--if-bg);
  color: var(--if-focus);
}
