/* ============================================================
   ForceLab — Design System v3
   Inspired by Notion · Linear · Vercel · Stripe
   Palette: Zinc neutrals + Blue-600 accent
   ============================================================ */

/* ── Custom Properties ─────────────────────────────────────── */
:root {
  /* Brand accent — clean Stripe/Vercel blue */
  --accent:        #2563eb;
  --accent-dark:   #1d4ed8;
  --accent-light:  #eff6ff;
  --accent-muted:  #dbeafe;
  --accent-glow:   rgba(37,99,235,.1);

  /* Status */
  --red:           #ef4444;
  --red-light:     #fef2f2;
  --amber:         #f59e0b;
  --amber-light:   #fffbeb;
  --green:         #10b981;
  --green-light:   #f0fdf4;
  --blue:          #2563eb;
  --blue-light:    #eff6ff;

  /* Sidebar — near-black Vercel-style */
  --sb-bg:          #09090b;
  --sb-text:        #71717a;
  --sb-text-act:    #fafafa;
  --sb-hover:       rgba(255,255,255,.04);
  --sb-active:      rgba(37,99,235,.14);
  --sb-border:      rgba(255,255,255,.06);
  --sb-w:           220px;
  --sb-w-collapsed: 56px;

  /* Content — Zinc palette */
  --bg:            #fafafa;
  --bg-2:          #ffffff;
  --bg-alt:        #f4f4f5;
  --bg-hover:      #f4f4f5;
  --ink:           #18181b;
  --ink-soft:      #3f3f46;
  --ink-mute:      #71717a;
  --ink-ghost:     #a1a1aa;
  --line:          #e4e4e7;
  --line-strong:   #d4d4d8;
  --line-faint:    #f4f4f5;

  /* Layout */
  --topbar-h:      52px;
  --r-sm:          4px;
  --r-md:          6px;
  --r-lg:          10px;
  --shadow-sm:     0 1px 2px rgba(0,0,0,.05);
  --shadow-md:     0 4px 12px rgba(0,0,0,.06), 0 1px 3px rgba(0,0,0,.04);
  --shadow-lg:     0 12px 32px rgba(0,0,0,.08), 0 3px 8px rgba(0,0,0,.04);
  --shadow-card:   0 0 0 1px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);

  /* Typography — Inter only */
  --ff:            'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --ff-head:       'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --ff-body:       'Inter', -apple-system, BlinkMacSystemFont, sans-serif;

  /* Type scale */
  --text-2xs:  0.6875rem;
  --text-xs:   0.75rem;
  --text-sm:   0.8125rem;
  --text-base: 0.875rem;
  --text-md:   0.9375rem;
  --text-lg:   1.0625rem;
  --text-xl:   1.25rem;
  --text-2xl:  1.5rem;

  /* Legacy aliases */
  --radius:     var(--r-md);
  --text-1:     var(--ink);
  --text-muted: var(--ink-mute);
}

/* ── Reset ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; scroll-behavior: smooth; }
body {
  font-family: var(--ff);
  font-size: var(--text-base);
  background: var(--bg);
  color: var(--ink);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
button { cursor: pointer; border: none; background: none; font-family: inherit; font-size: inherit; }
input, textarea, select { font-family: inherit; font-size: inherit; }
a { text-decoration: none; color: inherit; }
ul, ol { list-style: none; }
img { max-width: 100%; display: block; }

/* ── Login Screen ───────────────────────────────────────────── */
.login-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #09090b;
  position: relative;
  overflow: hidden;
}
.login-bg { position: absolute; inset: 0; pointer-events: none; }
.login-blob { position: absolute; border-radius: 50%; filter: blur(100px); opacity: .18; }
.login-blob.b1 { width: 560px; height: 560px; background: radial-gradient(circle, #2563eb, transparent 70%); top: -180px; left: -120px; }
.login-blob.b2 { width: 400px; height: 400px; background: radial-gradient(circle, #4f46e5, transparent 70%); bottom: -100px; right: -80px; }
.login-blob.b3 { width: 300px; height: 300px; background: radial-gradient(circle, #0891b2, transparent 70%); top: 45%; left: 55%; transform: translate(-50%,-50%); }

.login-card {
  position: relative;
  z-index: 1;
  background: #0e0e10;
  border: 1px solid #27272a;
  border-radius: 12px;
  padding: 2rem;
  width: 100%;
  max-width: 380px;
  box-shadow: 0 32px 80px rgba(0,0,0,.6);
}
.login-logo { display: flex; align-items: center; gap: 0.625rem; margin-bottom: 1.75rem; }
.login-logo-mark {
  width: 36px; height: 36px;
  background: var(--accent);
  border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 1rem; color: #fff;
  flex-shrink: 0; letter-spacing: -.02em;
}
.login-logo-name { display: block; font-weight: 700; font-size: 0.9rem; color: #fff; letter-spacing: -.01em; }
.login-logo-sub  { display: block; font-size: 0.7rem; color: #52525b; }
.login-title { font-size: var(--text-xl); font-weight: 700; color: #fff; letter-spacing: -.02em; }
.login-sub   { font-size: var(--text-sm); color: #71717a; margin-top: 0.2rem; margin-bottom: 1.25rem; }
.login-form { display: flex; flex-direction: column; gap: 0.75rem; }
.login-form .field-group label { color: #71717a; font-size: var(--text-xs); font-weight: 500; }
.login-form .field-group input {
  background: #18181b;
  border: 1px solid #27272a;
  color: #fff;
  border-radius: var(--r-sm);
}
.login-form .field-group input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
  background: #1c1c1f;
}
.login-form .field-group input::placeholder { color: #3f3f46; }
.login-form .btn-primary { background: var(--accent); border-color: var(--accent); margin-top: 0.25rem; }

/* ── App Shell ──────────────────────────────────────────────── */
.app-shell { display: flex; min-height: 100vh; }

/* ── Sidebar ────────────────────────────────────────────────── */
.sidebar {
  width: var(--sb-w);
  min-height: 100vh;
  min-height: 100dvh; /* R47-3: min-height:100vh por sí solo actuaría como piso y anularía el height:100dvh de abajo */
  background: var(--sb-bg);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  /* R47-3: 100vh usa el viewport "grande" (como si la barra de direcciones
     estuviera oculta) — en un celular real, con la barra visible, el
     sidebar (position:fixed en móvil) quedaba más alto que el área
     visible, empujando el footer (Cambiar contraseña/Cerrar sesión) fuera
     de pantalla. Como el propio sidebar NO desbordaba su caja (scrollHeight
     == clientHeight), no había nada que hacer scroll para revelarlo — el
     -webkit-overflow-scrolling:touch de más abajo no podía arreglar esto
     porque el problema nunca fue de scroll. Mismo patrón ya usado en
     .modal (ver max-height 90vh/90dvh) — dvh sí descuenta la barra de
     direcciones. Fallback a vh si el navegador no soporta dvh. */
  height: 100dvh;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  transition: width .2s ease;
  z-index: 100;
}
.sidebar::-webkit-scrollbar { width: 0; }
.sidebar.collapsed { width: var(--sb-w-collapsed); }
.sidebar.collapsed .sidebar-logo-text,
.sidebar.collapsed .user-info,
.sidebar.collapsed .nav-section-label,
.sidebar.collapsed .nav-item span { display: none; }
.sidebar.collapsed .nav-item { justify-content: center; padding: 0.5rem; }
.sidebar.collapsed .sidebar-header { justify-content: center; padding: 0.875rem 0; }
.sidebar.collapsed .sidebar-user { justify-content: center; padding: 0.625rem 0; }

.sidebar-header {
  padding: 0.875rem 0.875rem 0.625rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--sb-border);
}
.sidebar-logo { display: flex; align-items: center; gap: 0.5rem; }
.sidebar-logo-mark {
  width: 28px; height: 28px;
  background: var(--accent);
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 0.825rem; color: #fff;
  flex-shrink: 0; letter-spacing: -.02em;
}
.sidebar-logo-name { display: block; font-weight: 700; font-size: 0.825rem; color: #fafafa; letter-spacing: -.01em; }
.sidebar-logo-sub  { display: block; font-size: 0.6rem; color: #52525b; margin-top: 0.05rem; }
.sidebar-toggle {
  color: #3f3f46;
  padding: 0.2rem;
  border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  transition: color .15s, background .15s;
}
.sidebar-toggle:hover { color: #a1a1aa; background: rgba(255,255,255,.04); }
.sidebar.collapsed .sidebar-toggle svg { transform: rotate(180deg); }

.sidebar-user {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.625rem 0.875rem;
  border-bottom: 1px solid var(--sb-border);
}
.user-avatar {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.625rem; font-weight: 700; color: #fff;
  flex-shrink: 0;
}
.user-name { display: block; font-size: var(--text-xs); font-weight: 600; color: #d4d4d8; }
.user-role-badge {
  display: inline-block;
  font-size: 0.6rem;
  color: #60a5fa;
  background: rgba(37,99,235,.12);
  padding: 0.08rem 0.35rem;
  border-radius: 20px;
  margin-top: 0.1rem;
  font-weight: 500;
}

.sidebar-nav { flex: 1; padding: 0.25rem 0; }
.nav-section { padding: 0.125rem 0; }
.nav-section-label {
  display: block;
  font-size: 0.6rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: #3f3f46;
  padding: 0.5rem 0.875rem 0.175rem;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.75rem;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--sb-text);
  transition: color .15s, background .15s;
  position: relative;
}
.nav-item:hover { color: #d4d4d8; background: var(--sb-hover); }
.nav-item.active { color: #fafafa; background: var(--sb-active); }
.nav-item.active::before {
  content: '';
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%);
  width: 2px; height: 60%;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}
.nav-icon { width: 15px; height: 15px; flex-shrink: 0; opacity: .6; }
.nav-item.active .nav-icon,
.nav-item:hover .nav-icon { opacity: 1; }
.nav-item--logout { width: 100%; color: var(--sb-text); }
.nav-item--logout:hover { color: #f87171; background: rgba(239,68,68,.06); }
.sidebar-footer { border-top: 1px solid var(--sb-border); padding: 0.25rem 0; }
.sidebar-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 150; backdrop-filter: blur(2px); }
.sidebar-overlay.visible { display: block; }

/* ── Topbar ─────────────────────────────────────────────────── */
.main-area { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: var(--topbar-h);
  background: var(--bg-2);
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1.25rem;
  position: sticky;
  top: 0;
  z-index: 90;
}
.topbar-menu-btn { display: none; flex-direction: column; gap: 3.5px; padding: 0.25rem; border-radius: var(--r-sm); }
.topbar-menu-btn:hover { background: var(--bg-alt); }
.topbar-menu-btn span { display: block; width: 16px; height: 1.5px; background: var(--ink-soft); border-radius: 1px; }
.topbar-breadcrumb { flex: 1; font-size: var(--text-sm); font-weight: 600; color: var(--ink-soft); }
.topbar-right { display: flex; align-items: center; gap: 0.625rem; }
.topbar-version { font-size: var(--text-2xs); color: var(--ink-ghost); }
.topbar-avatar {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.6rem; font-weight: 700; color: #fff;
}

/* ── Content Area ───────────────────────────────────────────── */
.content-area { flex: 1; padding: 1.5rem; max-width: 1440px; }

/* ── Page Header ────────────────────────────────────────────── */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.page-header-left { flex: 1; }
.page-title {
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.2;
  letter-spacing: -.02em;
}
.page-subtitle { font-size: var(--text-sm); color: var(--ink-mute); margin-top: 0.2rem; }

/* ── Cards ──────────────────────────────────────────────────── */
.card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 1.125rem 1.25rem;
  box-shadow: var(--shadow-card);
}
.card-title {
  font-size: var(--text-md);
  font-weight: 650;
  color: var(--ink);
  letter-spacing: -.01em;
  margin-bottom: 0.875rem;
}
.card-sub { font-size: var(--text-xs); color: var(--ink-mute); margin-top: 0.15rem; }
.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.875rem;
}
.session-card-header { flex-wrap: wrap; row-gap: .35rem; }
.session-card-header .session-collapse-toggle { min-width: 0; flex: 1 1 220px; }
.session-card-header .session-collapse-toggle > div { min-width: 0; }
.session-card-header .session-collapse-toggle p { white-space: normal; overflow-wrap: break-word; word-break: break-word; }

/* ── Stat Cards ─────────────────────────────────────────────── */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}
.stat-card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 0.875rem 1rem;
  box-shadow: var(--shadow-card);
}
.stat-label { font-size: var(--text-2xs); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-ghost); }
.stat-value { font-size: 1.75rem; font-weight: 800; color: var(--ink); margin: 0.2rem 0 0.15rem; line-height: 1; letter-spacing: -.03em; }
.stat-delta { font-size: var(--text-2xs); color: var(--ink-ghost); }
.stat-delta.up { color: var(--green); }
.stat-delta.down { color: var(--red); }

/* ── Grid Layouts ───────────────────────────────────────────── */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; }
.grid-main-aside { display: grid; grid-template-columns: 1fr 300px; gap: 1rem; align-items: start; }
.grid-main-aside.plan-detail-grid { grid-template-columns: minmax(0, 1fr) 280px; }

.plan-aside-toggle-mobile.btn { display: none; align-items: center; gap: .4rem; margin-bottom: .75rem; }
.plan-aside-toggle-mobile .plan-aside-toggle-chevron { transition: transform .2s ease; }
.plan-aside-toggle-mobile.is-open .plan-aside-toggle-chevron { transform: rotate(180deg); }
.plan-aside-sticky {
  display: flex; flex-direction: column; gap: .625rem;
  position: sticky;
  top: calc(var(--topbar-h) + 1rem);
  max-height: calc(100vh - var(--topbar-h) - 2rem);
  overflow-y: auto; padding-right: 2px;
}
.plan-aside-sticky .card { margin-bottom: 0 !important; }
.plan-aside-sticky::-webkit-scrollbar { width: 3px; }
.plan-aside-sticky::-webkit-scrollbar-thumb { background: var(--line); border-radius: 2px; }
.calc-controls-grid { display: grid; gap: .5rem; margin-bottom: .625rem; }

@media (max-width: 1024px) {
  .plan-aside-toggle-mobile.btn { display: inline-flex; }
  .plan-aside-sticky { position: static; max-height: none; overflow: visible; }
  #plan-aside-panel.plan-aside-collapsed { display: none; }
  /* R104 — el panel se despliega ARRIBA de las sesiones, no debajo.

     El botón "Planes y herramientas" vive sobre la rejilla, pero el panel
     es el SEGUNDO hijo: al apilarse en una columna caía después de todas
     las sesiones. Pulsabas el botón y en pantalla no pasaba nada visible;
     había que bajar por 6-8 sesiones para encontrar lo que acababas de
     abrir. El disparador y su contenido estaban a media pantalla de
     distancia.

     `order` solo reordena la pintura, no el DOM: el orden de lectura y el
     foco del teclado se mantienen, y en escritorio (dos columnas) no
     cambia nada. */
  .grid-main-aside.plan-detail-grid > #plan-aside-panel { order: -1; }
}

/* ── Forms & Inputs ─────────────────────────────────────────── */
.field-group { display: flex; flex-direction: column; gap: 0.25rem; }
.field-group label { font-size: var(--text-xs); font-weight: 500; color: var(--ink-soft); }
.field-group input,
.field-group select,
.field-group textarea,
.control-input {
  height: 34px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: 0 0.625rem;
  font-size: var(--text-sm);
  color: var(--ink);
  background: var(--bg-2);
  transition: border-color .15s, box-shadow .15s;
  width: 100%;
}
.field-group textarea { height: auto; min-height: 68px; padding: 0.5rem 0.625rem; line-height: 1.5; resize: vertical; }
.field-group select,
select.control-input { cursor: pointer; }

/* ══════════════════════════════════════════════════════════════════
   R156 — El zoom de iOS al tocar un campo
   ------------------------------------------------------------------
   Safari en iOS hace zoom SOLO cuando enfocas un campo cuya letra mide
   menos de 16px. Los nuestros usan --text-sm (13px), asi que zoomeaba
   cada vez -- y como el zoom no vuelve solo, la pantalla se quedaba
   desplazada.

   No se puede bloquear el zoom: `user-scalable=no` lo ignora iOS desde
   la version 10, a proposito, por accesibilidad. Y el "zoom con efecto
   liga" no existe como funcion del navegador; habria que interceptar
   gestos tactiles a mano y pelearse con el sistema.

   La solucion real es quitarle el motivo: a 16px iOS no zoomea. El
   pellizco para ampliar sigue funcionando igual, que es lo que si
   queriamos conservar.

   Se aplica por tipo de puntero, no por ancho: la condicion de verdad
   es "esto se toca con el dedo", no "la pantalla es estrecha". Una
   tablet de 1024px tambien zoomea.
   ══════════════════════════════════════════════════════════════════ */
@media (pointer: coarse) {
  .field-group input,
  .field-group select,
  .field-group textarea,
  .control-input,
  .runner-set input,
  input[type="text"], input[type="number"], input[type="date"],
  input[type="time"], input[type="tel"], input[type="email"],
  input[type="password"], input[type="search"], select, textarea {
    font-size: 16px;
  }
}
.field-group input:focus,
.field-group select:focus,
.field-group textarea:focus,
.control-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(37,99,235,.12);
}
.field-group input::placeholder,
.field-group textarea::placeholder,
.control-input::placeholder { color: var(--ink-ghost); }
/* .control-input: mismo lenguaje visual que .field-group input/select, pero
   usable directamente en controles compactos/inline (filtros, toolbars,
   inputs dentro de wizards) que no están envueltos en la estructura completa
   de .field-group (label + input apilados). El ancho/alto puntual de cada
   caso sigue sobreescribiéndose con estilos inline (mayor especificidad). */
.field-hint { font-size: var(--text-2xs); color: var(--ink-ghost); margin-top: 0.15rem; line-height: 1.4; }
.field-calc { font-size: var(--text-2xs); color: var(--accent); margin-top: 0.15rem; font-weight: 600; }
.field-err  { font-size: var(--text-2xs); color: var(--red); margin-top: .15rem; }
.form-row   { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
.form-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.75rem; }
.form-row-4 { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 0.75rem; }

/* R48: lista de checkboxes estilo Notion (p.ej. "Permisos sobre el Banco de
   Ejercicios" en Crear/Editar usuario) — antes eran <input type="checkbox">
   sueltos sin tamaño/alineación propios, se veían enormes y desproporcionados
   frente al resto de la UI (que nunca deja un control nativo sin estilizar). */
.perm-check-list { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.perm-check-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .55rem .75rem;
  cursor: pointer;
  border-bottom: 1px solid var(--line);
}
.perm-check-row:last-child { border-bottom: none; }
.perm-check-row:hover { background: var(--bg-hover); }
.perm-check-row input[type="checkbox"] { width: 16px; height: 16px; flex-shrink: 0; accent-color: var(--accent); margin: 0; }
.perm-check-row span { font-size: .85rem; color: var(--ink); }

/* ── Form Sections — Notion-style blocks ────────────────────── */
.form-section {
  border: none;
  border-top: 1px solid var(--line);
  border-radius: 0;
  overflow: visible;
  margin-bottom: 0;
  background: transparent;
}
.form-section:first-of-type { border-top: none; }
.form-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6rem 0;
  background: transparent;
  border-bottom: 1.5px solid var(--line);
  cursor: pointer;
  user-select: none;
}
.form-section-header:hover .form-section-title { color: var(--accent); }
.form-section-title {
  font-size: var(--text-sm);
  font-weight: 800;
  letter-spacing: .01em;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding-left: 0.6rem;
  border-left: 3px solid var(--accent);
  line-height: 1.25;
}
.form-section-badge {
  font-size: var(--text-2xs);
  background: var(--accent-muted);
  color: var(--accent);
  padding: 0.1rem 0.4rem;
  border-radius: 20px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}
.form-section-chevron { width: 14px; height: 14px; color: var(--ink-ghost); transition: transform .2s; flex-shrink: 0; }
.form-section.open .form-section-chevron { transform: rotate(180deg); }
.form-section-body { padding: 0 0 0.875rem; display: grid; gap: 0.75rem; }
.form-section-body.hidden { display: none; }

.form-section-group-header {
  margin: 1.5rem 0 0.5rem;
  padding: .5rem .75rem;
  border: none;
  border-radius: var(--r-sm, 8px);
  background: var(--accent);
  display: flex; align-items: center;
  box-shadow: 0 1px 3px rgba(0,0,0,.12);
}
.form-section-group-header span {
  font-size: var(--text-xs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: #fff;
}
.form-section-label {
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ink-ghost);
  padding-bottom: 0.25rem;
  border-bottom: 1px solid var(--line);
}

/* Pathology chips */
.pathol-chip {
  display: inline-flex; align-items: center;
  padding: .18rem .45rem;
  border: 1px solid var(--line-strong);
  border-radius: 20px;
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  background: var(--bg-2);
  color: var(--ink-soft);
  transition: background .12s, border-color .12s, color .12s;
  user-select: none;
}
.pathol-chip:hover { border-color: var(--accent); color: var(--accent); }
.pathol-chip.selected { background: var(--accent); border-color: var(--accent); color: #fff; }
.pathol-chip--sm { font-size: var(--text-2xs); padding: .14rem .35rem; }
.pathol-tag {
  display: inline-flex; align-items: center; gap: .2rem;
  padding: .15rem .4rem .15rem .45rem;
  border-radius: 20px;
  background: var(--accent); color: #fff;
  font-size: var(--text-xs); font-weight: 500;
}
.pathol-tag-remove { background: none; border: none; color: rgba(255,255,255,.7); cursor: pointer; font-size: 0.75rem; line-height: 1; padding: 0 .08rem; }
.pathol-tag-remove:hover { color: #fff; }
.member-selector { flex-wrap: wrap; }
.fhx-member-block .field-group { margin: 0; }
.fhx-member-block select { font-size: var(--text-xs); padding: 0 .35rem; height: 28px; }

/* ── Buttons ────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.35rem;
  padding: 0 0.875rem;
  /* R130: era `height`, y eso recortaba cualquier botón de dos líneas
     -- el popup "¿Qué pasó con la sesión?" pedía 39 px y se quedaba en
     34, con la descripción cortada por la mitad. Como el relleno vertical
     es cero, un botón de una línea sigue midiendo 34 px exactos. */
  min-height: 34px;
  border-radius: var(--r-sm);
  font-size: var(--text-sm);
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background .15s, border-color .15s, box-shadow .15s, opacity .15s, color .15s;
  line-height: 1;
  letter-spacing: -.01em;
}
.btn:disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }
.btn-primary   { background: var(--accent); color: #fff; border-color: var(--accent); }
.btn-primary:hover:not(:disabled) { background: var(--accent-dark); border-color: var(--accent-dark); }
.btn-secondary { background: var(--bg-2); color: var(--ink-soft); border-color: var(--line-strong); }
.btn-secondary:hover:not(:disabled) { background: var(--bg-alt); color: var(--ink); }
.btn-danger    { background: var(--red); color: #fff; border-color: var(--red); }
.btn-danger:hover:not(:disabled) { background: #dc2626; }
.btn-ghost     { background: transparent; color: var(--ink-mute); border-color: transparent; }
.btn-ghost:hover:not(:disabled) { background: var(--bg-alt); color: var(--ink-soft); }
.btn-outline   { background: transparent; color: var(--accent); border-color: var(--accent); }
.btn-outline:hover:not(:disabled) { background: var(--accent-light); }
.btn-outline-danger { background: transparent; color: var(--red); border-color: var(--red); }
.btn-outline-danger:hover:not(:disabled) { background: #fef2f2; }
.btn-wizard    { background: var(--bg-2); color: var(--ink-soft); border-color: var(--line-strong); }
.btn-wizard:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.btn-wizard.done { border-color: #10b981; color: #059669; }
.btn-full      { width: 100%; }
.btn-sm        { min-height: 28px; padding: 0 0.6rem; font-size: var(--text-xs); }
.btn-xs        { min-height: 24px; padding: 0 0.5rem; font-size: var(--text-2xs); font-weight: 600; border-radius: 3px; }
.btn-icon      { width: 32px; height: 32px; padding: 0; }
.btn-icon.btn-sm { width: 28px; height: 28px; }
.btn-spinner   { width: 13px; height: 13px; border: 1.5px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .7s linear infinite; }

/* ── Tables — Stripe density ────────────────────────────────── */
.table-wrap { overflow-x: auto; }

/* ══════════════════════════════════════════════════════════════════
   R149 — Reparto de columnas en la tabla de ejercicios de una sesion
   ------------------------------------------------------------------
   La tabla no definia NINGUN ancho: el navegador repartia solo, y con
   diez columnas mas 0.875rem de padding por lado (unos 280px solo en
   huecos) el resultado era el peor posible -- "Extension de triceps
   acostado con mancuernas" partido en CUATRO lineas mientras Carga y
   Comentarios, casi siempre vacias, se quedaban su parte entera. Y aun
   asi la tabla no cabia y salia barra horizontal.

   `table-layout: fixed` hace que los anchos de abajo se respeten tal
   cual y que la columna de Ejercicio se quede TODO el resto. Los datos
   numericos ocupan poco y son de ancho conocido; el nombre no.
   ══════════════════════════════════════════════════════════════════ */
/* R150: la tabla paso de 10 columnas a 8. Fuera "Series" (fusionada con
   Reps) y fuera "Comentarios" (la nota vive bajo el nombre). Con eso el
   nombre del ejercicio pasa de 126px a ~250px en el hueco real de 850px
   que queda entre la barra lateral y el panel de Planes. */
.ex-nota {
  display: block; margin-top: .18rem;
  font-size: .72rem; font-weight: 400; line-height: 1.35;
  color: var(--ink-mute);
}
/* El signo separa sin competir: "3 x 10" debe leerse como UNA cifra con su
   separador, no como tres simbolos del mismo peso. */
.ex-por { color: var(--ink-ghost); font-weight: 400; margin: 0 .18rem; }

@media (min-width: 900px) {
  .table-wrap .exercises-table { table-layout: fixed; width: 100%; }
  .table-wrap .exercises-table th,
  .table-wrap .exercises-table td {
    padding-left: .5rem; padding-right: .5rem;
    overflow-wrap: break-word;
  }
  .table-wrap .exercises-table th:first-child,
  .table-wrap .exercises-table td:first-child { padding-left: .75rem; }
  .table-wrap .exercises-table th:last-child,
  .table-wrap .exercises-table td:last-child  { padding-right: .75rem; }

  /* R152: 34px y no 26. En las filas de biserie el asa lleva ademas
     `border-left:3px` y un `padding:0 .4rem` en linea; con box-sizing
     border-box eso dejaba ~12px para el simbolo, que mide 16. Se salia
     unos pixeles y ESO era lo que seguia dibujando la barra horizontal
     aunque todo el contenido pareciera caber. */
  /* R153: los anchos pasan de pixeles a PORCENTAJE. En px no crecian: al
     colapsar la barra lateral sobraba medio monitor y "INTENSIDAD" seguia
     partiendose por la mitad ("INTENSI/DAD") porque su columna seguia
     midiendo 72px. En % la tabla reparte lo que haya.

     Solo quedan en px las dos columnas cuyo contenido NO escala: el asa
     (un simbolo) y las acciones (cuatro botones de tamano fijo). */
  .table-wrap .exercises-table th:nth-child(1) { width: 34px;  }  /* asa */
  .table-wrap .exercises-table td:nth-child(1) { padding-left: .3rem !important;
                                                 padding-right: .3rem !important; }
  /* R160: la columna "Bloque" (11%) se fue -- su etiqueta sale ahora UNA
     vez en la cabecera del tramo, no una por fila. Todas las demas corren
     una posicion, y ese 11% se lo queda el nombre del ejercicio, que era
     justo la columna que iba apretada. */
  /* 2 = Ejercicio: sin ancho, se queda el resto */
  .table-wrap .exercises-table th:nth-child(3) { width: 15%;   }  /* Series x Reps */
  .table-wrap .exercises-table th:nth-child(4) { width: 13%;   }  /* Carga */
  .table-wrap .exercises-table th:nth-child(5) { width: 10%;   }  /* Intensidad */
  .table-wrap .exercises-table th:nth-child(6) { width: 10%;   }  /* Descanso */
  /* 4 botones x 28px + separaciones + relleno = 136 justo. Se dan 144 para
     que no dependa de que el navegador redondee a la baja. */
  /* R166: 172 y no 144 -- entra el boton de menu como quinto control.
     Los 28 px salen del nombre, que con R160 habia pasado de ~310 a 441:
     sigue muy por encima de donde estaba. */
  /* R190: 112. Con R188 la fila dejo de tener cinco controles, pero la
     columna siguio reservando el ancho de los cinco -- ese era el hueco
     blanco al final de cada fila.

     MEDIDO en el navegador, no calculado a ojo: fuerza necesita 75 px
     (editar + menu) y cardio 104 (en vivo + editar + menu). Manda cardio,
     que es el caso ancho, y se redondea a 112 por el mismo motivo que
     antes: no depender de como redondee el navegador.

     Los 60 px liberados no se pierden -- con `table-layout: fixed` van a
     la unica columna sin ancho, que es el nombre del ejercicio. */
  .table-wrap .exercises-table th:nth-child(7) { width: 112px; }  /* acciones */

  /* Y pegados a la derecha. Sin esto, en las filas de fuerza (dos
     controles en una columna dimensionada para tres) quedarian 37 px de
     aire justo en el borde de la tabla, que es donde mas se nota. Asi el
     sobrante cae ANTES de los iconos y ambos tipos de fila alinean sus
     acciones en la misma vertical. */
  .table-wrap .exercises-table tr[data-ex-id] > td:last-child { text-align: right; }
  /* R154: por CLASE, no por posicion. Estaba escrito como nth-child(8),
     pero en las filas de cardio el colspan hace que la celda de acciones
     sea la QUINTA: alli los botones nunca se achicaron, seguian a 32px y
     se salian 10px. Ese era el desbordamiento que quedaba. */
  .table-wrap .exercises-table .btn-icon { width: 28px; height: 28px; margin-right: 1px !important; }

  /* R154: "Activacion" pedia 100px en una columna de 88 y se salia. Se
     encoge la etiqueta en vez de ensanchar la columna: ese ancho saldria
     del nombre del ejercicio, que es lo que estamos protegiendo. */
  .table-wrap .exercises-table .badge {
    font-size: .6rem; padding: .1rem .35rem; letter-spacing: 0;
  }

  /* R153: `.data-table th` lleva `white-space:nowrap` (cabeceras de una
     palabra) y `letter-spacing:.06em`. Aqui se deja partir, y se aprieta el
     espaciado: ese .06em anadia ~6px a "INTENSIDAD", justo lo que la hacia
     no caber. */
  .table-wrap .exercises-table th {
    white-space: normal; line-height: 1.25; letter-spacing: .02em;
  }
}

.data-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.data-table th {
  text-align: left;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-ghost);
  padding: 0.5rem 0.875rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  background: transparent;
}
.data-table th:first-child { padding-left: 1rem; }
.data-table th:last-child  { padding-right: 1rem; }
.data-table td {
  padding: 0.625rem 0.875rem;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line-faint);
  vertical-align: middle;
}
.data-table td:first-child { padding-left: 1rem; }
.data-table td:last-child  { padding-right: 1rem; }
.data-table tr:last-child td { border-bottom: none; }
.data-table tr:hover td { background: rgba(0,0,0,.015); }
.data-table .td-primary { font-weight: 600; color: var(--ink); }

/* ── Badges ─────────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 0.2rem;
  font-size: var(--text-2xs); font-weight: 600;
  padding: 0.2rem 0.475rem;
  border-radius: 20px; letter-spacing: .01em;
}
.badge-active    { background: #f0fdf4; color: #15803d; }
.badge-inactive  { background: var(--bg-alt); color: var(--ink-ghost); }
.badge-scheduled { background: #eff6ff; color: #1d4ed8; }
.badge-completed { background: #f0fdf4; color: #15803d; }
.badge-cancelled { background: #fef2f2; color: #b91c1c; }
.badge-no_show   { background: #fef2f2; color: #b91c1c; }
.badge-low       { background: #f0fdf4; color: #15803d; }
.badge-moderate  { background: #fffbeb; color: #b45309; }
.badge-high      { background: #fef2f2; color: #b91c1c; }
.badge-accent    { background: var(--accent-muted); color: var(--accent); }

/* ── Alerts ─────────────────────────────────────────────────── */
.alert {
  padding: 0.625rem 0.875rem;
  border-radius: var(--r-md);
  font-size: var(--text-sm);
  margin-bottom: 0.875rem;
  border: 1px solid;
}
.alert-error   { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.alert-success { background: #f0fdf4; color: #15803d; border-color: #bbf7d0; }
.alert-warning { background: #fffbeb; color: #b45309; border-color: #fde68a; }
.alert-info    { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }

/* ── Modal ──────────────────────────────────────────────────── */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.4);
  backdrop-filter: blur(4px);
  z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  animation: fadeIn .15s ease;
}
.modal {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 12px;
  width: 100%; max-width: 600px; max-height: 90vh;
  /* dvh = altura de viewport realmente visible (navegadores móviles reales
     descuentan la barra de direcciones, a diferencia de vh que usa el
     viewport "grande"). Sin esto, en un celular con la barra de direcciones
     visible el modal podía sobrepasar la pantalla y empujar el encabezado
     (título + botón de cerrar) por encima del borde superior, invisible
     aunque existiera en el DOM. Fallback a vh si el navegador no soporta dvh. */
  max-height: 90dvh;
  display: flex; flex-direction: column;
  box-shadow: 0 20px 60px rgba(0,0,0,.12), 0 4px 12px rgba(0,0,0,.06);
  animation: slideUp .18s ease;
}
.modal.modal-lg { max-width: 860px; }
.modal.modal-xl { max-width: 1060px; }
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.875rem 1.25rem;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.modal-title { font-size: var(--text-base); font-weight: 650; color: var(--ink); letter-spacing: -.01em; }
.modal-close {
  width: 24px; height: 24px;
  border-radius: var(--r-sm);
  font-size: 1rem; color: var(--ink-ghost);
  display: flex; align-items: center; justify-content: center;
  transition: background .15s, color .15s;
}
.modal-close:hover { background: var(--bg-alt); color: var(--ink); }
.modal-body { padding: 1.25rem; overflow-y: auto; flex: 1; }
.modal-footer {
  padding: 0.875rem 1.25rem;
  border-top: 1px solid var(--line);
  display: flex; justify-content: flex-end; gap: 0.5rem;
  flex-shrink: 0;
}

/* ── Toast ──────────────────────────────────────────────────── */
.toast-container {
  position: fixed; bottom: 1rem; right: 1rem; z-index: 2000;
  display: flex; flex-direction: column; gap: 0.35rem;
  max-width: 320px; width: calc(100vw - 2rem);
}
.toast {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.6rem 0.875rem;
  background: #18181b; color: #fafafa;
  border-radius: var(--r-md); font-size: var(--text-sm);
  box-shadow: 0 8px 24px rgba(0,0,0,.2);
  animation: slideUp .2s ease;
  border-left: 2px solid var(--accent);
}
.toast.toast-error   { border-color: var(--red); }
.toast.toast-success { border-color: var(--green); }
.toast.toast-warning { border-color: var(--amber); }
/* R123: el ícono pasó de carácter a SVG. `display:flex` evita que el
   SVG herede la línea base del texto y quede medio píxel más abajo
   que el mensaje. */
.toast-icon { flex-shrink: 0; display: flex; align-items: center; }
.toast.removing { animation: slideDown .18s ease forwards; }
.toast-msg { flex: 1; }
.toast-undo-btn {
  flex-shrink: 0;
  background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); color: #fafafa;
  border-radius: 3px; padding: 0.18rem 0.55rem;
  font-size: var(--text-2xs); font-weight: 600; cursor: pointer;
  transition: background .12s;
}
.toast-undo-btn:hover { background: rgba(255,255,255,.2); }

/* ── Loading ────────────────────────────────────────────────── */
.loading-spinner-wrap { display: flex; align-items: center; justify-content: center; min-height: 200px; }
.spinner { width: 22px; height: 22px; border: 2px solid var(--line); border-top-color: var(--accent); border-radius: 50%; animation: spin .7s linear infinite; }
.spinner--lg { width: 36px; height: 36px; }
.spinner--sm { width: 14px; height: 14px; border-width: 1.5px; }

/* ── Search Bar ─────────────────────────────────────────────── */
/* ════════════════════════════════════════════════════════════════════
   R195 — La barra de filtros de una lista
   --------------------------------------------------------------------
   Las tres pantallas de lista resolvian esto de tres formas distintas:
   Valoraciones metia los filtros en una `.card` con estilos en linea,
   Pacientes los colgaba del `card-header` (y de paso su boton principal,
   que en el resto de la app va en la cabecera de pagina) y Planificacion
   los dejaba sueltos, sin caja.

   Aqui viven una sola vez. Menos apretada que una `.card` a proposito:
   es una barra de herramientas, no contenido -- por eso el radio y el
   relleno son menores que los de la tarjeta que va debajo.
   ════════════════════════════════════════════════════════════════════ */
.list-toolbar {
  display: flex; gap: .6rem; align-items: center; flex-wrap: wrap;
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: .55rem .75rem; margin-bottom: .8rem;
}
/* El buscador manda en el ancho; los filtros se quedan con lo suyo. */
.list-toolbar .search-bar { flex: 1 1 200px; max-width: none; margin-bottom: 0; }
.list-toolbar label {
  font-size: .78rem; color: var(--ink-mute);
  display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap;
}

.search-bar { position: relative; flex: 1; max-width: 280px; }
.search-bar input {
  width: 100%; height: 32px;
  padding: 0 0.7rem 0 2rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  font-size: var(--text-sm);
  background: var(--bg-2);
  transition: border-color .15s;
}
.search-bar input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.1); }
.search-bar svg { position: absolute; left: 0.55rem; top: 50%; transform: translateY(-50%); color: var(--ink-ghost); width: 13px; height: 13px; pointer-events: none; }

/* ── Tabs ───────────────────────────────────────────────────── */
.tabs {
  display: flex; border-bottom: 1px solid var(--line);
  margin-bottom: 1.25rem; flex-wrap: wrap;
}
.tab {
  padding: 0.55rem 0.875rem;
  font-size: var(--text-sm); font-weight: 500; color: var(--ink-mute);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  cursor: pointer; transition: color .15s, border-color .15s;
  white-space: nowrap;
}
.tab:hover { color: var(--ink-soft); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

/* ── Valoracion — Risk Display ──────────────────────────────── */
.risk-display {
  display: flex; align-items: center; gap: 0.875rem;
  padding: 0.875rem 1rem;
  border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--bg-alt);
}
.risk-score { font-size: 2rem; font-weight: 800; line-height: 1; min-width: 52px; text-align: center; letter-spacing: -.04em; }
.risk-score.low      { color: var(--green); }
.risk-score.moderate { color: var(--amber); }
.risk-score.high     { color: var(--red); }
.risk-label { font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; margin-top: 0.1rem; }
.risk-label.low      { color: #15803d; }
.risk-label.moderate { color: #b45309; }
.risk-label.high     { color: #b91c1c; }
.risk-factors-list { font-size: var(--text-xs); color: var(--ink-mute); line-height: 1.7; }
.risk-factor-item { display: flex; align-items: center; gap: 0.3rem; }
.risk-factor-item.present { color: var(--red); }
.risk-factor-item.present::before { content: '●'; font-size: 0.4rem; }
.risk-factor-item.absent  { color: var(--ink-ghost); }
.risk-factor-item.absent::before  { content: '○'; font-size: 0.4rem; }

/* AI diagnosis */
.ai-diagnosis-box {
  border: 1px solid var(--accent-muted);
  border-radius: var(--r-md);
  padding: 0.875rem 1rem;
  background: var(--accent-light);
}
.ai-diagnosis-header {
  display: flex; align-items: center; gap: 0.4rem;
  font-size: var(--text-2xs); font-weight: 700;
  color: var(--accent); text-transform: uppercase; letter-spacing: .06em;
  margin-bottom: 0.5rem;
}
.ai-diagnosis-content { font-size: var(--text-sm); color: var(--ink-soft); line-height: 1.65; white-space: pre-wrap; }
.ai-typing { display: inline-block; width: 5px; height: 12px; background: var(--accent); animation: blink .9s step-end infinite; vertical-align: middle; margin-left: 1px; }

/* ── Valoracion — Reference Badges ─────────────────────────── */
.ref-inline { margin-top: .2rem; min-height: 1rem; }
.ref-empty  { font-size: var(--text-2xs); color: var(--ink-ghost); }
/* Badge de interpretación unificado (CA-2): pill sólido, texto blanco, mismo
   radio/tipografía/espaciado en todos los componentes de resultado de la app. */
.ref-badge { display: inline-flex; align-items: center; gap: .22rem; padding: .12rem .5rem; border-radius: 5px; font-size: var(--text-2xs); font-weight: 600; color: #fff; line-height: 1.5; border: none; }
.ref-badge.ref-normal        { background: #16a34a; }
.ref-badge.ref-warning-light { background: #f59e0b; }
.ref-badge.ref-warning       { background: #f59e0b; }
.ref-badge.ref-danger-light  { background: #f97316; }
.ref-badge.ref-danger        { background: #ef4444; }
.ref-display { padding: .2rem 0; }
.vr-interp  { font-size: var(--text-2xs); font-weight: 600; margin-left: .3rem; }

/* Bilateral comparison — minimal Notion-style */
.bilateral-compare {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .3rem .55rem;
  border-left: 2px solid var(--line-strong);
  background: transparent;
}
.bc-values { font-size: var(--text-xs); color: var(--ink-soft); }
.bc-status { font-size: var(--text-xs); font-weight: 600; white-space: nowrap; }

/* Cognitive scales */
.cog-scale-block {
  background: var(--bg-alt); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 0.625rem 0.75rem; margin-bottom: 0.5rem;
}
.cog-scale-title { font-size: var(--text-sm); font-weight: 650; color: var(--ink); margin-bottom: .15rem; }
.cog-scale-ref {
  display: inline-block; font-size: var(--text-xs);
  background: #e0f2fe; border-radius: 4px;
  padding: .15rem .45rem; margin: .1rem 0 .45rem; line-height: 1.45; max-width: 100%;
}
.cog-typology-block {
  background: var(--accent-light); border: 1px solid var(--accent-muted);
  border-radius: var(--r-md); padding: 0.75rem 0.875rem; margin-top: 0.75rem;
}

/* Spirometry chart */
.spiro-chart-wrap {
  background: var(--bg-alt); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .625rem 0.75rem; margin-top: .5rem;
}
.spiro-bar-row { margin-bottom: .5rem; }
.spiro-bar-label { display: flex; justify-content: space-between; font-size: var(--text-2xs); margin-bottom: .15rem; }
.spiro-bar-track { height: 7px; background: var(--line); border-radius: 4px; position: relative; overflow: hidden; }
.spiro-bar-fill  { height: 100%; border-radius: 4px; transition: width .4s ease; }
.spiro-bar-normal-line { position: absolute; top: 0; bottom: 0; width: 2px; background: #0f172a; box-shadow: 0 0 0 1px rgba(255,255,255,.6); z-index: 1; }
.spiro-bar-hint  { font-size: var(--text-2xs); color: var(--ink-ghost); margin-top: .1rem; }

/* ── Exercise Library ───────────────────────────────────────── */
.exercise-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.75rem; }
.exercise-card {
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
  transition: box-shadow .15s, border-color .15s; cursor: pointer;
}
.exercise-card:hover { box-shadow: var(--shadow-md); border-color: var(--line-strong); }
.exercise-card-img {
  width: 100%; aspect-ratio: 16/9; object-fit: cover;
  background: var(--bg-alt); display: flex; align-items: center; justify-content: center;
  color: var(--ink-ghost); font-size: 1.5rem;
}
.exercise-card-body { padding: 0.625rem 0.75rem; }
.exercise-card-name { font-size: var(--text-sm); font-weight: 600; color: var(--ink); margin-bottom: 0.25rem; letter-spacing: -.01em; }
.exercise-card-meta { display: flex; gap: 0.3rem; flex-wrap: wrap; }

/* ── Agenda / Calendar ──────────────────────────────────────── */
.calendar-wrap { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; }
.calendar-header { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; border-bottom: 1px solid var(--line); }
.calendar-title { font-size: var(--text-base); font-weight: 650; color: var(--ink); letter-spacing: -.01em; }
.calendar-nav { display: flex; gap: 0.25rem; }
.calendar-nav button {
  width: 28px; height: 28px; border-radius: var(--r-sm);
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.875rem; color: var(--ink-soft); background: var(--bg-2);
  transition: background .15s;
}
.calendar-nav button:hover { background: var(--bg-alt); }
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.calendar-dow {
  text-align: center; font-size: var(--text-2xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-ghost);
  padding: 0.5rem 0; border-bottom: 1px solid var(--line); background: var(--bg-alt);
}
.calendar-day {
  min-height: 80px; border-right: 1px solid var(--line-faint);
  border-bottom: 1px solid var(--line-faint); padding: 0.25rem 0.3rem;
  cursor: pointer; transition: background .1s;
  /* Fix: los hijos de un grid con pista 1fr tienen mínimo implícito
     min-content -- las etiquetas de cita (.day-appt, white-space:nowrap)
     no se dejan angostar, así que en móvil la grilla entera crecía más
     ancha que el viewport y el contenedor (.calendar-wrap, overflow:hidden)
     recortaba en silencio las columnas de la derecha (sábado/domingo) y
     el texto de las citas. Mismo patrón que .grid-main-aside > * arriba. */
  min-width: 0;
}
.calendar-day:nth-child(7n) { border-right: none; }
.calendar-day:hover { background: var(--bg-alt); }
.calendar-day.other-month .day-num { color: var(--ink-ghost); }
.calendar-day.today { background: var(--accent-light); }
.calendar-day.today .day-num { color: var(--accent); font-weight: 700; }
.calendar-day.selected-day { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-sm); }
.day-num { font-size: var(--text-xs); font-weight: 500; color: var(--ink-soft); margin-bottom: 0.175rem; }
.day-appt {
  font-size: var(--text-2xs); padding: 0.07rem 0.25rem;
  border-radius: 3px; background: var(--accent); color: #fff;
  margin-bottom: 0.1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-weight: 500; cursor: pointer;
}
.day-appt.status-cancelled { background: var(--ink-ghost); }
.day-appt.status-completed { background: var(--green); }
.day-appt.status-no_show   { background: var(--red); }
.day-more-btn {
  font-size: var(--text-2xs); color: var(--accent); font-weight: 600;
  cursor: pointer; margin-top: .06rem; padding: .03rem .15rem;
  border-radius: 3px; display: inline-block;
}
.day-more-btn:hover { background: var(--accent-light); }

/* ── Patient Portal ─────────────────────────────────────────── */
.exercise-plan-day { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; margin-bottom: 0.625rem; }
.exercise-plan-day-header {
  background: var(--bg-alt); padding: 0.55rem 0.75rem;
  font-weight: 650; font-size: var(--text-sm);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
}
.exercise-item { display: flex; align-items: center; gap: 0.625rem; padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--line-faint); }
.exercise-item:last-child { border-bottom: none; }
.exercise-item-thumb {
  width: 42px; height: 42px; border-radius: var(--r-sm); object-fit: cover;
  background: var(--bg-alt); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 1.25rem;
}
.exercise-item-name { font-weight: 600; font-size: var(--text-sm); margin-bottom: 0.1rem; letter-spacing: -.01em; }
.exercise-item-meta { font-size: var(--text-xs); color: var(--ink-mute); }
.exercise-check {
  margin-left: auto; width: 20px; height: 20px; border-radius: 50%;
  border: 1.5px solid var(--line-strong); flex-shrink: 0; cursor: pointer;
  transition: background .15s, border-color .15s;
  display: flex; align-items: center; justify-content: center;
}
.exercise-check.done { background: var(--green); border-color: var(--green); }
.exercise-check.done::after { content: '✓'; color: #fff; font-size: 0.65rem; font-weight: 700; }

/* R64: peso como "cajita" tocable en vez de texto subrayado -- vacío
   invita con un botón explícito ("Agregar peso"), con valor cargado se ve
   como un campo (recuadro con el número adentro), mismo lenguaje visual
   que usan apps de este tipo (Strong/Hevy) para dejar claro que ambos
   estados son editables. */
.portal-peso-btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: inherit; font-size: .78rem; font-weight: 600; line-height: 1.2;
  padding: .2rem .5rem; border-radius: var(--r-sm);
  cursor: pointer; white-space: nowrap;
}
.portal-peso-btn--empty {
  color: var(--accent); background: var(--accent-glow); border: 1px solid var(--accent);
}
.portal-peso-btn--empty:hover { background: var(--accent-light); }
.portal-peso-btn--filled {
  color: var(--ink); font-weight: 500; background: var(--bg-alt); border: 1px solid var(--line);
}
.portal-peso-btn--filled:hover { border-color: var(--ink-ghost); }

/* R65: calendario mensual "Mi Progreso" -- reusa .calendar-wrap/.calendar-day
   de Agenda, solo agrega el punto de estado bajo el número del día. */
.progress-cal-dot {
  width: 6px; height: 6px; border-radius: 50%; margin: 2px auto 0;
}
.progress-cal-dot--full { background: #16a34a; }
.progress-cal-dot--partial { background: #f59e0b; }
.progress-cal-legend {
  display: flex; flex-wrap: wrap; gap: .9rem; margin-top: .6rem;
  font-size: .74rem; color: var(--ink-mute);
}
.progress-cal-legend span { display: inline-flex; align-items: center; gap: .35rem; }
.progress-cal-legend .progress-cal-dot { margin: 0; }

/* R68: temporizador de descanso, portal del paciente. */
.portal-rest-timer-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: var(--r-sm); border: 1px solid var(--line);
  background: var(--bg-alt); color: var(--accent); font-size: .8rem; cursor: pointer; line-height: 1; padding: 0;
}
.portal-rest-timer-btn:hover { border-color: var(--accent); }
.portal-rest-timer-bar {
  display: none; align-items: center; gap: .6rem; position: fixed; left: 50%; bottom: 1rem;
  transform: translateX(-50%); z-index: 500; background: var(--ink); color: #fff;
  border-radius: var(--r-md); padding: .55rem .9rem; box-shadow: 0 6px 20px rgba(0,0,0,.25);
  font-size: .82rem; max-width: calc(100vw - 2rem);
}
.portal-rest-timer-bar.done { background: #16a34a; }
.portal-rest-timer-label { opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 140px; }
.portal-rest-timer-clock { font-weight: 700; font-variant-numeric: tabular-nums; }
.portal-rest-timer-close {
  background: none; border: none; color: #fff; opacity: .75; cursor: pointer; font-size: .85rem; padding: 0 0 0 .3rem;
}
.portal-rest-timer-close:hover { opacity: 1; }

/* ── Drag & Drop ────────────────────────────────────────────── */
tr[data-ex-id] { transition: background .1s; }
tr.ex-dragging { opacity: 0.3; }
.drag-handle { cursor: grab; color: var(--ink-ghost); }
.drag-handle:active { cursor: grabbing; }

/* R30: biseries/circuitos ya no usan un botón flotante entre filas — se
   crean arrastrando un ejercicio directamente ENCIMA de otro. La zona de
   drop determina la intención: soltar cerca del borde superior/inferior
   reordena (línea de inserción); soltar en el centro agrupa (resaltado
   completo de la fila destino). */
tr.ex-drop-before td { box-shadow: inset 0 2px 0 0 var(--accent); }
tr.ex-drop-after  td { box-shadow: inset 0 -2px 0 0 var(--accent); }
tr.ex-drop-group td { background: var(--accent-light) !important; }
tr.ex-drop-group { outline: 2px dashed var(--accent); outline-offset: -2px; }

/* R31: todas las biseries/circuitos comparten el MISMO tinte (un solo
   color, no uno por grupo, para no saturar la sesión con muchos grupos) —
   este borde en la ÚLTIMA fila de cada grupo es lo único que distingue
   visualmente dónde termina un grupo y empieza el siguiente cuando dos
   grupos distintos quedan uno junto al otro. Aplica en Planificación y en
   el Portal del paciente (misma clase, mismo criterio). */
.ex-group-last td { border-bottom: 2px solid rgba(37,99,235,.35); }

/* R33: los inputs de las celdas editables de la tabla de sesión
   (Series/Reps/Peso/Intensidad/Descanso/Indicaciones en Planificación)
   no tenían NINGÚN estilo propio — quedaban con la apariencia por
   defecto del navegador, fuera del lenguaje visual Notion del resto de
   la app. Mismo tratamiento que .control-input, en versión compacta
   para caber dentro de una celda de tabla. */
.cell-input {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: .2rem .45rem;
  font-size: .8rem;
  font-family: inherit;
  color: var(--ink);
  background: var(--bg-2);
}
.cell-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(37,99,235,.12);
}

/* ── Pre-assessment ─────────────────────────────────────────── */
.pre-assessment-intro {
  background: var(--accent-light); border: 1px solid var(--accent-muted);
  border-radius: var(--r-sm); padding: .625rem 0.75rem; margin-bottom: 0.875rem;
  display: flex; flex-direction: column; gap: .25rem;
}
.pre-assessment-intro p { font-size: var(--text-xs); color: var(--ink-soft); margin: 0; line-height: 1.5; }
.pre-assessment-badge {
  font-size: var(--text-2xs); font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--accent);
  background: var(--accent-glow); border-radius: 20px;
  padding: .12rem .5rem; display: inline-block; width: fit-content;
}

/* ── Patient Autocomplete ───────────────────────────────────── */
.pac-wrap { position: relative; }
.pac-input {
  width: 100%; height: 34px; padding: 0 0.625rem;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  font-size: var(--text-sm); font-family: inherit;
  color: var(--ink); background: var(--bg-2);
  transition: border-color .15s; box-sizing: border-box;
}
.pac-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.1); }
.pac-dropdown {
  position: absolute; top: calc(100% + 3px); left: 0; right: 0;
  background: var(--bg-2); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: 0 8px 24px rgba(0,0,0,.1);
  z-index: 1000; max-height: 320px; overflow-y: auto;
}
.pac-item { display: flex; align-items: center; justify-content: space-between; padding: .45rem .75rem; cursor: pointer; transition: background .1s; gap: .45rem; }
.pac-item:hover { background: var(--bg-alt); }
.pac-name  { font-size: var(--text-sm); font-weight: 500; color: var(--ink); }
.pac-meta  { font-size: var(--text-xs); color: var(--ink-mute); white-space: nowrap; }
.pac-empty { padding: .45rem .75rem; font-size: var(--text-sm); color: var(--ink-mute); }
.pac-new-btn {
  padding: .45rem .75rem; font-size: var(--text-sm); font-weight: 500;
  color: var(--accent); cursor: pointer;
  border-top: 1px solid var(--line); background: var(--bg-alt);
  transition: background .1s;
}
.pac-new-btn:hover { background: var(--accent-light); }

/* ── Clinical Info Tooltip ──────────────────────────────────── */
.ci-info-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 13px; height: 13px; flex: 0 0 auto; border-radius: 50%;
  border: 1px solid var(--ink-ghost); color: var(--ink-ghost);
  font-size: .6rem; font-style: italic; font-weight: 700;
  font-family: Georgia, serif; line-height: 1; cursor: help; user-select: none;
  transition: background .12s, color .12s, border-color .12s;
}
.ci-info-icon:hover,
.ci-info-icon:focus { background: var(--accent); border-color: var(--accent); color: #fff; outline: none; }
.ci-tooltip {
  position: fixed; z-index: 4000; display: flex; gap: .5rem;
  width: 260px; max-width: calc(100vw - 16px); max-height: calc(100vh - 16px);
  overflow-y: auto; padding: .55rem .6rem;
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--r-md); box-shadow: var(--shadow-md);
  opacity: 0; transform: translateY(4px);
  pointer-events: none; transition: opacity .12s ease, transform .12s ease;
}
.ci-tooltip.ci-tooltip--visible { opacity: 1; transform: translateY(0); }
.ci-tooltip-body    { flex: 1; min-width: 0; }
.ci-tooltip-title   { font-size: var(--text-xs); font-weight: 700; color: var(--ink); margin-bottom: .15rem; }
.ci-tooltip-desc    { font-size: var(--text-2xs); line-height: 1.4; color: var(--ink-soft); margin-bottom: .25rem; }
.ci-tooltip-stats   { display: flex; gap: .5rem; flex-wrap: wrap; font-size: var(--text-2xs); color: var(--ink); margin-bottom: .2rem; }
.ci-tooltip-stats strong { font-weight: 700; color: var(--accent); }
.ci-tooltip-howto   { margin-bottom: .25rem; }
.ci-tooltip-howto-label { font-size: var(--text-2xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-ghost); margin-bottom: .18rem; }
.ci-tooltip-howto-img { display: block; width: 100%; max-height: 120px; object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line); }
.ci-tooltip-noimg { padding: .4rem; background: var(--bg-alt); border: 1px dashed var(--line); border-radius: var(--r-sm); text-align: center; color: var(--ink-ghost); font-size: var(--text-2xs); line-height: 1.35; }
.ci-tooltip-disclaimer { font-size: .58rem; line-height: 1.3; color: var(--ink-ghost); font-style: italic; }

/* ── Intake Form ────────────────────────────────────────────── */
.intake-shell {
  min-height: 100vh; width: 100%;
  display: flex; align-items: center; justify-content: center;
  padding: 2rem 1rem; background: var(--bg);
  font-family: var(--ff); box-sizing: border-box;
}
.intake-card {
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow-lg);
  max-width: 600px; width: 100%; padding: 2rem; box-sizing: border-box;
}
.intake-logo { display: flex; align-items: center; gap: 0.625rem; margin-bottom: 1.25rem; }
.intake-logo-mark { display: flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border-radius: var(--r-md); background: var(--accent); color: #fff; font-weight: 800; font-size: 1rem; }
.intake-logo-name { display: block; font-weight: 700; font-size: 0.875rem; color: var(--ink); letter-spacing: -.01em; }
.intake-logo-sub  { display: block; font-size: var(--text-xs); color: var(--ink-mute); }
.intake-title { font-weight: 700; font-size: 1.375rem; color: var(--ink); margin: 0 0 0.35rem; letter-spacing: -.02em; }
.intake-sub   { color: var(--ink-mute); font-size: var(--text-sm); line-height: 1.5; margin: 0 0 1.25rem; }
.intake-section-title {
  font-weight: 700; font-size: var(--text-2xs);
  letter-spacing: .07em; text-transform: uppercase; color: var(--accent);
  border-bottom: 1px solid var(--line); padding-bottom: 0.3rem;
  margin: 1.5rem 0 0.75rem;
}
.intake-section-title:first-of-type { margin-top: 0.25rem; }
.intake-radio-row { display: flex; gap: 1.25rem; flex-wrap: wrap; row-gap: 0.55rem; }
.intake-radio-label { display: flex; align-items: center; gap: 0.3rem; font-size: var(--text-sm); color: var(--ink-soft); cursor: pointer; }
/* R145b: los radios y casillas viven dentro de .field-group, asi que
   heredaban `width:100%; height:34px; border; padding` de la regla de
   inputs de mas arriba -- pensada para campos de texto. Con dos opciones
   cortas pasaba desapercibido; al anadir una tercera el radio se volvio un
   circulo enorme y el texto se partio en tres lineas. Se les devuelve su
   tamano natural. El `nowrap` va solo en la fila de opciones: la casilla
   del aviso de privacidad es una frase larga y SI debe poder partirse. */
.intake-radio-row .intake-radio-label { white-space: nowrap; }
.field-group .intake-radio-label input[type="radio"],
.field-group .intake-radio-label input[type="checkbox"] {
  width: auto; height: auto; min-height: 0; flex: 0 0 auto;
  padding: 0; border: 0; border-radius: 0; background: none;
  accent-color: var(--accent);
}
/* R145b: el enlace al aviso salia con el MISMO color que el texto y sin
   subrayado, o sea invisible. Una casilla que afirma "he leido el aviso"
   junto a un enlace que nadie ve es una aceptacion vacia. */
.intake-radio-label a { color: var(--accent); text-decoration: underline; font-weight: 600; }
.intake-radio-label a:hover { text-decoration-thickness: 2px; }
.intake-honeypot { position: absolute; left: -9999px; top: -9999px; width: 1px; height: 1px; overflow: hidden; }
.intake-success { text-align: center; padding: 1.25rem 0; }
.intake-success-icon { display: inline-flex; align-items: center; justify-content: center; width: 3rem; height: 3rem; border-radius: 50%; background: #f0fdf4; color: #15803d; font-size: 1.5rem; font-weight: 700; margin-bottom: 0.75rem; }

/* ── Empty States ───────────────────────────────────────────── */
.empty-state { text-align: center; padding: 2.5rem 1.25rem; color: var(--ink-mute); }
.empty-state-icon  { font-size: 2rem; margin-bottom: 0.5rem; }
.empty-state-title { font-weight: 650; font-size: var(--text-base); color: var(--ink-soft); margin-bottom: 0.25rem; letter-spacing: -.01em; }
.empty-state-sub   { font-size: var(--text-sm); }

/* ── Animations ─────────────────────────────────────────────── */
@keyframes spin      { to { transform: rotate(360deg); } }
@keyframes fadeIn    { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp   { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes slideDown { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(8px); } }
@keyframes blink     { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

/* ── Responsive ─────────────────────────────────────────────── */
@media (max-width: 1023px) {
  .sidebar {
    position: fixed; left: 0; top: 0; bottom: 0;
    transform: translateX(-100%); transition: transform .2s ease;
    z-index: 200; width: var(--sb-w) !important;
  }
  .sidebar.mobile-open { transform: translateX(0); }
  .sidebar .sidebar-logo-text,
  .sidebar .user-info,
  .sidebar .nav-section-label,
  .sidebar .nav-item span { display: flex !important; }
  .sidebar .nav-item { justify-content: flex-start !important; padding: 0.4rem 0.75rem !important; }
  .sidebar-toggle { display: none; }
  .topbar-menu-btn { display: flex; }
  .main-area { margin-left: 0 !important; }
  /* En móvil, todas las variantes de grid main+aside se apilan en una sola
     columna. Debe listarse explícitamente .plan-detail-grid porque su regla
     base (.grid-main-aside.plan-detail-grid, especificidad 0,2,0) es más
     específica que .grid-main-aside sola (0,1,0) y, sin esto, mantiene las
     dos columnas en pantallas angostas — aplastando las tarjetas del plan a
     ~50px y partiendo el texto letra por letra. */
  .grid-main-aside,
  .grid-main-aside.plan-detail-grid { grid-template-columns: 1fr; }
  /* Los hijos de un grid con pista 1fr tienen mínimo implícito min-content,
     así que una tarjeta con una tabla ancha crece al ancho de la tabla y se
     desborda del viewport (la página entera hace scroll horizontal). Con
     min-width:0 la tarjeta se limita a la pista y su .table-wrap interno
     (overflow-x:auto) hace el scroll en su lugar. */
  .grid-main-aside > * { min-width: 0; }
  .grid-2 { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: 1fr 1fr; }
  .form-row-3 { grid-template-columns: 1fr 1fr; }
  .form-row-4 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .content-area { padding: 0.875rem; }
  /* En celular, los encabezados de tarjeta (título/subtítulo + grupo de
     botones de acción) apilan: el grupo de botones baja a su propia línea en
     vez de comprimir el texto a una columna de ~50px que se parte letra por
     letra. min-width:0 permite que el bloque de texto ceda y no fuerce
     desbordes. */
  .card-header { flex-wrap: wrap; row-gap: .5rem; }
  .card-header > div { min-width: 0; }
  .grid-3 { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; }
  .form-row-3, .form-row-4 { grid-template-columns: 1fr; }
  .stats-grid { grid-template-columns: 1fr 1fr; }
  .exercise-grid { grid-template-columns: 1fr 1fr; }
  .page-header { flex-direction: column; gap: 0.75rem; }
  /* ============================================================
     R89 — Calendario mensual en el celular
     ------------------------------------------------------------
     A 375 px cada casilla mide ~50 px: una etiqueta "10:00 María" no
     cabe y se cortaba en "10:...", que no dice nada. El mes pasa a ser
     un mapa de densidad -- número del día y un punto por cita, con el
     color del estado -- y el detalle se lee en "Citas del Día", que en
     una columna queda justo debajo (por eso el toque hace scroll ahí).

     Los puntos no son clicables a propósito: 6 px es un objetivo
     imposible de acertar con el pulgar, y errarlo abriría la ficha de
     una cita en lugar de seleccionar el día.
     ============================================================ */
  .calendar-day {
    min-height: 46px;
    display: flex; flex-wrap: wrap;
    justify-content: center; align-content: flex-start;
    gap: 3px; padding: .3rem .15rem;
  }
  .day-num {
    flex: 0 0 100%; text-align: center;
    margin-bottom: .1rem; font-size: .8rem;
  }
  .day-appt {
    width: 6px; height: 6px; padding: 0; margin: 0;
    border-radius: 50%;
    font-size: 0; line-height: 0; overflow: hidden;
  }
  .day-appt, .day-more-btn { pointer-events: none; }
  .day-more-btn { flex: 0 0 100%; text-align: center; font-size: .58rem; margin: 0; }
  .modal { max-width: 100%; border-radius: 12px 12px 0 0; align-self: flex-end; max-height: 95vh; max-height: 95dvh; }
  .modal-overlay { align-items: flex-end; padding: 0; }

  /* AJ-4 (Portal únicamente — R47-1 revirtió la versión de Planificación/
     kine a tabla con scroll horizontal, ver .table-wrap): tabla de
     ejercicios del Portal "Mis Ejercicios" como tarjetas apiladas en vez
     de forzar scroll horizontal. Reflow puramente CSS — thead se oculta y
     cada <tr> pasa a ser un grid de 2 filas (cabecera + campos apilados);
     el DOM/JS no cambia, así que los selectores existentes (data-se-id,
     data-ex, etc.) siguen intactos. */
  .exercise-plan-day .data-table,
  .exercise-plan-day .data-table tbody { display: block; width: 100%; }
  .exercise-plan-day .data-table thead { display: none; }
  .exercise-plan-day .data-table tr {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: start;
    column-gap: .6rem;
    row-gap: .3rem;
    padding: .75rem .8rem;
    margin-bottom: .6rem;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-2);
    box-shadow: var(--shadow-card);
  }
  .exercise-plan-day .data-table tr:last-child { margin-bottom: 0; }
  .exercise-plan-day .data-table td { display: block; padding: 0; border: none; }

  /* [foto][nombre][✓] arriba, resto (series/reps/peso/…/comentarios o el
     bloque de cardio) apilado abajo, una etiqueta por campo. */
  .exercise-plan-day .data-table td:nth-child(1) { grid-column: 1; grid-row: 1; }
  .exercise-plan-day .data-table td:nth-child(2) { grid-column: 2; grid-row: 1; }
  .exercise-plan-day .data-table td:last-child   { grid-column: 3; grid-row: 1; }
  .exercise-plan-day .data-table td:nth-child(n+3):not(:last-child) {
    grid-column: 1 / -1;
    padding-top: .45rem;
    border-top: 1px solid var(--line-faint);
  }

  .exercise-plan-day .data-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: .66rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--ink-mute);
    margin-bottom: .2rem;
  }

  /* ============================================================
     R88 — Tablas del staff como tarjetas apiladas
     ------------------------------------------------------------
     Mismo patrón que arriba (portal del paciente), ahora para las
     listas del profesional: Pacientes, Usuarios, Clínicas y las
     tablas de Valoración. Con 6-8 columnas, un celular obliga a
     desplazarse en horizontal para leer una sola fila -- inviable en
     consulta, con una mano ocupada.

     Sigue siendo CSS puro: el HTML y el JS de cada módulo no cambian,
     así que los onclick, ids y data-* existentes siguen intactos. La
     etiqueta de cada campo sale del atributo `data-label`, que pone
     utils/mobile-tables.js copiando los encabezados.

     Se aplica solo a las tablas dentro de .table-wrap, que es la
     envoltura estándar de las listas. Las tablas de dentro de un modal
     o las de layout quedan fuera a propósito.

     Excepción explícita: .table-wrap.tw-plain conserva el scroll lateral.
     Se usa donde la tabla NO es una lista de fichas sino una rejilla de
     edición -- la tabla de ejercicios de una sesión, por ejemplo, donde
     cada celda es un campo editable y apilarlas rompe la lectura por
     columnas (R47-1 ya revirtió ese intento una vez).
     ============================================================ */
  .table-wrap:not(.tw-plain) { overflow-x: visible; }   /* ya no hace falta el scroll lateral */

  .table-wrap:not(.tw-plain) .data-table,
  .table-wrap:not(.tw-plain) .data-table tbody { display: block; width: 100%; }
  .table-wrap:not(.tw-plain) .data-table thead { display: none; }

  .table-wrap:not(.tw-plain) .data-table tr {
    display: block;
    padding: .7rem .8rem;
    margin-bottom: .55rem;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--bg-2);
    box-shadow: var(--shadow-card);
  }
  .table-wrap:not(.tw-plain) .data-table tr:last-child { margin-bottom: 0; }

  .table-wrap:not(.tw-plain) .data-table td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
    padding: .28rem 0;
    border: none;
    text-align: left !important;   /* gana al text-align inline de las celdas numéricas */
  }
  /* La primera celda hace de título de la tarjeta: sin etiqueta y con
     más peso, para que la fila se identifique de un vistazo. */
  .table-wrap:not(.tw-plain) .data-table td:first-child {
    font-weight: 700;
    font-size: var(--text-sm);
    padding-bottom: .45rem;
    margin-bottom: .3rem;
    border-bottom: 1px solid var(--line-faint);
  }
  .table-wrap:not(.tw-plain) .data-table td:first-child::before { display: none; }

  .table-wrap:not(.tw-plain) .data-table td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: .66rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--ink-mute);
    white-space: nowrap;
  }
  /* El valor se alinea a la derecha frente a su etiqueta y puede partir
     en varias líneas sin empujar la etiqueta. */
  .table-wrap:not(.tw-plain) .data-table td[data-label] { text-align: right !important; }
  .table-wrap:not(.tw-plain) .data-table td[data-label] > * { text-align: left; }

  /* Fila de "no hay resultados": ocupa la tarjeta entera, centrada. */
  .table-wrap:not(.tw-plain) .data-table tr[data-fullrow] td { display: block; text-align: center !important; }
  .table-wrap:not(.tw-plain) .data-table tr[data-fullrow] td::before { display: none; }

  /* Las celdas de acciones (botones) necesitan espacio propio: en una
     fila estrecha los iconos quedarían apretados contra su etiqueta.
     R96: `clear` para que arranquen bajo los parámetros flotados -- si no
     se colocan al lado del último y se salen de la pantalla. */
  .table-wrap:not(.tw-plain) .data-table td:last-child { padding-top: .45rem; clear: both; }

  /* ============================================================
     R90 — La tarjeta de ejercicio (Planificación)
     ------------------------------------------------------------
     Esta tabla tiene 10 columnas y es la que más se consulta en
     consulta. Como tarjeta necesita tres ajustes propios:

     1. El asa de arrastrar (⠿) se oculta: reordenar arrastrando no
        funciona con el dedo y como "título" de la tarjeta no dice nada.
     2. Por eso el título pasa a ser la celda del ejercicio (3ª), no la
        primera -- la regla genérica de arriba se anula para las dos
        primeras celdas.
     3. La miniatura se ancla a la izquierda y el contenido se corre:
        es lo que convierte la lista en algo reconocible de un vistazo.
     ============================================================ */
  /* La especificidad tiene que igualar a la regla genérica de arriba
     (.table-wrap:not(.tw-plain) .data-table td), que pone display:flex;
     con solo `.exercises-table td.drag-handle` el asa seguía visible. */
  .table-wrap .exercises-table td.drag-handle { display: none; }

  /* R160: aqui vivia la celda del badge de bloque (era la 2a). Ya no
     existe: en el movil el bloque se lee en la cabecera del tramo, que es
     una sola linea para todas las tarjetas del bloque en vez de un badge
     repetido dentro de cada una.

     La celda del ejercicio, que era la 3a, es ahora la 2a. */
  /* La celda del ejercicio es el título de la tarjeta. */
  .table-wrap .exercises-table td:nth-child(2) {
    display: block;
    font-size: var(--text-sm);
    padding-bottom: .45rem;
    margin-bottom: .35rem;
    border-bottom: 1px solid var(--line-faint);
    min-width: 0 !important;
    text-align: left !important;
  }
  /* La celda del nombre limpia su propio flotante (la miniatura). Sin
     esto, la primera celda de parámetros se coloca AL LADO de la
     miniatura y arrastra la rejilla: "Series" quedaba solo en su fila. */
  .table-wrap .exercises-table td:nth-child(2)::after {
    content: ''; display: block; clear: both;
  }
  /* Sin "EJERCICIO" como etiqueta: el nombre se explica solo, y encabezar
     la tarjeta con un rótulo en versalitas es justo el ruido que hacía
     ilegible el intento anterior. */
  .table-wrap .exercises-table td:nth-child(2)::before { display: none; }

  /* Hueco de la miniatura: se reserva SIEMPRE (imagen o inicial) para
     que el texto de todas las tarjetas arranque a la misma altura. */
  .ex-thumb-wrap {
    display: inline-flex; float: left;
    width: 52px; height: 52px;
    margin: 0 .6rem .25rem 0;
    border-radius: 8px; overflow: hidden;
    align-items: center; justify-content: center;
    background: var(--bg-alt);
    border: 1px solid var(--line-faint);
  }
  .ex-thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
  /* Sin imagen (o con la URL rota): cuadro con la inicial. */
  .ex-thumb-fail {
    font-size: 1.15rem; font-weight: 700; color: var(--ink-ghost);
  }
  /* La fila cierra el float para que la siguiente tarjeta no se monte. */
  .table-wrap .exercises-table tr::after { content: ''; display: block; clear: both; }

  /* R96: parámetros en rejilla de dos columnas, con la etiqueta ENCIMA
     del valor. Antes iban seguidos en una línea corrida y se leían como
     una frase: "SERIES 3 REPS / TIEMPO 45 seg PESO — INTENSIDAD 7 RPE".
     Sin un salto entre etiqueta y valor, el ojo no sabe dónde termina
     un dato y empieza el siguiente. */
  .table-wrap .exercises-table td.ro-cell {
    /* Flotantes y no inline-flex: entre elementos inline el navegador
       inserta el espacio en blanco del HTML, y dos celdas al 50% + ese
       espacio suman más del 100% -- la segunda saltaba de línea y la
       rejilla quedaba descuadrada. */
    float: left;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    width: 50%;
    box-sizing: border-box;
    padding: .15rem .4rem .15rem 0;
    line-height: 1.25;
    border: none;
    text-align: left !important;
  }
  .table-wrap .exercises-table td.ro-cell::before {
    content: attr(data-label);
    margin: 0 0 .1rem;
    font-size: .6rem;
    line-height: 1.2;
  }
  .table-wrap .exercises-table td.ro-cell .cell-val {
    font-size: .82rem !important;
    color: var(--ink) !important;
  }
  /* R150: aqui habia una regla para la celda de Comentarios. Esa columna
     ya no existe -- la nota se pinta bajo el nombre (.ex-nota), donde en
     movil cae dentro del titulo de la tarjeta, que es su sitio natural. */
  /* R94: el nombre vuelve a abrir solo el video/imagen, en móvil y en
     escritorio. Editar tiene su propio botón ✎ en la fila, igual que
     cardio, así que marcar también el nombre sugeriría dos caminos. */
}

/* En escritorio la miniatura estorba: la tabla se lee por columnas y una
   imagen por fila la haría el doble de alta sin aportar nada que el
   nombre no diga ya. */
@media (min-width: 641px) {
  .ex-thumb-wrap { display: none; }
}

/* ============================================================
   R109 — Miniatura en "Agregar ejercicio a sesión"
   ------------------------------------------------------------
   El popup reutiliza el HTML de _exThumbHtml (R91), pero aquel se
   diseñó para la TARJETA de la tabla de sesión en móvil, y arrastraba
   dos cosas que aquí estorban:

     · La regla de arriba lo esconde en escritorio a propósito (una
       imagen por fila haría la tabla del doble de alta). En el popup
       eso dejaba las filas SIN NINGUNA imagen en computadora.
     · En móvil venía con `float:left` y 52 px, pensado para que el
       texto de la tarjeta lo rodee. Dentro de una fila flex, el float
       empujaba el nombre a una columna de dos palabras por línea.

   Aquí la miniatura es un elemento más de la fila: tamaño fijo, sin
   flotar y sin encogerse. Va FUERA de toda media query y con más
   especificidad (0,2,0) que las reglas de arriba (0,1,0), así que gana
   en los dos tamaños sin necesidad de !important.
   ============================================================ */
.ex-bank-row .ex-thumb-wrap {
  display: inline-flex;
  float: none;
  /* R184: 40 -> 72 px. Los dibujos del banco son figuras completas con
     mucho blanco por dentro del cuadro, asi que a 40 px la persona medía
     unos 20 y no se distinguia una abduccion de una aduccion.

     El tope de .ex-bank-list sube a la vez (240 -> 360 px): agrandar la
     miniatura a secas habria bajado de 3,9 a 3,2 ejercicios a la vista.
     Con los dos cambios la fila mide 90 px y se ven 4. */
  width: 72px;
  height: 72px;
  margin: 0;
  flex-shrink: 0;
  border-radius: 6px;
  overflow: hidden;
  align-items: center;
  justify-content: center;
  background: var(--bg-alt);
  border: 1px solid var(--line-faint);
}
/* `contain` y no `cover`: en el banco conviven fotos y láminas
   anatómicas verticales; recortar deja fuera justo la articulación que
   identifica al ejercicio. */
.ex-bank-row .ex-thumb { width: 100%; height: 100%; object-fit: contain; display: block; }
.ex-bank-row .ex-thumb-fail { font-size: .95rem; font-weight: 700; color: var(--ink-ghost); }
/* El nombre manda sobre badges y miniatura al repartir el ancho. */
.ex-bank-row .ex-name { min-width: 0; overflow-wrap: anywhere; }

/* R111 — en 375 px la fila llevaba cinco columnas: casilla, miniatura,
   nombre y dos badges. Al nombre le quedaban ~90 px y salía partido en
   cuatro líneas de una palabra. Ahora el nombre reclama al menos el 55%
   y los badges bajan a una segunda línea cuando no caben. */
@media (max-width: 640px) {
  .ex-bank-row { flex-wrap: wrap; }
  .ex-bank-row .ex-name { flex: 1 1 55%; }
  .ex-bank-row .badge { font-size: .6rem; }
}

/* ============================================================
   R110 — Registro diario: nota del día vs comentario de sesión
   ------------------------------------------------------------
   Las dos cosas se leían idénticas, en cursiva gris, aunque una
   responda al día completo y la otra a un ejercicio concreto. Ahora se
   distinguen por ESTRUCTURA, no por color de texto: el comentario de
   sesión lleva una línea vertical de acento y, encima, el nombre del
   ejercicio. La nota del día se queda como estaba.
   ============================================================ */
/* ============================================================
   R114 — Registro del día como línea plegable (portal del paciente)
   ------------------------------------------------------------
   Plegada ocupa una línea y no desplaza "Empezar sesión"; desplegada
   muestra el formulario en el sitio, sin viaje ni modal. Mismo patrón
   que las filas de día del Registro diario del kine (R112), para que
   las dos pantallas se comporten igual.
   ============================================================ */
.checkin-card { padding: 0 !important; }
.checkin-cab {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  padding: .7rem .9rem;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-size: .85rem;
  color: var(--ink);
}
.checkin-ico { font-size: 1rem; line-height: 1; flex-shrink: 0; }
.checkin-txt { flex: 1; font-weight: 600; }
.checkin-accion { font-size: .78rem; color: var(--accent); font-weight: 600; }
.checkin-chev { flex-shrink: 0; color: var(--ink-ghost); transition: transform .15s ease; }
.checkin-cab.is-open .checkin-chev { transform: rotate(180deg); }
.checkin-cuerpo { padding: 0 .9rem .9rem; }
.checkin-cuerpo[hidden] { display: none; }

/* ── R112: día plegable ── */
.reg-dia + .reg-dia { border-top: 1px solid var(--line); }
.reg-dia-cab {
  display: flex;
  align-items: center;
  gap: .4rem;
  width: 100%;
  padding: .5rem 0;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-size: .78rem;
  font-weight: 600;
  color: var(--ink);
}
.reg-dia-chev { flex-shrink: 0; color: var(--ink-ghost); transition: transform .15s ease; }
.reg-dia-cab.is-open .reg-dia-chev { transform: rotate(90deg); }
.reg-dia-fecha { flex: 1; }
/* El punto resume el día de un vistazo: peor dolor en rehabilitación,
   peor descanso en deporte. Sin autorreporte no hay nada que colorear y
   se deja el hueco -- que informa: "ese día solo comentó entrenando". */
.reg-dia-punto {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.reg-dia-punto-vacio { background: transparent; }
.reg-dia-iconos {
  display: inline-flex;
  gap: .15rem;
  font-size: .72rem;
  line-height: 1;
  flex-shrink: 0;
}
.reg-dia-cuerpo { padding: 0 0 .5rem .95rem; }
.reg-dia-cuerpo[hidden] { display: none; }

.reg-item + .reg-item { margin-top: .5rem; }
.reg-item-cab {
  display: flex;
  align-items: center;
  gap: .3rem;
  font-size: .74rem;
  font-weight: 600;
  color: var(--ink-soft);
}
.reg-item-ico { font-size: .78rem; line-height: 1; }
.reg-item-sep { color: var(--ink-ghost); font-weight: 400; }

.reg-sep {
  margin: .6rem 0 .3rem;
  font-size: .64rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-ghost);
}

.reg-nota-dia {
  font-size: .76rem;
  color: var(--ink-mute);
  margin-top: .2rem;
  font-style: italic;
}
.reg-coment-ses {
  margin-top: .35rem;
  padding-left: .5rem;
  border-left: 2px solid var(--accent);
}
.reg-coment-ex {
  display: block;
  font-size: .68rem;
  font-weight: 600;
  color: var(--accent);
  line-height: 1.25;
  /* Los nombres del banco llegan a 60 caracteres; en un panel de 280 px
     eso son cuatro líneas. Una sola, y el resto en el tooltip. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.reg-coment-txt {
  display: block;
  font-size: .76rem;
  color: var(--ink-soft);
  line-height: 1.35;
}

/* ── Utilities ──────────────────────────────────────────────── */
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.gap-1 { gap: 0.5rem; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.text-mute  { color: var(--ink-mute); }
.text-sm    { font-size: var(--text-sm); }
.text-xs    { font-size: var(--text-xs); }
.font-bold  { font-weight: 700; }
.w-full     { width: 100%; }

/* ============================================================
   Analytics / Análisis estadísticos
   ============================================================ */
.an-wrap { max-width: 1180px; margin: 0 auto; padding: 1.25rem 1.5rem 3rem; }
.an-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; }
.an-title { font-size: 1.5rem; font-weight: 800; color: var(--ink); margin: 0; }
.an-sub { font-size: .85rem; color: var(--ink-mute); margin: .25rem 0 0; max-width: 70ch; }
.an-tabs { display: inline-flex; gap: .25rem; background: var(--bg-alt, #f1f5f9); padding: .25rem; border-radius: 10px; margin-bottom: 1.25rem; }
.an-tab { border: none; background: transparent; padding: .45rem 1.1rem; border-radius: 7px; font-size: .85rem; font-weight: 700; color: var(--ink-mute); cursor: pointer; transition: all .15s; }
.an-tab:hover { color: var(--ink); }
.an-tab.active { background: var(--accent); color: #fff; box-shadow: 0 1px 3px rgba(37,99,235,.3); }
.an-controls { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .75rem; margin-bottom: 1.1rem; }
.an-field { display: flex; flex-direction: column; gap: .25rem; font-size: .72rem; font-weight: 700; color: var(--ink-mute); text-transform: uppercase; letter-spacing: .04em; }
.an-select { padding: .45rem .6rem; border: 1.5px solid var(--line); border-radius: 8px; font-size: .85rem; font-weight: 500; color: var(--ink); background: var(--bg, #fff); min-width: 200px; text-transform: none; letter-spacing: 0; }
.an-select:focus { outline: none; border-color: var(--accent); }
.an-grid2 { display: grid; grid-template-columns: 1.6fr 1fr; gap: 1rem; margin-bottom: 1rem; }
@media (max-width: 900px) { .an-grid2 { grid-template-columns: 1fr; } .an-header { flex-direction: column; } }
.an-card { background: var(--bg, #fff); border: 1px solid var(--line); border-radius: 12px; padding: 1rem 1.1rem; margin-bottom: 1rem; box-shadow: 0 1px 2px rgba(0,0,0,.04); }
.an-card-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap; margin-bottom: .75rem; }
.an-card-head h3 { font-size: .92rem; font-weight: 800; color: var(--ink); margin: 0; }
.an-hint { font-size: .74rem; color: var(--ink-mute); }
.an-badge { font-size: .75rem; font-weight: 700; padding: .2rem .6rem; border: 1.5px solid var(--accent); color: var(--accent); border-radius: 20px; }
.an-stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: .55rem; }
.an-stat { background: var(--bg-alt, #f8fafc); border: 1px solid var(--line); border-radius: 8px; padding: .5rem .6rem; display: flex; flex-direction: column; gap: .1rem; }
.an-stat-label { font-size: .68rem; font-weight: 700; color: var(--ink-mute); text-transform: uppercase; letter-spacing: .03em; }
.an-stat-value { font-size: .95rem; font-weight: 800; color: var(--ink); }
.an-table-wrap { overflow-x: auto; }
.bm-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.an-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
.an-table th { text-align: left; font-size: .72rem; font-weight: 700; color: var(--ink-mute); text-transform: uppercase; letter-spacing: .03em; padding: .5rem .6rem; border-bottom: 2px solid var(--line); white-space: nowrap; }
.an-table td { padding: .5rem .6rem; border-bottom: 1px solid var(--line); color: var(--ink); }
.an-table tbody tr:hover { background: var(--bg-alt, #f8fafc); }

/* ============================================================
   Tarjeta unificada de test / asistente (asx-*)
   Formato Y-Balance / Tests de Salto para TODOS los tests.
   ============================================================ */
.asx-card { background: var(--bg-alt, #f8fafc); border: 1px solid var(--line); border-radius: var(--r-sm, 8px); padding: .65rem .8rem; margin-bottom: .7rem; }
.asx-title { margin: 0 0 .15rem; font-size: .85rem; font-weight: 700; color: var(--ink); }
.asx-ref { margin: 0 0 .45rem; font-size: .72rem; color: var(--ink-mute); line-height: 1.5; }
.asx-empty { margin: 0 0 .55rem; font-size: .8rem; color: var(--ink-mute); font-style: italic; }
.asx-result { margin-bottom: .55rem; font-size: .82rem; }
.asx-result .asx-row { display: flex; align-items: baseline; gap: .45rem; padding: .14rem 0; }
.asx-result .asx-k { color: var(--ink-mute); font-size: .8rem; }
.asx-result .asx-v { font-weight: 700; color: var(--ink); }
.asx-result .asx-interp { margin-top: .15rem; }
.asx-actions { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }

/* ============================================================
   F1 — Barra de pestañas del portal del paciente
   Solo se muestra si el <body> tiene .tabbar-on (lo pone App.onLogin
   cuando el rol es 'patient') Y estamos en ancho de móvil/tablet: en
   escritorio la barra lateral ya cumple esa función y duplicarla
   confundiría. Es <nav> fuera de .main-area para que quede fija sobre
   el contenido sin heredar su scroll.
   ============================================================ */
.tabbar { display: none; }

/* R88: los dos juegos de pestañas (staff / paciente) viven en el mismo
   <nav>; se muestra el del rol activo. Fuera del media query para que
   valga también si alguna vez se enseñan en pantallas anchas. */
body:not(.tabbar-staff) .tabbar-item[data-tabbar-role="staff"],
body:not(.tabbar-patient) .tabbar-item[data-tabbar-role="patient"] { display: none !important; }

@media (max-width: 1023px) {
  body.tabbar-on .tabbar {
    display: grid;
    /* El staff tiene 5 pestañas y el paciente 4: la cuenta la fija
       App.onLogin en --tabbar-cols al saber el rol. */
    grid-template-columns: repeat(var(--tabbar-cols, 4), 1fr);
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 60;                      /* sobre el contenido, bajo modales */
    background: var(--bg-2);
    border-top: 1px solid var(--line);
    /* Área táctil real + respeto por la barra gestual del teléfono
       (requiere viewport-fit=cover en el meta viewport). */
    padding-bottom: env(safe-area-inset-bottom, 0px);
    box-shadow: 0 -1px 3px rgba(0,0,0,.04);
  }
  .tabbar-item {
    background: none;
    border: none;
    padding: .45rem .25rem .5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .15rem;
    cursor: pointer;
    color: var(--ink-mute);
    font-family: inherit;
    font-size: .66rem;
    font-weight: 600;
    line-height: 1.2;
    min-height: 50px;                 /* objetivo táctil cómodo */
    -webkit-tap-highlight-color: transparent;
    transition: color .15s;
  }
  .tabbar-item svg { width: 21px; height: 21px; }
  .tabbar-item.active { color: var(--accent); }
  .tabbar-item.active svg { stroke-width: 2.1; }

  /* El contenido necesita espacio para que la última tarjeta no quede
     debajo de la barra. */
  body.tabbar-on .content-area {
    padding-bottom: calc(58px + env(safe-area-inset-bottom, 0px));
  }
  /* Con pestañas, el botón hamburguesa del topbar sobra para el
     paciente: todo su menú ya está abajo. */
  body.tabbar-on .topbar-menu-btn { display: none; }
}

/* ============================================================
   F1 — Esqueletos de carga (skeleton screens)
   ============================================================ */
.skel {
  background: linear-gradient(90deg, var(--bg-alt) 25%, var(--line-faint) 37%, var(--bg-alt) 63%);
  background-size: 400% 100%;
  animation: skel-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-sm);
}
.skel-line { display: block; }
.skel-card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: .9rem 1rem;
}
@keyframes skel-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
/* Windows trae "reducir movimiento" activado con más frecuencia de lo
   que se supone: el esqueleto tiene que seguir siendo visible sin la
   animación, no desaparecer. */
@media (prefers-reduced-motion: reduce) {
  .skel { animation: none; background: var(--bg-alt); }
}

/* ============================================================
   F1 — Lista de accesos de la pantalla "Más"
   ============================================================ */
.portal-more-group { padding: 0; overflow: hidden; }
.portal-more-item {
  width: 100%;
  background: none;
  border: none;
  border-bottom: 1px solid var(--line);
  padding: .85rem 1rem;
  display: flex;
  align-items: center;
  gap: .7rem;
  font-family: inherit;
  font-size: .9rem;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
  transition: background .12s;
}
.portal-more-item:last-child { border-bottom: none; }
.portal-more-item:hover { background: var(--bg-hover); }

/* ============================================================
   F2 — Modo sesión (runner)
   Pantalla enfocada: mientras dura, el <body> lleva .runner-on y se
   apagan barra lateral, topbar y pestañas. La sesión ocupa la pantalla
   entera, como en una app nativa, para no navegar fuera sin querer.
   ============================================================ */
body.runner-on .sidebar,
body.runner-on .topbar,
body.runner-on .tabbar,
body.runner-on .sidebar-overlay { display: none !important; }
/* R148: tambien el ancho maximo. .content-area lo limita a 1440px para
   que las tablas del resto de la app no se estiren, pero el modo sesion
   dice ocupar "la pantalla entera": sin quitarlo, en un monitor ancho la
   barra azul y la navegacion se cortaban a 1440 y quedaba una franja
   muerta a la derecha. Lo que SI se queda centrado y limitado es el
   contenido interior (1080px, ver R147); eso es deliberado. */
body.runner-on .content-area { padding: 0 !important; max-width: none !important; }
body.runner-on .main-area { margin-left: 0 !important; }

.runner { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; background: var(--bg-2); }

.runner-top {
  display: flex; align-items: center; gap: .5rem;
  background: var(--accent); color: #fff;
  padding: calc(.6rem + env(safe-area-inset-top, 0px)) .8rem .6rem;
  position: sticky; top: 0; z-index: 10;
}
.runner-x {
  background: rgba(255,255,255,.16); border: none; color: #fff;
  width: 30px; height: 30px; border-radius: 50%; font-size: .95rem;
  cursor: pointer; flex-shrink: 0; line-height: 1;
}
.runner-top-mid { flex: 1; min-width: 0; text-align: center; }
.runner-clock { font-size: 1.28rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; letter-spacing: .01em; }
.runner-sub { font-size: .7rem; opacity: .85; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.runner-count { font-size: .8rem; font-weight: 600; opacity: .9; flex-shrink: 0; font-variant-numeric: tabular-nums; }

.runner-prog { height: 3px; background: rgba(37,99,235,.18); }
.runner-prog i { display: block; height: 100%; background: var(--accent); transition: width .25s ease; }

/* R146: 6rem eran un hueco reservado para la barra de descanso que casi
   nunca aparecia. Ahora ahi vive la navegacion fija, que si esta siempre. */
.runner-body { flex: 1; padding: 1rem .9rem 5rem; }

.runner-ex { display: flex; gap: .7rem; align-items: center; margin-bottom: .5rem; }
/* R76: la miniatura pasa de 62px a 96px (y a 112px en pantallas con
   sitio). Entrenando se mira de reojo y a un brazo de distancia; a 62px
   no se distinguía la posición del ejercicio, que es justo para lo que
   está la imagen. Los <video> en bucle usan la misma clase. *//* ══════════════════════════════════════════════════════════════════
   R146 — El video como protagonista
   ------------------------------------------------------------------
   Casi todos los ejercicios son videos cortos de modelos anatomicos 3D.
   Se cargaban y se reproducian en bucle... dentro de 96px recortados a
   cuadro (y a 40px en biseries). Se pagaba el coste del video y se tiraba
   su valor: a ese tamano no se distingue un patron de movimiento.

   Ahora ocupa el ancho completo y con `contain`, que NO recorta: una
   figura de cuerpo entero perdia cabeza y pies con `cover`.

   Y se colapsa solo al escribir (ver .runner-escribiendo). Asi no hay que
   elegir entre verlo grande y llegar a los campos: quien no conoce el
   ejercicio lo mira, y quien ya lo sabe lo despacha con el mismo toque
   que iba a dar para anotar.
   ══════════════════════════════════════════════════════════════════ */
.runner-cabecera { display: flex; flex-direction: column; gap: .55rem; margin-bottom: .5rem; }
.runner-hero {
  width: 100%; aspect-ratio: 1 / 1; max-height: 46vh;
  border-radius: 12px; overflow: hidden; background: var(--bg-alt);
  position: relative; flex-shrink: 0; cursor: pointer;
}
.runner-hero > img, .runner-hero > video {
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.runner-hero-vacio { width: 100%; height: 100%; }
/* R157: cuando el video se reproduce aqui no hay nada que abrir, asi que
   tampoco debe parecer pulsable. */
.runner-hero-quieto { cursor: default; }

/* Selector de ejercicio en biseries y circuitos. Un solo video grande con
   letras debajo, en vez de N videos pequenos: asi el video mide lo mismo
   tenga el grupo dos ejercicios o cinco. */
.runner-hero-sel { display: flex; gap: .3rem; }
.runner-hero-sel button {
  flex: 1 1 0; min-width: 0; cursor: pointer;
  font-size: .68rem; font-weight: 600; font-family: inherit;
  padding: .32rem .3rem; border-radius: 7px;
  background: var(--bg-alt); color: var(--ink-mute);
  border: 1px solid transparent;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.runner-hero-sel button.on {
  background: var(--accent-glow); color: var(--accent); border-color: var(--accent);
}

/* ══════════════════════════════════════════════════════════════════
   R158 — El selector ES la leyenda (biseries y circuitos)
   ------------------------------------------------------------------
   El nombre del ejercicio salia SEIS veces en una pantalla: dos en las
   pestanas del selector (recortado), dos en un titulo que unia los
   nombres con " + ", y dos mas en la leyenda con miniaturas de 40px.
   Con una biserie del mismo ejercicio a dos brazos, el titulo decia
   literalmente lo mismo dos veces y ocupaba tres lineas.

   Ahora es una sola lista. Se lee entero (no recortado), que era lo que
   permitia distinguir "Brazo derecho" de "Brazo izquierdo".
   ══════════════════════════════════════════════════════════════════ */
.runner-sel-lista { background: var(--bg-alt); border-radius: 10px; padding: .25rem; }
.runner-sel-fila {
  display: flex; gap: .55rem; align-items: flex-start; width: 100%;
  padding: .45rem .5rem; border: 0; border-radius: 8px;
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.runner-sel-fila + .runner-sel-fila { border-top: 1px solid var(--line); }
.runner-sel-fila.on { background: var(--accent-glow); border-top-color: transparent; }
.runner-sel-letra {
  width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0; margin-top: 1px;
  background: var(--line-strong); color: var(--ink-soft);
  font-size: .62rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.runner-sel-fila.on .runner-sel-letra { background: var(--accent); color: #fff; }
.runner-sel-txt { min-width: 0; flex: 1; }
.runner-sel-nombre { display: block; font-size: .8rem; font-weight: 600; line-height: 1.25; color: var(--ink-mute); }
.runner-sel-fila.on .runner-sel-nombre { color: var(--ink); }
.runner-sel-presc { display: block; font-size: .72rem; color: var(--ink-mute); margin-top: .05rem; }
.runner-sel-nota {
  display: block; font-size: .72rem; color: var(--ink-mute);
  border-left: 2px solid var(--line-strong); padding-left: .38rem; margin-top: .15rem;
}
/* Las acciones solo bajo la fila activa: son del ejercicio que se ve. */
.runner-sel-acc { padding: 0 .5rem .45rem 3rem; }
.runner-info-grupo { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.runner-info-grupo .runner-ex-meta { margin: 0; }

/* ══════════════════════════════════════════════════════════════════
   R159 — "Que significa este numero" (RIR / RPE)
   ------------------------------------------------------------------
   El valor prescrito es el boton. La duda aparece en el runner, a mitad
   de una serie: un glosario en otra seccion no lo abriria nadie.
   ══════════════════════════════════════════════════════════════════ */
.int-chip {
  display: inline-flex; align-items: center; gap: .2rem;
  background: var(--accent-glow); color: var(--accent);
  border: 1px solid var(--accent-soft, #bfdbfe); border-radius: 99px;
  padding: .04rem .42rem; font: inherit; font-weight: 600; font-size: .95em;
  cursor: pointer; vertical-align: baseline;
}
.int-chip-q {
  width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0;
  background: var(--accent); color: #fff; font-size: .55rem; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.int-ficha { font-size: .88rem; line-height: 1.55; color: var(--ink-soft); }
.int-ficha p { margin: 0 0 .6rem; }
.int-ficha strong { color: var(--ink); }
.int-esc-tit { font-size: .74rem; color: var(--ink-mute); margin-bottom: .5rem; }
/* La escala entera, para leer el valor como POSICION y no como cifra. */
.int-esc { display: flex; gap: 2px; margin: .2rem 0 .15rem; }
.int-esc span {
  flex: 1; height: 24px; display: flex; align-items: center; justify-content: center;
  font-size: .62rem; font-weight: 700; color: #fff;
}
.int-esc span:first-child { border-radius: 5px 0 0 5px; }
.int-esc span:last-child  { border-radius: 0 5px 5px 0; }
.int-esc-pies {
  display: flex; justify-content: space-between;
  font-size: .68rem; color: var(--ink-mute); margin-bottom: .7rem;
}
.int-comp {
  background: var(--accent-glow); border-radius: 8px; padding: .55rem .7rem;
  font-size: .84rem; line-height: 1.5; color: #1e3a8a;
}
.int-comp strong { display: block; margin-bottom: .15rem; color: #1e40af; }
/* Solo en RPE: separa esfuerzo de dolor. */
.int-aviso {
  background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px;
  padding: .55rem .7rem; font-size: .84rem; line-height: 1.5; color: #78350f;
}
.int-aviso strong { color: #78350f; }

/* Escribiendo: la cabecera pasa a fila y el video a miniatura, de modo que
   las series suben por encima del teclado. */
.runner-escribiendo .runner-cabecera { flex-direction: row; align-items: center; gap: .7rem; }
.runner-escribiendo .runner-hero { width: 64px; height: 64px; aspect-ratio: auto; }
.runner-escribiendo .runner-hero-sel { display: none; }
.runner-escribiendo .runner-ex-name { font-size: .92rem; }

/* ══════════════════════════════════════════════════════════════════
   R147 — Tablet y escritorio
   ------------------------------------------------------------------
   El runner no tenia diseno para pantalla grande: era el de movil
   estirado a lo que midiera la ventana. En un monitor ancho las filas
   de serie se estiraban entera, con el "30 seg" en un extremo y el
   "lastre / kg / marcar" en el otro, a metro y medio de distancia. Y el
   video de R146, al ocupar el 100% de 1900px, dejaba la figura diminuta
   en el centro de una franja enorme: peor que la miniatura anterior.

   Se resuelve con DOS puntos de corte y un solo diseno:

     < 900px   una columna (movil y tablet vertical), con ancho maximo
               para que el video no se desmadre en tablet.
     >= 900px  dos columnas: video a la izquierda y FIJO al desplazar,
               datos y series a la derecha.

   El video fijo no es un adorno: esto se usa acompanando al paciente,
   y en un circuito de 4 ejercicios x 3 rondas son doce filas. Al bajar
   a la ronda 2 se perdia el video -- y con el, lo que el paciente esta
   mirando.
   ══════════════════════════════════════════════════════════════════ */
.runner-media { min-width: 0; }

/* Movil ancho y tablet vertical: una columna, pero sin dejar que el
   video crezca hasta ocupar toda la pantalla. */
@media (min-width: 560px) and (max-width: 899px) {
  /* `width:100%` NO sobra: .runner-body es hijo de un flex en columna, y
     ahi unos margenes automaticos en el eje transversal impiden que el
     elemento se estire -- se encoge al contenido. Sin esta linea el
     bloque salia a 550px en una ventana de 980. */
  .runner-body { width: 100%; max-width: 520px; margin-left: auto; margin-right: auto; }
}

@media (min-width: 900px) {
  .runner-body {
    display: grid;
    grid-template-columns: 46% 1fr;
    column-gap: 1.4rem;
    align-content: start;
    width: 100%;   /* ver la nota del bloque de tablet sobre margin auto */
    max-width: 1080px;
    margin-left: auto; margin-right: auto;
    padding-bottom: 5.5rem;
  }
  /* La cabecera deja de ser una caja: sus dos hijos pasan a ser celdas
     de la rejilla, uno en cada columna. */
  .runner-cabecera { display: contents; }

  .runner-media {
    grid-column: 1; grid-row: 1 / span 99;
    position: sticky; top: 1rem; align-self: start;
  }
  .runner-info { grid-column: 2; }
  /* Todo lo demas (notas, series, controles) a la derecha. */
  .runner-body > *:not(.runner-cabecera) { grid-column: 2; }

  /* Aqui el video no compite con el teclado: hay raton. Que se encogiera
     al escribir seria molesto, justo lo contrario de lo que busca. */
  .runner-escribiendo .runner-cabecera { display: contents; }
  .runner-escribiendo .runner-hero { width: 100%; height: auto; aspect-ratio: 1 / 1; }
  .runner-escribiendo .runner-hero-sel { display: flex; }

  .runner-hero { max-height: none; }
  .runner-set { padding: .5rem .7rem; }
  .runner-nav { padding-left: max(.9rem, calc((100% - 1080px) / 2));
                padding-right: max(.9rem, calc((100% - 1080px) / 2)); }
}

.runner-ex-img {
  width: 96px; height: 96px; border-radius: 12px;
  object-fit: cover; flex-shrink: 0; cursor: pointer; background: var(--bg-alt);
}
@media (min-width: 480px) { .runner-ex-img { width: 112px; height: 112px; } }
.runner-ex-name { font-size: 1.06rem; font-weight: 700; line-height: 1.25; margin: 0; }
.runner-ex-meta { font-size: .76rem; color: var(--ink-mute); margin-top: .15rem; }
.runner-notes {
  font-size: .8rem; color: var(--ink-soft); background: var(--bg-alt);
  border-radius: var(--r-md); padding: .5rem .65rem; margin-bottom: .8rem;
}

.runner-sets { display: flex; flex-direction: column; gap: .4rem; margin: .9rem 0 1.2rem; }
.runner-set {
  display: grid; grid-template-columns: 26px 1fr 78px auto 42px;
  align-items: center; gap: .5rem;
  background: var(--bg-alt); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: .5rem .6rem;
  transition: background .15s, border-color .15s;
}
.runner-set.done { background: var(--green-light); border-color: transparent; }
.runner-set-n { font-size: .78rem; font-weight: 700; color: var(--ink-mute); text-align: center; }
/* R76: las reps pasaron de texto fijo a campo editable. La celda es
   flex para que el input y la palabra "reps" queden en la misma línea
   sin romper las 5 columnas del grid. */
.runner-set-reps {
  display: flex; align-items: center; gap: .3rem; min-width: 0;
  font-size: .9rem; font-weight: 600; font-variant-numeric: tabular-nums;
}
.runner-set-reps i { font-style: normal; font-size: .72rem; font-weight: 400; color: var(--ink-mute); }
.runner-set-repsi {
  width: 46px; padding: .4rem .3rem; text-align: center;
  border: 1.5px solid var(--line-strong); border-radius: var(--r-sm);
  font-family: inherit; font-size: .92rem; font-weight: 600;
  background: var(--bg-2); color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* Marcada = solo lectura, igual que el peso: el registro queda
   congelado y para corregirlo hay que desmarcar la serie. */
.runner-set-repsi:read-only { border-color: transparent; background: transparent; }
/* ============================================================
   R80 — Mapa anatómico de la sesión (plataforma del profesional)
   ============================================================ */
.ses-map {
  display: flex; align-items: center; gap: .9rem;
  padding: .7rem .85rem; margin: .5rem 0 0;
  border-top: 1px solid var(--line);
}
.ses-map-fig { flex-shrink: 0; width: 92px; }
.ses-map-fig svg { width: 100%; height: auto; display: block; }
.ses-map-info { min-width: 0; flex: 1; }
.ses-map-title {
  font-size: .72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-mute); margin-bottom: .15rem;
}
.ses-map-meta { font-size: .75rem; color: var(--ink-mute); margin-bottom: .4rem; }
.ses-map-chips { display: flex; flex-wrap: wrap; gap: .25rem; }
.ses-map-chip {
  font-size: .68rem; font-weight: 600; padding: .1rem .4rem;
  border-radius: var(--r-sm); background: var(--bg-2); color: var(--ink-mute);
  border: 1px solid var(--line);
}
.ses-map-chip--more { background: transparent; }
@media (max-width: 560px) {
  .ses-map { flex-direction: column; align-items: flex-start; }
  .ses-map-fig { width: 76px; align-self: center; }
}

/* Carga acumulada por grupo muscular (aside de Planificación) */
.ml-ctl { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .4rem; }
.ml-seg { display: flex; border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.ml-btn {
  font-size: .68rem; font-weight: 600; padding: .22rem .5rem;
  background: transparent; border: none; cursor: pointer;
  color: var(--ink-mute); white-space: nowrap;
}
.ml-btn.on { background: var(--accent); color: #fff; }
.ml-list { display: flex; flex-direction: column; gap: .3rem; }
.ml-row { display: grid; grid-template-columns: 62px 1fr 32px; align-items: center; gap: .4rem; }
.ml-row-label { font-size: .72rem; color: var(--ink-mute); }
.ml-row-bar { height: 6px; border-radius: 99px; background: var(--bg-2); overflow: hidden; }
.ml-row-bar i { display: block; height: 100%; background: var(--accent); border-radius: 99px; }
.ml-row-val { font-size: .72rem; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }

/* R79 — momento del registro de dolor en la tabla de Mi Progreso.
   Los colores son los mismos de la gráfica, para que la etiqueta y el
   punto de la curva se lean como lo mismo. */
.log-ctx {
  display: inline-block; font-size: .64rem; font-weight: 700;
  padding: .1rem .35rem; border-radius: var(--r-sm);
  text-transform: uppercase; letter-spacing: .03em; vertical-align: 1px;
}
.log-ctx--rest { background: #10b98120; color: #10b981; }
.log-ctx--post { background: #f59e0b20; color: #b45309; }

/* R78 — cronómetro de serie (ejercicios prescritos en segundos).
   Vive dentro de .runner-set-reps para no romper las 5 columnas. */
.runner-set-timer {
  width: 28px; height: 28px; flex-shrink: 0; padding: 0;
  border: 1.5px solid var(--accent); border-radius: 50%;
  background: transparent; color: var(--accent); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s, color .15s;
}
.runner-set-timer:hover { background: var(--accent); color: #fff; }
.runner-set-timer.running {
  border-color: var(--red); color: #fff; background: var(--red);
}
/* La cuenta atrás sustituye al campo mientras corre: se lee de lejos
   y con el brazo ocupado. */
.runner-set-count {
  flex: 1; min-width: 0; text-align: center;
  font-size: 1.15rem; font-weight: 700; color: var(--accent);
  font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.runner-set.timing { border-color: var(--accent); background: var(--bg-2); }

.runner-set-peso {
  width: 100%; padding: .4rem .45rem; text-align: center;
  border: 1.5px solid var(--line-strong); border-radius: var(--r-sm);
  font-family: inherit; font-size: .92rem; font-weight: 600;
  background: var(--bg-2); color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.runner-set-peso:read-only { border-color: transparent; background: transparent; }
.runner-set-unit { font-size: .72rem; color: var(--ink-mute); }

/* ════════════════════════════════════════════════════════════════════
   R185 — Filas de serie sin carga
   --------------------------------------------------------------------
   La rejilla base es `26px 1fr 78px auto 42px` (n, reps, carga, unidad,
   palomita). Los elementos ocultos con `hidden` valen `display:none` y
   por tanto DEJAN DE SER items de la rejilla: no hace falta reordenar
   nada, solo redefinir el reparto que queda.

     · rs-sin-carga  -> se van carga Y unidad. Quedan 3 items.
     · rs-sin-unidad -> se va solo la unidad ('pc' sin lastre). Quedan 4.
   ════════════════════════════════════════════════════════════════════ */
.runner-set-carga { min-width: 0; }
.runner-set.rs-sin-carga  { grid-template-columns: 26px 1fr 42px; }
.runner-set.rs-sin-unidad { grid-template-columns: 26px 1fr 78px 42px; }

/* Un enlace, no un boton con marco: es una salida opcional para el caso
   raro (dominadas con disco), y darle presencia de control lo devolveria
   justo al problema que R185 venia a quitar -- algo que pide ser usado. */
.runner-set-lastre {
  width: 100%; padding: .4rem .2rem;
  background: none; border: none; cursor: pointer;
  font-family: inherit; font-size: .78rem; font-weight: 600;
  color: var(--ink-ghost); white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
}
.runner-set-lastre:hover { color: var(--accent); }
.runner-set-check {
  width: 34px; height: 34px; border-radius: 50%;
  border: 2px solid var(--line-strong); background: var(--bg-2);
  color: transparent; cursor: pointer; display: flex;
  align-items: center; justify-content: center; padding: 0;
  -webkit-tap-highlight-color: transparent;
  transition: background .15s, border-color .15s, color .15s;
}
.runner-set.done .runner-set-check { background: var(--green); border-color: var(--green); color: #fff; }

/* R146: fija al pie. Antes iba con el contenido y con el video grande se
   salia de pantalla. Ademas ocupa el `padding-bottom` que .runner-body ya
   reservaba para la barra de descanso -- un hueco que estaba en blanco
   siempre que esa barra no estuviera, o sea casi siempre. La barra de
   descanso tiene z-index mayor y la tapa cuando aparece, que es cuando no
   estas navegando. */
.runner-nav {
  display: flex; gap: .6rem; z-index: 10;
  position: fixed; left: 0; right: 0; bottom: 0;
  padding: .55rem .9rem calc(.55rem + env(safe-area-inset-bottom, 0px));
  background: var(--bg); border-top: 1px solid var(--line);
}
.runner-nav .btn { flex: 1; padding: .7rem; font-size: .9rem; }

/* Barra de descanso — fija al pie, sobre el contenido */
.runner-rest {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  display: flex; align-items: center; gap: .5rem;
  background: var(--accent-light); border-top: 1.5px solid var(--accent);
  padding: .6rem .8rem calc(.6rem + env(safe-area-inset-bottom, 0px));
}
.runner-rest button {
  background: var(--bg-2); border: 1px solid var(--accent);
  color: var(--accent); border-radius: var(--r-sm);
  padding: .38rem .55rem; font-size: .76rem; font-weight: 600;
  font-family: inherit; cursor: pointer; flex-shrink: 0;
}
.runner-rest-mid { flex: 1; text-align: center; line-height: 1.15; }
.runner-rest-mid span { display: block; font-size: .66rem; color: var(--ink-mute); }
.runner-rest-mid b { font-size: 1.15rem; color: var(--accent); font-variant-numeric: tabular-nums; }
.runner-rest-skip { background: var(--accent) !important; color: #fff !important; }

/* Llamada a la acción en "Hoy" */
.runner-cta { margin-bottom: .5rem; padding: .8rem; font-size: .95rem; font-weight: 700; }
.runner-cta-note { font-size: .74rem; color: var(--ink-mute); text-align: center; margin: -.25rem 0 .6rem; }
.linkish {
  background: none; border: none; padding: 0; font: inherit;
  color: var(--accent); text-decoration: underline; cursor: pointer;
}

/* ============================================================
   F3 — Resumen de cierre de sesión
   ============================================================ */
.sum-wrap { max-width: 460px; margin: 0 auto; padding-bottom: 1.5rem; }
.sum-hero { text-align: center; padding: 1.4rem 0 1rem; }
.sum-emoji { font-size: 2.6rem; line-height: 1; }
.sum-title { font-size: 1.4rem; font-weight: 800; margin: .5rem 0 .15rem; }
.sum-sub { font-size: .88rem; color: var(--ink-mute); }

.sum-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.sum-stat {
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: .8rem .4rem; text-align: center;
}
.sum-stat b { display: block; font-size: 1.18rem; font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; }
.sum-stat span { font-size: .68rem; color: var(--ink-mute); text-transform: uppercase; letter-spacing: .03em; }

.sum-compare { text-align: center; font-size: .82rem; color: var(--ink-mute); margin-top: .6rem; }

.sum-prs {
  margin-top: .8rem; background: var(--amber-light);
  border: 1px solid var(--amber); border-radius: var(--r-lg); padding: .7rem .85rem;
}
.sum-prs-title {
  font-size: .7rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: #92400e; margin-bottom: .35rem;
}
.sum-pr { font-size: .85rem; color: var(--ink); padding: .12rem 0; }
.sum-pr span { color: var(--ink-mute); font-size: .78rem; }

/* R73 — cardio hecho, listado aparte de las series en el resumen */
.sum-cardio {
  margin-top: .8rem; background: var(--bg-alt);
  border: 1px solid var(--line); border-radius: var(--r-lg); padding: .7rem .85rem;
}
.sum-cardio-row { font-size: .85rem; color: var(--ink); padding: .12rem 0; }
.sum-cardio-row span { color: var(--ink-mute); font-size: .78rem; }

/* R74 — biserie/circuito como UN paso del modo sesión */
.runner-group-badge {
  display: inline-block; font-size: .64rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  background: var(--accent-glow); color: var(--accent);
  border-radius: 999px; padding: .12rem .5rem; margin-bottom: .3rem;
}
.runner-group-legend {
  display: flex; flex-direction: column; gap: .5rem;
  margin: .75rem 0 .25rem; padding: .6rem .7rem;
  background: var(--bg-alt); border-radius: var(--r-lg);
}
.runner-legend-row { display: flex; align-items: center; gap: .55rem; }
.runner-legend-letter {
  flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent); color: #fff; font-size: .72rem; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
}
.runner-legend-img {
  flex-shrink: 0; width: 40px; height: 40px; border-radius: 8px;
  object-fit: cover; cursor: pointer; background: var(--bg-alt);
}
.runner-legend-name {
  font-size: .84rem; font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* R77 — prescripción del ejercicio dentro de la leyenda de una biserie
   ("10-12 reps", "30 seg"). Secundaria al nombre, pero legible. */
.runner-legend-presc {
  font-size: .72rem; color: var(--ink-mute); margin-top: .1rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* R91: la nota del ejercicio dentro de su fila de la biserie. A
   diferencia de la prescripción NO se recorta con puntos suspensivos:
   "Brazo derecho" es justo lo que hay que leer para no confundir A con
   B, y cortarlo lo dejaría inservible. */
.runner-legend-note {
  font-size: .74rem; color: var(--ink-soft);
  margin-top: .2rem; line-height: 1.3;
  border-left: 2px solid var(--line); padding-left: .4rem;
}
.runner-round { margin-bottom: .8rem; }
.runner-round-title {
  font-size: .68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-mute); margin-bottom: .3rem;
}

/* R75 — acciones por ejercicio migradas desde la lista */
.runner-acts { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: .3rem; }
.runner-act {
  cursor: pointer; font-size: .7rem; font-weight: 600; color: var(--accent);
  white-space: nowrap; user-select: none;
}
/* R116: "ya hay comentario" se pintaba en GRIS APAGADO, que se lee como
   deshabilitado -- justo lo contrario de una confirmación. Ahora es verde,
   el mismo color con el que la app marca lo hecho. */
.runner-act.has { color: #10b981; font-weight: 600; }
.runner-video {
  display: inline-block; font-size: .72rem; font-weight: 500;
  color: var(--accent); text-decoration: none; margin-top: .2rem;
}
.runner-subst { font-size: .72rem; color: var(--accent); margin-top: .2rem; }
.runner-subst span { cursor: pointer; text-decoration: underline; }
/* R76 — selector de unidad kg|lb junto al ejercicio.
   Antes la unidad era un texto clicable dentro de cada fila: se leía
   como etiqueta y nadie descubría que se podía tocar. Ahora es un
   segmentado con la opción activa resaltada. */
.runner-unit-sw {
  display: inline-flex; margin-left: auto; background: var(--bg-alt);
  border-radius: 999px; padding: .12rem; flex-shrink: 0;
}
.runner-unit-op {
  border: 0; background: transparent; cursor: pointer;
  padding: .18rem .5rem; border-radius: 999px;
  font-size: .68rem; font-weight: 700; color: var(--ink-mute); line-height: 1.2;
}
.runner-unit-op.on { background: var(--bg-2); color: var(--accent); box-shadow: var(--shadow-sm); }

/* R76 — agregar / quitar series (o rondas) durante la sesión */
.runner-sets-ctl { display: flex; gap: .4rem; margin-top: .5rem; }
.runner-set-add {
  flex: 1; border: 1px dashed var(--line-strong); background: transparent;
  cursor: pointer; padding: .5rem; border-radius: var(--r-md);
  font-size: .76rem; font-weight: 600; color: var(--ink-mute);
}
.runner-set-add:hover { border-color: var(--accent); color: var(--accent); }
.runner-set-del {
  width: 42px; border: 1px solid var(--line); background: var(--bg-2);
  cursor: pointer; border-radius: var(--r-md);
  font-size: 1rem; font-weight: 700; color: var(--ink-mute); line-height: 1;
}

/* R73 — paso de cardio dentro del modo sesión */
.runner-cardio { margin: .9rem 0 1.2rem; }
.runner-cardio-done {
  font-size: .85rem; font-weight: 700; color: var(--green);
  text-align: center; margin-bottom: .6rem;
}
.runner-cardio-detail {
  margin-top: .9rem; border-top: 1px solid var(--line); padding-top: .8rem;
}

/* ============================================================
   F4 — Mapa corporal y contexto de sesión
   ============================================================ */
.bm-card { display: flex; gap: .9rem; align-items: center; margin-bottom: .6rem; padding: .8rem .9rem; }
.bodymap { display: flex; gap: .15rem; flex-shrink: 0; }
.bodymap svg { display: block; }
.bm-info { min-width: 0; flex: 1; }
.bm-meta { font-size: .78rem; color: var(--ink-mute); font-weight: 600; margin-bottom: .35rem; }
.bm-chips { display: flex; flex-wrap: wrap; gap: .25rem; }
.bm-chip {
  font-size: .7rem; font-weight: 600; padding: .18rem .45rem;
  border-radius: 999px; background: var(--accent-light); color: var(--accent);
}
.bm-chip--more { background: var(--bg-alt); color: var(--ink-mute); }

/* R76 — Carga por grupo muscular (Mi Progreso) */
.ml-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.ml-toggle { display: flex; gap: .2rem; background: var(--bg-alt); border-radius: var(--r-md); padding: .15rem; }
.ml-tab {
  border: 0; background: transparent; cursor: pointer; padding: .25rem .5rem;
  border-radius: calc(var(--r-md) - .15rem); font-size: .85rem; line-height: 1;
  color: var(--ink-mute);
}
.ml-tab.on { background: var(--bg-2); color: var(--accent); box-shadow: var(--shadow-sm); }
.ml-sub { font-size: .78rem; color: var(--ink-mute); margin: .3rem 0 .6rem; }
.ml-sub strong { color: var(--ink); }
.ml-table { margin-top: .6rem; display: flex; flex-direction: column; gap: .3rem; }
.ml-row { display: grid; grid-template-columns: 1fr 84px 28px; align-items: center; gap: .5rem; }
.ml-name {
  font-size: .76rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ml-bar { height: 6px; border-radius: 999px; background: var(--bg-alt); overflow: hidden; }
.ml-bar i { display: block; height: 100%; background: #b91c1c; opacity: .75; }
.ml-val { font-size: .76rem; font-weight: 700; color: var(--ink); text-align: right; }
.ml-empty { font-size: .7rem; color: var(--ink-ghost); margin-top: .6rem; line-height: 1.45; }
.ml-range { display: flex; gap: .3rem; margin-top: .7rem; }
.ml-range-btn {
  flex: 1; border: 1px solid var(--line); background: var(--bg-2); cursor: pointer;
  padding: .3rem; border-radius: var(--r-md); font-size: .72rem; font-weight: 600;
  color: var(--ink-mute);
}
.ml-range-btn.on { border-color: var(--accent); color: var(--accent); background: var(--accent-light); }
.bm-sec { font-size: .7rem; color: var(--ink-ghost); margin-top: .35rem; line-height: 1.4; }
.bm-streak {
  font-size: .8rem; color: var(--ink-mute); background: var(--green-light);
  border-radius: var(--r-md); padding: .5rem .7rem; margin-bottom: .75rem;
}
.bm-streak strong { color: var(--green); }

/* Selector de músculos por chips (formulario del profesional) */
.mchip-box {
  border: 1.5px solid var(--line); border-radius: var(--r-md);
  padding: .55rem .6rem; background: var(--bg-alt);
  max-height: 190px; overflow-y: auto;
}
.mchip-group + .mchip-group { margin-top: .5rem; }
.mchip-region {
  display: block; font-size: .65rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-ghost); margin-bottom: .25rem;
}
.mchip-row { display: flex; flex-wrap: wrap; gap: .28rem; }
.mchip {
  font-family: inherit; font-size: .74rem; font-weight: 500;
  padding: .26rem .5rem; border-radius: 999px; cursor: pointer;
  border: 1.5px solid var(--line-strong); background: var(--bg-2);
  color: var(--ink-soft); transition: background .12s, color .12s, border-color .12s;
}
.mchip:hover { border-color: var(--accent); color: var(--accent); }
.mchip.on { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }

/* R106 — porciones del músculo (Pectoral → superior/medio/inferior).
   Aparecen solo con el padre marcado. Se diferencian por PESO VISUAL, no
   por color: más pequeñas, con borde punteado y en línea propia, para que
   se lean como un detalle del chip de arriba y no como músculos nuevos
   compitiendo por atención en la misma rejilla. */
.mchip-subrow {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  width: 100%;
  margin: .15rem 0 .3rem .9rem;
  padding-left: .55rem;
  border-left: 2px solid var(--line);
}
.mchip-subrow[hidden] { display: none; }
.mchip.mchip-sub {
  font-size: .68rem;
  padding: .12rem .45rem;
  border-style: dashed;
}
.mchip.mchip-sub.on { border-style: solid; }

/* ============================================================
   R89 — Aviso de "sin conexión"
   ------------------------------------------------------------
   Franja fija arriba, por encima de todo. Solo aparece con
   body.is-offline (lo pone App._bindConexion). En móvil la barra de
   pestañas vive abajo, así que arriba no estorba nada.
   ============================================================ */
.offline-bar {
  display: none;
  position: fixed; top: 0; left: 0; right: 0; z-index: 3000;
  padding: .45rem .8rem;
  background: #b45309; color: #fff;
  font-size: .78rem; font-weight: 600; text-align: center;
  padding-top: calc(.45rem + env(safe-area-inset-top, 0px));
}
body.is-offline .offline-bar { display: block; }

/* ============================================================
   R93 — Análisis Biomecánico en el celular
   ------------------------------------------------------------
   Medido en un plan real a 375 px: la página hacía scroll lateral
   (387 px de ancho) y los controles se salían por la derecha.

   La causa no era una regla suelta sino PADDINGS ANIDADOS que se
   suman: .content-area (14) + .an-wrap (24) + .an-card (18) dejaban
   262 px útiles de 375 -- un tercio de la pantalla gastado en
   márgenes. Encima, los campos tenían anchos fijos (330 px el
   selector de paciente) que no caben en lo que queda.
   ============================================================ */
@media (max-width: 640px) {
  /* 1. Recuperar el ancho: márgenes mínimos en cada nivel. */
  .an-wrap, .bm-wrap { padding-left: .6rem; padding-right: .6rem; padding-top: .9rem; }
  .an-wrap .an-card, .bm-wrap .an-card { padding-left: .7rem; padding-right: .7rem; }

  /* 2. Los controles se apilan y cada campo ocupa el ancho disponible.
        Sin `min-width: 0` un select con texto largo ("Sentadilla Bipodal
        Frontal") se niega a encogerse y empuja la fila entera fuera. */
  .an-controls { flex-direction: column; align-items: stretch; gap: .6rem; }
  .an-controls > * { width: 100%; min-width: 0; }
  .an-field { width: 100%; min-width: 0; }
  .an-field select, .an-field input, .an-select {
    width: 100%; min-width: 0; max-width: 100%; box-sizing: border-box;
  }

  /* 3. Tablas de resultados: scroll propio en vez de estirar la página.
        Aquí NO se convierten en tarjetas -- son matrices de medidas
        (izquierda/derecha, grados) que solo se leen comparando columnas. */
  .an-card > table, .bm-table { display: block; overflow-x: auto; max-width: 100%; }

  /* 4. Gráficas: el SVG tiene viewBox propio, solo hay que dejar que
        escale al ancho del contenedor en vez de conservar su tamaño. */
  .bm-dynchart { max-width: 100%; overflow-x: auto; }
  .bm-dynchart svg, .an-card svg[viewBox] { max-width: 100%; height: auto; }

  /* 5. Red de seguridad: nada dentro del módulo puede empujar el ancho
        de la página. Si aparece un elemento nuevo con medida fija, se
        recorta con su propio scroll en lugar de descuadrarlo todo. */
  .bm-wrap, .bm-wrap * { max-width: 100%; }
}

/* ============================================================
   R95 — Bajar el volumen: jerarquía de acciones y ficha
   ------------------------------------------------------------
   Regla que se aplica aquí: en una fila de acciones solo UNA
   puede ir en relleno sólido. Cuando compiten tres (azul, rojo y
   el nuevo botón) la mirada no encuentra dónde ir y el conjunto
   se lee como una alarma. El resto baja a contorno, que se sigue
   leyendo como botón pero no grita.
   ============================================================ */

/* Iconos destructivos: grises en reposo, con color solo al apuntarlos.
   Un icono de borrar permanentemente en rojo compite con las acciones
   reales de la fila y además invita al toque accidental. */
.btn-danger-hover { color: var(--ink-ghost); }
.btn-danger-hover:hover:not(:disabled) { color: var(--red); background: var(--red-light); }

/* Interruptor secundario dentro de un formulario (p. ej. "Variar peso y
   reps por serie"). No es un campo más: es una opción sobre el campo que
   tiene encima, así que va en texto pequeño y sin marco -- con marco
   pesaba más que los datos a los que modifica. */
.sheet-toggle {
  display: inline-flex; align-items: center; gap: .4rem;
  margin-top: .4rem;
  font-size: .78rem; font-weight: 400; color: var(--ink-mute);
  cursor: pointer;
}
.sheet-toggle input[type="checkbox"] {
  width: 15px; height: 15px; margin: 0;
  accent-color: var(--accent); cursor: pointer;
}
.sheet-toggle:hover { color: var(--ink-soft); }

/* La microtabla por serie, dentro de la ficha, se separa con un filete
   y una sangría: deja claro que depende del campo Peso y no es una
   sección nueva del formulario. */
#xs-sd-wrap {
  border-left: 2px solid var(--line);
  padding-left: .7rem;
}
#xs-sd-wrap .sd-reps, #xs-sd-wrap .sd-peso { height: 32px; }


/* ============================================================
   R96 — Biomecánica: figura + explicación en el celular
   ------------------------------------------------------------
   El par "captura + texto" se maqueta con una tabla cuya primera
   celda mide 260 px fijos. En escritorio funciona; en 375 px deja
   ~115 px para la explicación y el texto sale a una palabra por
   línea (ilegible).

   Se apila: foto arriba a ancho completo, explicación debajo.
   ============================================================ */
@media (max-width: 640px) {
  .bm-figrow, .bm-figrow tbody, .bm-figrow tr { display: block; width: 100%; }
  .bm-figrow-img, .bm-figrow-txt {
    display: block; width: 100% !important;
    padding-right: 0 !important;
  }
  .bm-figrow-txt { padding-top: .7rem; }
  /* La captura ya no compite con el texto por el alto de pantalla. */
  .bm-figrow .bm-cap img { height: 300px; }
}

/* ============================================================
   R122 — Franja "Instala la app"
   ------------------------------------------------------------
   Va abajo y no arriba a propósito: arriba está la franja de "sin
   conexión", que es urgente y no puede competir con una sugerencia.

   Se sienta SOBRE la barra de pestañas, nunca encima de ella: la
   tabbar mide 58px + el área gestual del teléfono, la misma cuenta
   que usa .content-area para no esconder su última tarjeta.

   z-index 59 = justo por debajo de la tabbar (60), para que si algo
   se solapa gane siempre la navegación.
   ============================================================ */
.pwa-bar {
  display: none;
  position: fixed;
  left: .6rem; right: .6rem;
  bottom: calc(.6rem + env(safe-area-inset-bottom, 0px));
  z-index: 59;
  align-items: center;
  gap: .65rem;
  padding: .6rem .7rem;
  background: var(--bg-2);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: 0 6px 24px rgba(0,0,0,.14);
  /* Entra deslizándose: aparecer de golpe se lee como un fallo de
     dibujado, no como un aviso que llega. */
  animation: pwaBarIn .32s cubic-bezier(.2,.8,.3,1) both;
}
body.pwa-bar-on .pwa-bar { display: flex; }
body.tabbar-on .pwa-bar {
  bottom: calc(58px + .6rem + env(safe-area-inset-bottom, 0px));
}

@keyframes pwaBarIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.pwa-bar-ico {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 9px;
  background: var(--accent-light);
  color: var(--accent);
}
.pwa-bar-ico svg { width: 19px; height: 19px; }

.pwa-bar-txt {
  flex: 1 1 auto;
  min-width: 0;              /* sin esto el texto largo empuja el botón fuera */
  display: flex; flex-direction: column; gap: .1rem;
}
.pwa-bar-txt strong {
  font-size: .82rem; font-weight: 650; color: var(--ink);
  line-height: 1.25;
}
.pwa-bar-txt span {
  font-size: .72rem; color: var(--ink-mute); line-height: 1.3;
}

.pwa-bar .btn { flex: 0 0 auto; }

.pwa-bar-x {
  flex: 0 0 auto;
  background: none; border: none; cursor: pointer;
  padding: .3rem;
  color: var(--ink-mute);
  display: flex; align-items: center;
}
.pwa-bar-x svg { width: 15px; height: 15px; }
.pwa-bar-x:hover { color: var(--ink); }

/* En pantallas muy estrechas el subtítulo se cae antes que el botón:
   el botón es la acción, el subtítulo solo la adorna. */
@media (max-width: 359px) {
  .pwa-bar-txt span { display: none; }
}

/* ============================================================
   R123 — Tamaños de ícono
   ------------------------------------------------------------
   Tres y no ocho. Antes los íconos se pintaban a 11, 12, 13, 14, 15,
   16, 17 y 18 px porque el tamaño se escribía a mano en cada sitio y
   a ojo nunca sale dos veces el mismo número. Ahora lo pone `Icon()`
   con una clase, y cambiar un tamaño en toda la app es cambiar aquí.

   `flex-shrink: 0` en todas: dentro de una fila estrecha, un ícono sin
   esta regla se aplasta hasta volverse una raya.
   ============================================================ */
.ico        { width: 14px; height: 14px; flex-shrink: 0; }
.ico-sm     { width: 12px; height: 12px; }
.ico-lg     { width: 18px; height: 18px; }
/* El dibujo grande de una pantalla vacía. No es "un tamaño más": es el
   único sitio donde el ícono es la ilustración y no la etiqueta. */
.ico-xl     { width: 32px; height: 32px; }
/* Alineación con el texto cuando el ícono va suelto dentro de un
   párrafo o un botón, en vez de en una caja flex. */
.ico, .ico-sm, .ico-lg, .ico-xl { vertical-align: -.12em; }

/* ============================================================
   R124 — Modo en vivo de cardio
   ------------------------------------------------------------
   La regla que ordena todo el bloque: la GRÁFICA se queda con el
   espacio que sobre. Por eso .cl-body es una columna flex y .cl-chart
   es el único `flex:1` -- cabecera, cifras, zonas y controles piden
   solo su alto, y lo demás es de la gráfica. Antes era al revés: cada
   elemento se apilaba con su margen y la gráfica heredaba las sobras,
   que en un celular eran 147 px para cinco zonas.

   En móvil es una hoja a pantalla completa. Un modal centrado con aire
   alrededor desperdicia justo lo que aquí más falta: alto.
   ============================================================ */
.cl-overlay {
  position: fixed; inset: 0; z-index: 9800;
  background: rgba(15,23,42,.6);
  display: flex; align-items: center; justify-content: center;
  padding: 1rem; box-sizing: border-box;
}
.cl-sheet {
  background: #fff; border-radius: 12px;
  width: min(720px, 100%);
  height: min(760px, 100%);
  display: flex; flex-direction: column;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0,0,0,.25);
}

.cl-head {
  flex: 0 0 auto;
  padding: .8rem 1rem;
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
}
.cl-title { margin: 0; font-size: .95rem; font-weight: 700; color: var(--ink); }
.cl-close { background: none; border: none; cursor: pointer; color: var(--ink-mute); padding: .25rem; display: flex; }

.cl-body {
  flex: 1 1 auto; min-height: 0;      /* sin min-height:0 el flex se niega a encoger */
  padding: .85rem 1rem;
  display: flex; flex-direction: column; gap: .7rem;
  overflow: hidden;
}

.cl-pre { flex: 0 0 auto; text-align: center; display: flex; flex-direction: column; gap: .5rem; align-items: center; }
.cl-nota { font-size: .78rem; color: var(--ink-mute); margin: 0; }
.cl-wakelock { font-size: .7rem; color: var(--ink-mute); margin: 0; }

/* Fila de fase + barra de progreso de la sesión completa */
.cl-fase { flex: 0 0 auto; display: none; flex-direction: column; gap: .3rem; }
.cl-corriendo .cl-fase { display: flex; }
.cl-fase-txt { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.cl-fase-nom { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-soft); }
.cl-fase-idx { font-size: .72rem; color: var(--ink-mute); flex-shrink: 0; }
.cl-progreso { height: 4px; background: var(--bg-alt); border-radius: 2px; overflow: hidden; }
.cl-progreso-bar { height: 100%; width: 0; background: var(--accent); border-radius: 2px; transition: width .4s linear; }

/* Cronómetro y FC en una sola fila: apilados gastaban dos renglones
   completos que ahora son de la gráfica. */
.cl-cifras { flex: 0 0 auto; display: none; align-items: center; justify-content: space-between; gap: 1rem; }
.cl-corriendo .cl-cifras { display: flex; }
.cl-crono { display: flex; flex-direction: column; line-height: 1; }
.cl-crono-num { font-size: 2rem; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.cl-cap { font-size: .65rem; color: var(--ink-mute); text-transform: uppercase; letter-spacing: .05em; margin-top: .15rem; }
.cl-fc { display: flex; flex-direction: column; align-items: flex-end; min-width: 0; }
.cl-fc-fila { display: flex; align-items: baseline; gap: .35rem; }
/* El número es lo que se lee desde el otro lado de la sala: es el que
   cambia de color con la zona, no la línea de texto de abajo. */
.cl-fc-num { font-size: 2.6rem; font-weight: 800; line-height: 1; color: #94a3b8; font-variant-numeric: tabular-nums; transition: color .25s; }
.cl-fc-un  { font-size: .8rem; font-weight: 600; color: var(--ink-mute); }
.cl-zbadge {
  font-size: .72rem; font-weight: 800; color: #fff;
  background: #cbd5e1; border-radius: 5px; padding: .12rem .35rem;
  align-self: center; flex-shrink: 0;
}
.cl-estado { font-size: .78rem; font-weight: 700; text-align: right; margin-top: .15rem; }

/* El único que crece */
.cl-chart { flex: 1 1 auto; min-height: 140px; display: flex; align-items: stretch; }
.cl-chart svg { width: 100%; }

/* Minutos acumulados por zona */
.cl-zonas { flex: 0 0 auto; display: none; flex-direction: column; gap: .25rem; }
.cl-corriendo .cl-zonas { display: flex; }
.cl-zbars { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: var(--bg-alt); }
.cl-zbar { height: 100%; }
.cl-zlegs { display: flex; flex-wrap: wrap; gap: .5rem; }
.cl-zleg { font-size: .68rem; color: var(--ink-mute); display: inline-flex; align-items: center; gap: .22rem; }
.cl-zleg i { width: 7px; height: 7px; border-radius: 2px; display: inline-block; }

.cl-pie {
  flex: 0 0 auto;
  padding: .7rem 1rem calc(.7rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line);
  display: flex; gap: .5rem; justify-content: center; align-items: center;
}

/* Resumen final: aquí sí se puede hacer scroll, es lectura y no vigilancia */
.cl-resumen .cl-body { overflow-y: auto; }
.cl-fin { text-align: center; display: flex; flex-direction: column; gap: .8rem; }
.cl-fin-tit { font-size: 1rem; font-weight: 700; color: var(--ink); margin: 0; }
.cl-stats { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.cl-stat { background: var(--bg-alt); border-radius: 8px; padding: .6rem .75rem; text-align: left; }
.cl-stat-lbl { font-size: .65rem; color: var(--ink-mute); text-transform: uppercase; letter-spacing: .04em; margin: 0 0 .2rem; }
.cl-stat-val { font-size: 1.15rem; font-weight: 800; color: var(--ink); margin: 0; }
.cl-chart-fin { min-height: 220px; flex: 0 0 auto; }

/* En el celular ocupa la pantalla entera: el alto es justo lo que le
   falta a esta vista, y los bordes redondeados sobre un fondo oscuro
   solo servirían para regalar píxeles. */
@media (max-width: 640px) {
  .cl-overlay { padding: 0; }
  .cl-sheet { width: 100%; height: 100%; border-radius: 0; max-height: none; }
  .cl-head { padding-top: calc(.8rem + env(safe-area-inset-top, 0px)); }
  .cl-crono-num { font-size: 1.7rem; }
  .cl-fc-num { font-size: 2.4rem; }
  .cl-pie .btn { flex: 1 1 auto; }
}

/* Pantalla baja y ancha (teléfono girado): las cifras se estrechan para
   que la gráfica conserve alto. */
@media (max-height: 520px) {
  .cl-crono-num { font-size: 1.4rem; }
  .cl-fc-num { font-size: 1.9rem; }
  .cl-body { gap: .45rem; padding: .5rem .75rem; }
  .cl-cap, .cl-fase-idx { display: none; }
}

/* ============================================================
   R125 — Selector de rango de fechas
   ------------------------------------------------------------
   El calendario del sistema no se puede pintar: los domingos en rojo
   y la tipografía Segoe UI de la captura los pone Windows. Este es
   nuestro, así que sigue los mismos tokens que el resto (Inter, azul
   de marca, radios del sistema) y se ve igual en cualquier aparato.

   El panel va en flujo normal, no flotando: vive dentro de un modal
   que hace scroll, y un desplegable posicionado en fijo se quedaría
   pegado en el sitio equivocado al desplazar.
   ============================================================ */
.dr-trigger {
  width: 100%;
  display: flex; align-items: center; gap: .5rem;
  padding: .5rem .65rem;
  background: var(--bg-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  font-size: .85rem; color: var(--ink);
  text-align: left;
}
.dr-trigger:hover { border-color: var(--accent); }
.dr-trigger.dr-open { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
.dr-trigger-txt { flex: 1 1 auto; }
.dr-trigger .dr-chev { color: var(--ink-ghost); transition: transform .18s; }
.dr-trigger.dr-open .dr-chev { transform: rotate(180deg); }

.dr-panel {
  margin-top: .4rem;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--bg-2);
  box-shadow: var(--shadow-md);
  padding: .6rem;
}
.dr-nav { display: flex; align-items: center; justify-content: space-between; margin-bottom: .5rem; }
.dr-nav-tit { font-size: .8rem; font-weight: 600; color: var(--ink); }
.dr-navbtn {
  background: none; border: none; cursor: pointer;
  color: var(--ink-mute); padding: .25rem .35rem; border-radius: var(--r-sm); display: flex;
}
.dr-navbtn:hover { background: var(--bg-alt); color: var(--ink); }

.dr-meses { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; }
.dr-mes-tit {
  font-size: .72rem; font-weight: 600; color: var(--ink-soft);
  text-align: center; margin: 0 0 .3rem;
}
/* La columna de la izquierda es el número de semana: los mesociclos se
   cuentan en semanas, y tenerlo delante evita contar cuadritos. */
.dr-row { display: grid; grid-template-columns: 22px repeat(7, 1fr); gap: 1px; }
.dr-head { margin-bottom: .15rem; }
.dr-dn, .dr-wk {
  font-size: .62rem; color: var(--ink-ghost);
  display: flex; align-items: center; justify-content: center;
}
.dr-wk { font-variant-numeric: tabular-nums; }

.dr-d {
  border: none; background: none; cursor: pointer;
  font-size: .72rem; color: var(--ink); font-family: inherit;
  padding: .3rem 0; border-radius: var(--r-sm);
  font-variant-numeric: tabular-nums;
}
.dr-d:hover { background: var(--bg-alt); }
.dr-vacio { cursor: default; }
.dr-vacio:hover { background: none; }
.dr-hoy { font-weight: 700; }
/* El relleno del rango va con los cuadrados pegados (radio 0) para que
   la semana se lea como una barra continua y no como siete pastillas. */
.dr-in  { background: var(--accent-muted); color: var(--accent-dark); border-radius: 0; }
.dr-a, .dr-b { background: var(--accent); color: #fff; }
.dr-a { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.dr-b { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.dr-solo { border-radius: var(--r-sm); }
.dr-a:hover, .dr-b:hover { background: var(--accent-dark); }

.dr-pie {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  border-top: 1px solid var(--line); margin-top: .5rem; padding-top: .45rem;
}
.dr-res-fuerte { font-size: .78rem; font-weight: 600; color: var(--ink); }
.dr-res-suave  { font-size: .72rem; color: var(--ink-mute); margin-left: .4rem; }
.dr-res-vacio  { font-size: .76rem; color: var(--ink-mute); }
.dr-acc {
  background: none; border: none; cursor: pointer; padding: 0;
  font-size: .74rem; font-weight: 600; color: var(--accent); font-family: inherit;
}
.dr-acc:hover { text-decoration: underline; }

/* Aviso de cruce o de hueco. Avisa, no bloquea: la periodización real
   se empalma, y lo que decide qué plan está vigente es el estado. */
.dr-aviso {
  margin-top: .45rem; padding: .5rem .6rem;
  border-radius: var(--r-md);
  display: flex; gap: .5rem; align-items: flex-start;
  font-size: .76rem; line-height: 1.4;
}
.dr-aviso p { margin: 0 0 .2rem; }
.dr-aviso svg { margin-top: .1rem; flex-shrink: 0; }
.dr-aviso-cruce { background: var(--amber-light); color: #b45309; }
.dr-aviso-cruce .dr-acc { color: #b45309; }
.dr-aviso-hueco { background: var(--red-light); color: #b91c1c; }
.dr-aviso-hueco .dr-acc { color: #b91c1c; }

/* Tira con los bloques del paciente: responde de un vistazo la pregunta
   "¿esto choca con algo?", que con cuatro fechas sueltas había que
   contestar restando de cabeza. */
.dr-tira { position: relative; margin-top: .55rem; }
.dr-bar {
  position: absolute; height: 17px;
  background: var(--accent-muted); border-radius: var(--r-sm);
  display: flex; align-items: center; padding: 0 .4rem;
  overflow: hidden;
}
.dr-bar span {
  font-size: .62rem; color: var(--accent-dark);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dr-bar-own { background: var(--accent); }
.dr-bar-own span { color: #fff; }
.dr-tira-pie {
  display: flex; justify-content: space-between;
  font-size: .62rem; color: var(--ink-ghost);
  border-top: 1px solid var(--line); padding-top: .25rem; margin-top: .15rem;
}

/* En el celular los dos meses no caben lado a lado: se apilan. */
@media (max-width: 560px) {
  .dr-meses { grid-template-columns: 1fr; gap: .7rem; }
  .dr-pie { flex-wrap: wrap; }
  .dr-res-suave { margin-left: 0; width: 100%; }
}

/* ============================================================
   R126 — El logo de marca
   ------------------------------------------------------------
   Sustituye a los cuadrados azules con una "F" que había en el login,
   la barra lateral y la ficha previa. Los trazos salen del manual
   oficial y viven en utils/brand.js.

   Las letras FORCE LAB heredan el color de su contenedor: la tarjeta
   de login es casi negra (#0e0e10) y el texto original del logo es
   negro, así que ahí desaparecería. Con `currentColor` un solo archivo
   sirve para fondo claro y oscuro, sin dos versiones que mantener.
   ============================================================ */
.brand-lockup { display: block; width: 100%; max-width: 190px; height: auto; }
.brand-mark   { display: block; height: 100%; width: auto; }

/* Login: el logo ya dice el nombre y el eslogan, así que el bloque de
   texto que lo acompañaba sobraba. */
/* Sobre la tarjeta oscura, el eslogan también va en blanco: en su azul
   de marca sobre casi negro no se lee. Es lo que hace el propio manual
   en su versión para fondo oscuro. */
.login-logo {
  display: block; margin-bottom: 1.5rem;
  color: #fafafa;
  --brand-eslogan: #a1a1aa;
}
/* El logo va centrado aunque el texto de debajo esté alineado a la
   izquierda: es la marca presidiendo la tarjeta, no una línea más del
   formulario. Alineado a la izquierda se leía como algo desplazado. */
.login-logo .brand-lockup { margin-left: auto; margin-right: auto; }

/* Barra lateral: solo el corredor, y sin la caja azul de antes -- el
   logo trae su propio color y encerrarlo en un cuadrado lo apagaba.
   El nombre de al lado es el de la CLÍNICA, no el de la plataforma. */
.sidebar-logo-mark {
  width: auto; height: 26px;
  background: none; border-radius: 0;
  display: flex; align-items: center;
  flex-shrink: 0;
  color: #fafafa;   /* el corredor va en monocromo aquí: ver app.js */
}

/* Ficha previa: fondo claro, el corredor va en su azul de marca. */
.intake-logo-mark {
  width: auto; height: 30px;
  background: none; border-radius: 0;
  display: flex; align-items: center;
  flex-shrink: 0;
}

/* Con la barra lateral plegada solo queda el corredor, centrado. */
.sidebar.collapsed .sidebar-logo { justify-content: center; }

/* ============================================================
   R128 — Banco de ejercicios: filtrar sin reconstruir
   ------------------------------------------------------------
   El circulito de selección y el botón de editar ahora están SIEMPRE
   en el HTML de cada tarjeta; lo que cambia es este CSS. Antes se
   dibujaban o no según el modo, así que entrar en modo selección --o
   marcar una sola tarjeta-- obligaba a rehacer las 300 tarjetas con
   sus 300 imágenes. Aquí eso es una clase.
   ============================================================ */
.exercise-card[hidden] { display: none; }

.ex-select-check {
  position: absolute; top: 6px; left: 6px;
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.8);
  background: rgba(0,0,0,.25);
  display: none; align-items: center; justify-content: center;
  pointer-events: none; transition: background .15s, border-color .15s;
}
/* Solo se ve cuando la rejilla está en modo selección. */
.exercise-grid.is-selecting .ex-select-check { display: flex; }
/* Y el botón de editar estorba mientras se seleccionan varios. */
.exercise-grid.is-selecting .btn-ex-edit { display: none; }

.ex-select-check svg { display: none; }
.exercise-card.is-sel .ex-select-check {
  background: var(--accent); border-color: var(--accent);
}
.exercise-card.is-sel .ex-select-check svg { display: block; }
.exercise-card.is-sel { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================
   R130 — Lista de opciones (título + descripción)
   ------------------------------------------------------------
   Es el patrón de "elige qué quieres hacer" con una explicación
   debajo de cada opción. Estaba montado con la clase .btn y estilos
   sueltos, y .btn está pensada para un control de UNA línea: altura
   fija de 34 px y `white-space: nowrap`. El resultado era la
   descripción cortada por la mitad y sin poder partirse en pantallas
   estrechas.

   Esto no es un botón, es una tarjeta que se pulsa. Se le da su
   propia clase en vez de pelear con .btn a base de estilos en línea.
   ============================================================ */
.opt-list { display: flex; flex-direction: column; gap: .5rem; }

.opt-card {
  display: block; width: 100%; text-align: left;
  padding: .7rem .85rem;
  background: var(--bg-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  font-family: inherit;
  /* Sin altura fija y con el texto libre de partirse: es justo lo que
     .btn impedía. */
  white-space: normal;
  transition: border-color .15s, background .15s;
}
.opt-card:hover  { border-color: var(--accent); background: var(--accent-light); }
.opt-card:active { background: var(--accent-muted); }

.opt-card-tit {
  display: block;
  font-size: .88rem; font-weight: 600; color: var(--ink);
  line-height: 1.3;
}
.opt-card-desc {
  display: block;
  font-size: .78rem; font-weight: 400; color: var(--ink-mute);
  line-height: 1.4; margin-top: .2rem;
}

/* Variante para la opción que destruye algo: el borde avisa al pasar
   por encima, sin gritar mientras solo se está leyendo la lista. */
.opt-card--peligro:hover { border-color: var(--red); background: var(--red-light); }
.opt-card--peligro:hover .opt-card-tit { color: var(--red); }

/* ============================================================
   R135 — Microciclos: tira del bloque y semanas plegables
   ============================================================ */
.mc-tira-card { margin-bottom: .85rem; padding: .7rem .85rem; }
.mc-tira-cab {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .5rem; margin-bottom: .5rem; flex-wrap: wrap;
}
.mc-tira-tit { font-size: .8rem; font-weight: 600; color: var(--ink); }
.mc-tira-sub { font-size: .72rem; color: var(--ink-mute); }

/* Las celdas se reparten el ancho: con 8 semanas salen anchas y con 30
   estrechas, pero la tira nunca desborda ni pide scroll lateral. */
.mc-tira { display: flex; gap: 3px; }
.mc-cel {
  flex: 1 1 0; min-width: 0; height: 26px;
  border: 1px solid transparent; border-radius: 3px;
  padding: 0; cursor: pointer;
  transition: transform .12s, box-shadow .12s;
}
.mc-cel:hover { transform: translateY(-2px); }
.mc-ok      { background: #16a34a; }
.mc-parcial { background: #f59e0b; }
.mc-fallo   { background: #ef4444; }
.mc-vacia   { background: var(--line); }
.mc-futura  { background: transparent; border: 1px dashed var(--line-strong); cursor: default; }
.mc-futura:hover { transform: none; }
/* La semana en curso: un anillo, no otro color -- el color ya dice el
   estado y pisarlo perdería esa información. */
.mc-actual  { box-shadow: 0 0 0 2px var(--accent); }

.mc-leyenda { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: .5rem; }
.mc-leyenda span { font-size: .65rem; color: var(--ink-mute); display: inline-flex; align-items: center; gap: .25rem; }
.mc-p { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }

/* ── Cabecera de semana ── */
.mc-sem {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .5rem .7rem; margin-bottom: .4rem;
  background: var(--bg-alt); border: 1px solid var(--line);
  border-radius: var(--r-md); cursor: pointer;
  transition: background .15s, border-color .15s;
}
.mc-sem:hover { border-color: var(--line-strong); }
/* La semana desplegada se marca en azul: es dónde estás mirando. */
.mc-sem-on { background: var(--accent-light); border-color: var(--accent-muted); }
.mc-sem-on .mc-sem-n { color: var(--accent-dark); }
.mc-sem-on .mc-sem-chev { transform: rotate(180deg); }
.mc-sem-fallo .mc-sem-res { color: #b91c1c; }

.mc-sem-izq, .mc-sem-der { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.mc-sem-n   { font-size: .82rem; font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
.mc-sem-f   { font-size: .74rem; color: var(--ink-mute); white-space: nowrap; }
.mc-sem-res { font-size: .7rem; color: var(--ink-mute); white-space: nowrap; }
.mc-sem-chev { color: var(--ink-mute); transition: transform .18s; flex-shrink: 0; }

.mc-sem-cuerpo { margin-bottom: .5rem; }

/* Semanas sin nada: una línea fina. Se muestran a propósito -- un parón
   en la periodización es información, no espacio desperdiciado. */
.mc-hueco {
  display: flex; align-items: center; gap: .45rem;
  padding: .4rem .7rem; margin-bottom: .4rem;
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  color: var(--ink-ghost);
}
.mc-hueco span { font-size: .7rem; }

@media (max-width: 640px) {
  .mc-sem-f   { display: none; }   /* el rango de fechas ya está en la tira */
  .mc-tira    { gap: 2px; }
  .mc-cel     { height: 22px; }
}
/* Semana futura que YA tiene sesiones puestas: sigue punteada (no ha
   pasado) pero con un fondo tenue, para no confundirla con una vacía. */
.mc-futura-plan { background: var(--accent-light); border-color: var(--accent-muted); cursor: pointer; }
.mc-futura-plan:hover { transform: translateY(-2px); }
/* La rutina cíclica: se distingue de una semana con un borde punteado,
   porque no ocupa un lugar en el calendario. */
.mc-sem-sf { border-style: dashed; }


/* ════════════════════════════════════════════════════════════════════
   R160 — Cabecera de bloque en la tabla de la sesion
   --------------------------------------------------------------------
   Sustituye a la columna "Bloque", donde la misma etiqueta se repetia en
   cada fila: tres "Movilidad" identicas seguidas, y un 11% del ancho
   gastado en decir tres veces lo mismo.

   El nombre NO es el del catalogo, es el de la seccion dentro de esta
   sesion ("Calentamiento (8-10 min)", "Agarre especifico"). La barrita de
   color si sale del bloque del catalogo, que es lo que sigue clasificando
   el ejercicio.
   ════════════════════════════════════════════════════════════════════ */

/* Sin `display` aqui: en escritorio esto tiene que seguir siendo una
   celda de tabla. Una celda con display:block sale del layout de la
   tabla y se encoge a su contenido -- la cabecera medi­a 24px y el nombre
   del bloque se recortaba a cero. Las anulaciones de tarjeta van abajo,
   dentro de su media query. */
.table-wrap .exercises-table td.ex-bloque-td {
  padding: .55rem .4rem .2rem;
  border-bottom: none;
  text-align: left;
}
.table-wrap .exercises-table td.ex-bloque-td::before { display: none; }
/* La primera cabecera de la tabla no necesita el aire de separacion. */
.table-wrap .exercises-table tbody tr:first-child td.ex-bloque-td { padding-top: .15rem; }

.ex-bloque-cab {
  display: flex;
  align-items: center;
  gap: .45rem;
  background: var(--bg-alt);
  border-radius: var(--r-sm);
  padding: .3rem .5rem;
}
.ex-bloque-barra {
  width: 3px;
  min-height: 15px;
  align-self: stretch;
  border-radius: 2px;
  background: var(--bloque-color, var(--ink-ghost));
  flex-shrink: 0;
}
.ex-bloque-nombre {
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--bloque-color, var(--ink));
  /* Un nombre largo ("Fuerza-resistencia tren superior/tronco") se
     acorta antes que empujar el contador y el boton fuera de la fila. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ex-bloque-n {
  font-size: .66rem;
  color: var(--ink-mute);
  white-space: nowrap;
}
/* El lapiz solo aparece al acercarse: renombrar es raro, y un icono
   permanente en cada cabecera competiria con el nombre. En tactil no hay
   hover, asi que ahi se deja siempre visible. */
.btn-bloque-renombrar {
  background: none; border: none; cursor: pointer; padding: 0 .1rem;
  color: var(--ink-ghost); line-height: 1; flex-shrink: 0;
  opacity: 0; transition: opacity .12s ease;
}
.ex-bloque-cab:hover .btn-bloque-renombrar,
.btn-bloque-renombrar:focus-visible { opacity: 1; }
.btn-bloque-renombrar:hover { color: var(--accent); }
@media (hover: none) {
  .btn-bloque-renombrar { opacity: 1; }
}
.btn-bloque-add {
  margin-left: auto;
  background: none; border: none; cursor: pointer;
  font-size: .66rem; font-weight: 600; color: var(--accent);
  white-space: nowrap; padding: .1rem .15rem; flex-shrink: 0;
}
.btn-bloque-add:hover { text-decoration: underline; }

/* Debajo de 640px la tabla se convierte en lista de tarjetas: cada `tr`
   recibe fondo blanco, borde y sombra, y cada `td` pasa a display:flex.
   La cabecera de bloque no es un dato -- es el separador ENTRE tarjetas,
   asi que con ese chrome parecia un ejercicio vacio. Se le quita todo y
   queda como rotulo suelto sobre el grupo.

   El breakpoint tiene que ser EXACTAMENTE el mismo que el de esas reglas
   (linea ~1400): si aqui se pone otro, hay un tramo de anchos donde la
   fila es tarjeta y la cabecera cree que no lo es. */
@media (max-width: 640px) {
  .table-wrap:not(.tw-plain) .data-table tr.ex-bloque-fila {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    margin: .55rem 0 .35rem;
  }
  .table-wrap:not(.tw-plain) .data-table tr.ex-bloque-fila:first-child { margin-top: 0; }
  /* Aqui si: en modo tarjeta la celda deja de ser celda. */
  .table-wrap:not(.tw-plain) .data-table td.ex-bloque-td {
    display: block;
    width: auto;
  }
}

/* ════════════════════════════════════════════════════════════════════
   R161 — Mesociclos ligeros
   --------------------------------------------------------------------
   Bandas de color SOBRE la tira de semanas, con las mismas proporciones
   que ella: un mesociclo de 4 semanas ocupa 4 celdas. Van pegadas a la
   tira (sin hueco) para que se lean como una etiqueta de ese tramo y no
   como una barra independiente.

   Un plan sin mesociclos no pinta nada de esto.
   ════════════════════════════════════════════════════════════════════ */
.mc-tira-der { display: flex; align-items: center; gap: .7rem; }
.mc-meso-btn {
  background: none; border: none; cursor: pointer;
  font-size: .7rem; font-weight: 600; color: var(--accent);
  padding: .1rem .2rem; white-space: nowrap;
}
.mc-meso-btn:hover { text-decoration: underline; }
.mc-tira-meso { font-weight: 700; }

.mc-meso-bandas { display: flex; gap: 3px; margin-bottom: 2px; }
.mc-meso-banda {
  border-radius: var(--r-xs, 4px) var(--r-xs, 4px) 0 0;
  padding: .16rem .4rem;
  font-size: .66rem; font-weight: 700; color: #fff;
  /* Un nombre largo se recorta antes que ensanchar su banda: la anchura
     la manda el numero de semanas, no el texto. */
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mc-meso-hueco { min-width: 0; }

.mc-meso-enfasis {
  display: flex; gap: 3px;
  font-size: .66rem; line-height: 1.4; color: var(--ink-mute);
  margin-top: .35rem;
}
.mc-meso-enfasis > div { min-width: 0; padding-right: .3rem; }

/* En la cabecera de cada semana desplegable, debajo de las fechas. */
.mc-sem-meso { font-size: .66rem; font-weight: 700; }

/* ── Editor ── */
.meso-fila {
  border: 1px solid var(--line-faint); border-radius: var(--r-sm);
  padding: .55rem .6rem; margin-bottom: .5rem; background: var(--bg-alt);
}
.meso-fila-top { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.meso-fila-top .meso-nombre { flex: 1; min-width: 120px; }
.meso-rango {
  display: flex; align-items: center; gap: .3rem;
  font-size: .75rem; color: var(--ink-mute); white-space: nowrap;
}
.meso-rango input { width: 56px; text-align: center; }
.meso-quitar {
  background: none; border: none; cursor: pointer;
  color: var(--ink-ghost); padding: .1rem; line-height: 1;
}
.meso-quitar:hover { color: var(--danger, #dc2626); }
.meso-desc { margin-top: .4rem; font-size: .78rem; }

/* Debajo de 640px el rango no cabe junto al nombre. */
@media (max-width: 640px) {
  .meso-fila-top .meso-nombre { flex: 1 1 100%; }
  .mc-meso-enfasis { display: none; }
  .mc-tira-der { gap: .5rem; }
}

/* ════════════════════════════════════════════════════════════════════
   R163 — Popup "Agregar ejercicio": dos pasos en movil
   ════════════════════════════════════════════════════════════════════ */

/* La lista recupera su tope DESDE LA HOJA. Estaba escrito en linea
   (max-height:240px) y por eso valia igual en los dos tamanos: un valor
   en linea no se sobreescribe sin !important. */
.ex-bank-list {
  /* R184: sube con la miniatura (ver .ex-bank-row .ex-thumb-wrap). Debajo
     de 640 px este tope no existe -- la regla movil lo pone en `none` --,
     asi que esto es solo de escritorio. */
  max-height: 360px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: .35rem;
}

/* En escritorio el envoltorio de filtros no existe para el layout: los
   cuatro selects siguen siendo hijos de la fila flex, exactamente como
   antes de R163. */
.addex-filtros { display: contents; }
.addex-filtros-btn { display: none; }
.addex-cuenta { display: none; }
/* Ocultos en escritorio, donde no hay pasos que recorrer. El JS los
   muestra con style.display='' -- que resuelve a ESTA regla, no a la del
   media query de abajo. Por eso el valor movil tiene que estar declarado
   alli: sin el, '' seguia valiendo `none` y el boton Continuar no
   aparecia nunca. */
#addex-atras, #addex-continuar { display: none; }

@media (max-width: 640px) {
  /* La lista se come todo lo que el paso 1 le deja. El 240px fijo era el
     30% de una pantalla de 812, y con las filas a 144px no cabia ni fila
     y media. */
  .ex-bank-list { max-height: none; border: none; padding: 0; }
  .ex-bank-list .ex-bank-row { border-bottom: 1px solid var(--line-faint); border-radius: 0; }

  .addex-filtros-btn {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .45rem .7rem; border: 1.5px solid var(--line-strong);
    border-radius: var(--r-sm); background: #fff;
    font-size: .78rem; font-weight: 600; color: var(--ink-mute);
    white-space: nowrap; cursor: pointer;
  }
  .addex-filtros-btn.on { background: var(--accent-glow); border-color: var(--accent); color: var(--accent); }
  /* Plegados por defecto: casi siempre se busca escribiendo. */
  .addex-filtros { display: none; }
  .addex-filtros.addex-filtros-on {
    display: grid; grid-template-columns: 1fr 1fr; gap: .4rem;
    flex-basis: 100%; margin-top: .1rem;
  }
  .addex-filtros select { width: 100%; }

  .addex-cuenta { display: inline; font-size: .8rem; color: var(--ink-mute); margin-right: auto; }
  #addex-atras, #addex-continuar { display: inline-flex; }
  #addex-continuar, #save-ex-btn { flex: 1; }
}

/* Cabecera de lote en el paso 2 (Fuerza / Cardio). Solo aparece cuando
   hay DOS formularios: con uno solo, etiquetarlo es ruido. */
.addex-lote-cab {
  display: flex; align-items: center; gap: .45rem;
  background: var(--bg-alt); border-radius: var(--r-sm);
  padding: .3rem .5rem; margin-top: .875rem;
}
.addex-lote-barra {
  width: 3px; align-self: stretch; min-height: 15px;
  border-radius: 2px; background: var(--lote-color, var(--ink-ghost)); flex-shrink: 0;
}
.addex-lote-cab b { font-size: .72rem; font-weight: 700; color: var(--lote-color, var(--ink)); }
.addex-lote-cab span { font-size: .68rem; color: var(--ink-mute); }
.addex-lote-lista {
  font-size: .72rem; color: var(--ink-mute); line-height: 1.5;
  padding: .3rem .5rem 0;
}

/* ════════════════════════════════════════════════════════════════════
   R164 — Tarjeta de sesion en movil
   ════════════════════════════════════════════════════════════════════ */

/* En escritorio no existe: alli cada parametro tiene su columna. */
.ex-resumen { display: none; }
/* ════════════════════════════════════════════════════════════════════
   R188 — Menos iconos en la fila (escritorio)
   --------------------------------------------------------------------
   Habia CINCO controles por fila: progreso, editar, reemplazar,
   eliminar y el menu. De esos, el menu tenia una sola entrada
   ("Agrupar con X"), asi que la pantalla pagaba cinco huecos para dar
   cuatro acciones.

   Ahora fuera se queda lo que se toca en cada pasada -- editar, y el
   "modo en vivo" del cardio, que se usa con el cliente delante -- y el
   resto entra al menu. NO hace falta tocar el JS: _menuAccionesEx
   recoge automaticamente toda accion cuyo boton este en `display:none`
   (ver R166), asi que esconderlos aqui es a la vez sacarlos de la fila
   y meterlos al menu, con su nombre escrito.

   El caso del borrado importa aparte: elimina SIN preguntar (se apoya
   en el "Deshacer" del aviso). Como icono gris de 28 px pegado al
   lapiz era un resbalon esperando; dentro del menu hay que elegirlo
   leyendo la palabra "Eliminar". En movil ya estaba asi.
   ════════════════════════════════════════════════════════════════════ */
.table-wrap .exercises-table td .btn-progress-ex,
.table-wrap .exercises-table td .btn-replace-ex,
.table-wrap .exercises-table td .btn-remove-ex { display: none; }

/* R187: en cardio ese resumen es un <button> (abre la prescripcion). Sin
   este reseteo heredaria el aspecto de control del navegador -- marco,
   fondo gris y fuente propia -- en medio de una tarjeta de texto. El
   `display` NO se toca aqui: lo decide la regla de arriba y su gemela de
   la media query, para que siga siendo cosa de un solo sitio. */
button.ex-resumen {
  width: 100%; text-align: left;
  background: none; border: none; padding: .1rem 0;
  font-family: inherit; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
button.ex-resumen:active { opacity: .6; }

/* R166: el menu tambien en escritorio. Ahi los cuatro iconos siguen en la
   fila y el menu solo guarda lo que no tiene boton propio -- agrupar. Es
   la unica puerta visible a las biseries: hasta ahora la unica forma era
   arrastrar un ejercicio sobre otro, sin una sola palabra que lo dijera. */
.btn-ex-menu, .btn-row-menu {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 28px;
  background: none; border: none; cursor: pointer;
  color: var(--ink-ghost); font-size: 1.05rem; line-height: 1;
  vertical-align: middle;
}
.btn-ex-menu:hover, .btn-row-menu:hover { color: var(--accent); }

@media (max-width: 640px) {
  /* Fuera la miniatura de la TARJETA (no la del banco, que la conserva
     su propia regla .ex-bank-row .ex-thumb-wrap). Al elegir entre 291
     ejercicios la imagen ayuda; al leer una sesion ya escrita, no --  y
     su inicial era la letra mas grande de la tarjeta, por encima del
     nombre del ejercicio. Si la quieres ver, el nombre sigue abriendo el
     video igual que en escritorio. */
  .table-wrap .exercises-table .ex-thumb-wrap { display: none; }

  /* Los parametros dejan de tener celda propia: los dice el resumen. */
  .table-wrap .exercises-table td.ro-cell,
  .table-wrap .exercises-table td[colspan] { display: none; }
  /* ...menos la cabecera de bloque, que tambien lleva colspan. */
  .table-wrap:not(.tw-plain) .data-table td.ex-bloque-td { display: block; }

  /* El nombre a la izquierda.

     `.data-table td[data-label] { text-align: right !important }` (linea
     ~1569) alinea a la derecha toda celda etiquetada, para que el valor
     quede enfrentado a su rotulo. La celda del nombre tambien lleva
     data-label -- se lo pone mobile-tables.js -- y esa regla gana por
     especificidad (0,4,1) a la del titulo de la tarjeta (0,3,1).

     Hasta ahora no se notaba porque la miniatura flotaba a la izquierda y
     el texto la rodeaba. Al esconderla, el nombre se fue a la derecha y
     el resumen se quedo a la izquierda: la tarjeta partida en dos
     alineaciones. Se sube la especificidad a (0,4,2) apuntando a la celda
     concreta, en vez de tocar la regla general que el resto de tablas
     sigue necesitando. */
  .table-wrap:not(.tw-plain) .data-table td[data-label]:nth-child(2) {
    text-align: left !important;
  }

  /* Y sin la raya de debajo. Separaba el titulo de la rejilla de
     parametros; ahora esos parametros viven en el resumen, DENTRO de
     esta misma celda, asi que la raya quedaba cortando la tarjeta por un
     sitio donde ya no hay dos cosas que separar. */
  .table-wrap .exercises-table tr[data-ex-id] > td:nth-child(2) {
    border-bottom: none;
    padding-bottom: 0;
    margin-bottom: 0;
  }

  /* En el telefono un nombre de bloque largo cabe mejor en dos lineas que
     recortado con puntos suspensivos: "Calentamiento (8-..." esconde
     justo la duracion, que es la razon de escribirla. */
  .ex-bloque-nombre {
    white-space: normal;
    overflow: visible;
    line-height: 1.3;
  }

  .ex-resumen {
    display: block;
    font-size: 13px;
    color: var(--ink-mute);
    line-height: 1.4;
    margin-top: .15rem;
    font-weight: 400;
  }
  /* Los separadores y unidades, un tono por debajo: los numeros mandan. */
  .ex-resumen .u { color: var(--ink-ghost); }

  /* La celda de acciones deja ver solo el menu. */
  .table-wrap .exercises-table td .btn-icon { display: none; }
  /* En la tarjeta el menu es el unico control, y se toca con el dedo. */
  .btn-ex-menu { width: 32px; height: 32px; font-size: 1.15rem; }
  /* La celda de acciones se ancla arriba a la derecha de la tarjeta. */
  .table-wrap:not(.tw-plain) .data-table .exercises-table td:last-child,
  .table-wrap .exercises-table tr[data-ex-id] > td:last-child {
    position: absolute; top: .35rem; right: .35rem;
    width: auto; padding: 0; border: none; background: none;
  }
  .table-wrap .exercises-table tr[data-ex-id] { position: relative; }
  /* Hueco para que el nombre no pase por debajo del boton. */
  .table-wrap .exercises-table tr[data-ex-id] > td:nth-child(2) { padding-right: 2.2rem; }
}

/* Menu de acciones (modal) */
/* R200 — En tablet la tabla de Planificación se pasaba 30 px de su caja
   (medido a 768: necesita 751 y caben 721), asi que quedaba con
   desplazamiento lateral. En vez de dejarlo, se retira la columna menos
   util de ESTA pantalla: el numero de expediente. El nombre ya identifica
   a la persona; el expediente sirve para cruzar datos con otra cosa, que
   no es lo que se hace aqui. Por debajo de 640 la tabla se vuelve
   tarjetas y la regla deja de importar. */
@media (max-width: 900px) {
  #patient-plan-grid .col-exp { display: none; }
}

/* R200: y en Planificación, "Eliminar toda la planificación". Sigue en
   el DOM porque el menú de la fila lo pulsa (ver _menuFilaPlan); lo que
   se esconde es su presencia en la fila, donde era una papelera roja a
   un pixel del chevron de abrir. */
#patient-plan-grid .btn-delete-patient-plans { display: none; }

/* ════════════════════════════════════════════════════════════════════
   R197 — La fila de Pacientes deja fuera solo "Ver"
   --------------------------------------------------------------------
   Los tres botones siguen en el DOM: el menu de la fila los pulsa por
   dentro (_menuAccionesPaciente), asi que esconderlos aqui es a la vez
   sacarlos de la fila y meterlos al menu con su nombre. Mismo mecanismo
   que R188 en la tabla de ejercicios.

   Se acota a la tabla de Pacientes con `#patients-table-wrap` para no
   afectar a ninguna otra tabla que reutilice esas clases.
   ════════════════════════════════════════════════════════════════════ */
#patients-table-wrap .btn-valoracion-patient,
#patients-table-wrap .btn-edit-patient,
#patients-table-wrap .btn-delete-patient { display: none; }

/* R196: se llamaba `ex-menu-*` porque nacio en la tabla de ejercicios,
   pero ahora la de Pacientes usa el mismo menu. El nombre pasa a decir lo
   que es -- el menu de acciones de UNA FILA -- y no de donde salio. */
.row-menu { display: flex; flex-direction: column; }
.row-menu-item {
  display: flex; align-items: center; gap: .7rem;
  padding: .8rem .5rem; background: none; border: none;
  border-bottom: 1px solid var(--line-faint);
  font-size: .9rem; color: var(--ink); cursor: pointer; text-align: left;
}
.row-menu-item:last-child { border-bottom: none; }
.row-menu-item:hover { background: var(--bg-alt); }
.row-menu-item svg { color: var(--item-color, var(--ink-mute)); flex-shrink: 0; }


/* ════════════════════════════════════════════════════════════════════
   R165 — Paleta de color del bloque
   ════════════════════════════════════════════════════════════════════ */
.bl-paleta { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.bl-color {
  width: 30px; height: 30px; border-radius: 50%;
  border: 2px solid transparent; cursor: pointer; padding: 0;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.08);
}
/* El elegido se marca con un anillo POR FUERA: un borde interior sobre un
   circulo de color se confunde con el propio color. */
.bl-color.on { outline: 2px solid var(--accent); outline-offset: 2px; }
.bl-color-auto {
  width: auto; height: 30px; border-radius: 99px;
  padding: 0 .6rem; background: var(--bg-alt);
  font-size: .72rem; font-weight: 600; color: var(--ink-mute);
  border: 1px solid var(--line);
}

/* Pie explicativo del menu de acciones (R166). */
.row-menu-pie {
  font-size: .74rem; color: var(--ink-mute); line-height: 1.5;
  margin: .5rem .2rem 0; padding-top: .5rem;
  border-top: 1px solid var(--line-faint);
}

/* R170 — Carga de la sesión (sRPE). Va en la misma línea que la fecha,
   pero destacada: es el único dato de esa línea que resume la sesión
   entera en un número, y se busca de un vistazo al recorrer el plan. */
.carga-ses {
  font-weight: 700;
  color: var(--accent);
  cursor: help;
}

/* R171 — Botón ocupado: no basta con deshabilitarlo. Un botón que se ve
   igual invita a pulsarlo otra vez, que es exactamente lo que pasó. El
   giro dice "está trabajando", y el texto dice qué. */
.btn-ocupado {
  display: inline-block;
  width: 12px; height: 12px;
  margin-right: .4rem;
  vertical-align: -1px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: btn-giro .6s linear infinite;
  opacity: .85;
}
@keyframes btn-giro { to { transform: rotate(360deg); } }
button[aria-busy="true"] { cursor: progress; opacity: .8; }
/* Sin animación para quien la haya desactivado: el punto sigue estando
   ahí, simplemente quieto. */
@media (prefers-reduced-motion: reduce) {
  .btn-ocupado { animation: none; }
}

/* R175 — "realizada el X" es un boton, no un adorno.

   Conserva el naranja de aviso que ya tenia: sigue siendo una senal de
   que las dos fechas no coinciden. El subrayado punteado es lo unico que
   se anade, para que se vea que hay algo que pulsar sin convertirlo en un
   boton mas de una cabecera que ya va cargada. */
.btn-fecha-real {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: #d97706;
  border-bottom: 1px dotted #d97706;
  cursor: pointer;
}
.btn-fecha-real:hover { color: #b45309; border-bottom-style: solid; }

/* R177 — Campo que se muestra pero no se edita.

   Tiene que verse distinto de uno editable: un campo con borde y fondo
   blanco invita a escribir, y descubrir que no se puede solo al intentarlo
   es peor que no ofrecerlo. Fondo gris, sin borde marcado y cursor normal
   -- se lee como un dato, no como un formulario. */
.campo-solo-lectura {
  background: var(--bg-alt) !important;
  border-color: var(--line-faint) !important;
  color: var(--ink-mute) !important;
  cursor: default;
  resize: none;
}
.campo-solo-lectura:focus { outline: none; box-shadow: none; }
