/* ===========================================================
   Global-VZLA · Plataforma Web — COMPONENTES COMPARTIDOS
   -----------------------------------------------------------
   Las piezas que aparecen en más de una herramienta, escritas una
   sola vez: botón, campo, tarjeta, tabla, chip, modal, aviso,
   avatar y globo de ayuda.

   TODAS las clases llevan prefijo `gv-`. No es decoración: las 6
   herramientas ya tienen su `.btn`, su `.card` y su `.modal` con
   medidas distintas, y todas están en producción. El prefijo deja
   convivir lo viejo y lo nuevo, y permite migrar una herramienta
   por vez sin romper las otras cinco.

   Se carga DESPUÉS de tokens.css y base.css, ANTES del styles.css
   propio de cada herramienta (que puede afinar, nunca redefinir).
   =========================================================== */

/* ===========================================================
   BOTONES
   Cuatro variantes y tres tamaños. Todo lo demás es un caso
   particular y va en la herramienta, no acá.
   =========================================================== */
.gv-btn{
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--sp-2);
  height:var(--h-md);
  padding:0 var(--sp-4);
  border:1px solid transparent;
  border-radius:var(--r-md);
  background:none;
  font-family:var(--font-body);
  font-size:var(--fs-base);
  font-weight:600;
  line-height:1;
  white-space:nowrap;
  cursor:pointer;
  transition:background var(--t), border-color var(--t), color var(--t), box-shadow var(--t), transform var(--t-fast);
}
.gv-btn:disabled,
.gv-btn[aria-disabled="true"]{ opacity:.45; pointer-events:none; }
.gv-btn:active:not(:disabled){ transform:translateY(1px); }
.gv-btn svg{ flex:0 0 auto; }

/* Primario — la acción que la pantalla quiere que pases.
   Uno por pantalla. Dos primarios es no haber decidido cuál importa. */
.gv-btn--primary{
  background:var(--accent);
  color:var(--on-accent);
  box-shadow:0 6px 18px -8px var(--accent);
}
.gv-btn--primary:hover:not(:disabled){
  background:var(--accent-2);
  box-shadow:0 10px 24px -8px var(--accent);
}

/* Secundario — acción normal, la mayoría de los botones. */
.gv-btn--ghost{
  background:var(--surface);
  border-color:var(--border-2);
  color:var(--text-2);
}
.gv-btn--ghost:hover:not(:disabled){
  background:var(--surface-2);
  border-color:var(--accent-2);
  color:var(--text);
}

/* Sutil — acción terciaria, sin caja hasta que la tocás. */
.gv-btn--quiet{ color:var(--text-2); }
.gv-btn--quiet:hover:not(:disabled){ background:var(--surface-2); color:var(--text); }

/* Destructivo. Rojo solo acá: si el rojo se usa para adornar, deja
   de significar "esto no se deshace". */
.gv-btn--danger{ background:var(--danger-solid); color:var(--on-solid); }
.gv-btn--danger:hover:not(:disabled){ filter:brightness(1.08); }

.gv-btn--sm{ height:var(--h-sm); padding:0 var(--sp-3); font-size:var(--fs-sm); }
.gv-btn--lg{ height:var(--h-lg); padding:0 var(--sp-5); font-size:var(--fs-md); }
.gv-btn--block{ width:100%; }

/* Botón que es solo un ícono. Cuadrado a propósito: un ícono en una
   caja rectangular queda descentrado. Necesita `aria-label`. */
.gv-icon-btn{
  display:grid; place-items:center;
  width:var(--h-md); height:var(--h-md);
  flex:0 0 auto;
  border:1px solid var(--border-2);
  border-radius:var(--r-md);
  background:var(--surface);
  color:var(--text-2);
  cursor:pointer;
  transition:color var(--t), border-color var(--t), background var(--t);
}
.gv-icon-btn:hover:not(:disabled){ color:var(--text); border-color:var(--accent-2); background:var(--surface-2); }
.gv-icon-btn:disabled{ opacity:.35; pointer-events:none; }
.gv-icon-btn--sm{ width:var(--h-sm); height:var(--h-sm); border-radius:var(--r-sm); }
/* Sin caja hasta el hover: para las acciones dentro de una fila de
   tabla, donde 8 recuadros seguidos ensucian la lectura. */
.gv-icon-btn--quiet{ background:transparent; border-color:transparent; }
.gv-icon-btn--quiet:hover:not(:disabled){ background:var(--surface-2); border-color:var(--border); }

/* ===========================================================
   CAMPOS DE FORMULARIO
   =========================================================== */
.gv-field{ display:block; margin-bottom:var(--sp-4); }
.gv-field__label{
  display:block;
  margin-bottom:var(--sp-2);
  font-size:var(--fs-base);
  font-weight:500;
  color:var(--text-2);
}
.gv-field__label .gv-req{ color:var(--danger); margin-left:2px; }

.gv-input,
.gv-select,
.gv-textarea{
  width:100%;
  height:var(--h-md);
  padding:0 var(--sp-3);
  background:var(--bg);
  border:1px solid var(--border-2);
  border-radius:var(--r-md);
  color:var(--text);
  font-family:var(--font-body);
  font-size:var(--fs-md);
  transition:border-color var(--t), box-shadow var(--t), background var(--t);
}
.gv-textarea{ height:auto; min-height:90px; padding:var(--sp-3); line-height:var(--lh); }
.gv-input::placeholder,
.gv-textarea::placeholder{ color:var(--text-3); }
.gv-input:hover:not(:disabled),
.gv-select:hover:not(:disabled),
.gv-textarea:hover:not(:disabled){ border-color:var(--text-3); }
.gv-input:focus,
.gv-select:focus,
.gv-textarea:focus{ outline:none; border-color:var(--accent-2); box-shadow:var(--ring); }
.gv-input:disabled,
.gv-select:disabled,
.gv-textarea:disabled{ background:var(--surface-2); color:var(--text-3); cursor:not-allowed; }
.gv-input--lg{ height:var(--h-lg); font-size:var(--fs-md); }

/* Estado de error. El color no viaja solo: al lado va el texto que
   dice qué pasó. Quien no distingue rojo y verde ve el mensaje. */
.gv-input[aria-invalid="true"],
.gv-select[aria-invalid="true"],
.gv-textarea[aria-invalid="true"]{ border-color:var(--danger); }
.gv-field__error{
  display:block; margin-top:var(--sp-2);
  font-size:var(--fs-sm); color:var(--danger);
}
.gv-field__hint{
  display:block; margin-top:var(--sp-2);
  font-size:var(--fs-sm); color:var(--text-3);
}

/* La flecha del <select> se dibuja acá y no se hereda del sistema:
   la nativa cambia de forma según el navegador y rompe la unidad.
   `currentColor` en el SVG no funciona dentro de un data-URI, así
   que el color se resuelve con una máscara. */
.gv-select{
  appearance:none; -webkit-appearance:none;
  padding-right:var(--sp-8);
  cursor:pointer;
  background-image:none;
}
.gv-select-wrap{ position:relative; display:block; }
.gv-select-wrap::after{
  content:''; position:absolute; right:var(--sp-3); top:50%;
  width:12px; height:12px; margin-top:-6px;
  background:var(--text-3);
  -webkit-mask:no-repeat center/12px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  mask:no-repeat center/12px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  pointer-events:none;
}

/* Casilla y radio: el color del tilde sale del acento de marca. */
.gv-check{ display:inline-flex; align-items:center; gap:var(--sp-2); cursor:pointer; font-size:var(--fs-base); }
.gv-check input{ width:16px; height:16px; accent-color:var(--accent); cursor:pointer; }

/* Contraseña con ojito. El botón se posiciona contra el envoltorio,
   nunca contra el campo. */
.gv-pass{ position:relative; display:block; }
.gv-pass .gv-input{ padding-right:var(--sp-10); }
.gv-pass__eye{
  position:absolute; right:var(--sp-2); top:50%; transform:translateY(-50%);
  display:grid; place-items:center; width:30px; height:30px;
  border-radius:var(--r-sm); color:var(--text-3); cursor:pointer;
  transition:color var(--t), background var(--t);
}
.gv-pass__eye:hover{ color:var(--accent-text); background:var(--surface-2); }

/* ===========================================================
   TARJETAS Y PANELES
   =========================================================== */
.gv-card{
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow);
}
.gv-card--pad{ padding:var(--sp-5); }
.gv-card--hover{ transition:transform var(--t), border-color var(--t), box-shadow var(--t); }
.gv-card--hover:hover{ transform:translateY(-3px); border-color:var(--accent-line); box-shadow:var(--shadow-lg); }

.gv-card__head{
  display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--border);
}
.gv-card__title{ font-family:var(--font-display); font-size:var(--fs-lg); font-weight:600; }
.gv-card__sub{ font-size:var(--fs-sm); color:var(--text-3); margin-top:2px; }
.gv-card__body{ padding:var(--sp-5); }

/* Encabezado de sección: etiqueta mono + línea que llena el resto.
   Ordena una pantalla larga sin gastar un título grande. */
.gv-sec-h{
  display:flex; align-items:center; gap:var(--sp-3);
  margin:var(--sp-6) 0 var(--sp-4);
  font-family:var(--font-mono);
  font-size:var(--fs-xs);
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--text-3);
}
.gv-sec-h::after{ content:''; flex:1; height:1px; background:var(--border); }

/* ===========================================================
   CHIPS, ETIQUETAS Y ESTADOS
   El color nunca va solo: cada estado lleva su texto. Un punto de
   color no dice nada a quien no lo distingue.
   =========================================================== */
.gv-chip{
  display:inline-flex; align-items:center; gap:var(--sp-1);
  padding:2px var(--sp-2);
  border:1px solid var(--border);
  border-radius:var(--r-full);
  background:var(--surface-2);
  color:var(--text-2);
  font-family:var(--font-mono);
  font-size:var(--fs-xs);
  font-weight:600;
  line-height:1.7;
  white-space:nowrap;
}
.gv-chip--accent { background:var(--accent-soft);  border-color:var(--accent-line);  color:var(--accent-text); }
.gv-chip--info   { background:var(--info-soft);    border-color:var(--info-line);    color:var(--info); }
.gv-chip--success{ background:var(--success-soft); border-color:var(--success-line); color:var(--success); }
.gv-chip--warning{ background:var(--warning-soft); border-color:var(--warning-line); color:var(--warning); }
.gv-chip--danger { background:var(--danger-soft);  border-color:var(--danger-line);  color:var(--danger); }

/* Punto de estado. Va SIEMPRE acompañado de texto — es un refuerzo
   visual, no la información. */
.gv-dot{
  display:inline-block; flex:0 0 auto;
  width:7px; height:7px; border-radius:var(--r-full);
  background:currentColor;
}

/* Aviso en bloque. Ámbar y no rojo cuando hay que prestar atención
   pero no pasó nada malo — el rojo se reserva para lo que falló. */
.gv-note{
  display:flex; align-items:flex-start; gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4);
  border:1px solid var(--border);
  border-left:3px solid var(--text-3);
  border-radius:var(--r-md);
  background:var(--surface-2);
  font-size:var(--fs-base);
  line-height:var(--lh);
}
.gv-note__ic{ flex:0 0 auto; margin-top:1px; color:var(--text-3); }
.gv-note--info    { border-left-color:var(--info);    background:var(--info-soft); }
.gv-note--info .gv-note__ic{ color:var(--info); }
.gv-note--success { border-left-color:var(--success); background:var(--success-soft); }
.gv-note--success .gv-note__ic{ color:var(--success); }
.gv-note--warning { border-left-color:var(--warning); background:var(--warning-soft); }
.gv-note--warning .gv-note__ic{ color:var(--warning); }
.gv-note--danger  { border-left-color:var(--danger);  background:var(--danger-soft); }
.gv-note--danger .gv-note__ic{ color:var(--danger); }

/* Avatar con iniciales. El gradiente de marca es lo único de la
   pantalla que lo lleva: identifica a la persona de un vistazo. */
.gv-avatar{
  display:grid; place-items:center; flex:0 0 auto;
  width:var(--h-md); height:var(--h-md);
  border-radius:var(--r-md);
  /* `--grad-solid` y no `--grad`: las iniciales van encima. Con el gradiente
     decorativo, el extremo cian deja el texto blanco en 1.81:1. Ver tokens.css. */
  background:var(--grad-solid);
  color:var(--on-solid);
  font-family:var(--font-display);
  font-size:var(--fs-base);
  font-weight:700;
  letter-spacing:.3px;
}
.gv-avatar--round{ border-radius:var(--r-full); }

/* ===========================================================
   TABLAS — regla de la casa (CLAUDE.md §8bis)
   -----------------------------------------------------------
   1. Encabezado SIEMPRE centrado. Los datos mantienen su
      alineación: números a la derecha (.gv-num), texto a la
      izquierda.
   2. Columnas redimensionables arrastrando el borde derecho del
      <th>, como Google Sheets. El ancho se recuerda.
      El comportamiento lo maneja shared/tables.js.
   =========================================================== */
.gv-table-wrap{
  overflow:auto;
  border:1px solid var(--border);
  border-radius:var(--r-lg);
  background:var(--surface);
}
.gv-table{ width:100%; border-collapse:collapse; font-size:var(--fs-sm); }

.gv-table thead th{
  position:sticky; top:0; z-index:1;
  padding:var(--sp-3);
  background:var(--surface-2);
  border-bottom:1px solid var(--border);
  color:var(--text-2);
  font-family:var(--font-mono);
  font-size:var(--fs-2xs);
  font-weight:700;
  letter-spacing:.08em;
  text-transform:uppercase;
  /* Regla 1: encabezado centrado, incluidas las columnas numéricas
     (por eso también se declara en .gv-table th.gv-num). */
  text-align:center;
  white-space:nowrap;
  /* Regla 2: la zona de agarre se posiciona contra el <th>. Sin un
     `position` distinto de `static`, el cursor de arrastre cae en el
     lugar equivocado — gotcha pisado en Conversion Lab y en Scout. */
  position:relative;
}
.gv-table th.gv-num{ text-align:center; }

.gv-table tbody td{
  padding:var(--sp-3);
  border-bottom:1px solid var(--border);
  color:var(--text);
  vertical-align:middle;
  /* Con una columna redimensionada (table-layout:fixed) un texto que
     no entra se derrama sobre la celda vecina si no se recorta. */
  overflow:hidden;
  text-overflow:ellipsis;
}
.gv-table tbody tr:last-child td{ border-bottom:none; }
.gv-table tbody tr:hover{ background:var(--surface-2); }
.gv-table td.gv-num{ font-family:var(--font-mono); font-variant-numeric:tabular-nums; text-align:right; }
.gv-table__empty{ padding:var(--sp-10); text-align:center; color:var(--text-3); font-size:var(--fs-base); }

/* La rayita que marca dónde termina una columna: corta y tenue, no
   una línea de punta a punta. Al pasar el mouse se marca más, para
   confirmar el punto antes de arrastrar. */
.gv-table th::before{
  content:''; position:absolute; top:28%; right:0; height:44%;
  border-right:1px solid var(--border);
  pointer-events:none;
  transition:border-color var(--t-fast);
}
.gv-table th:hover::before{ border-right-color:var(--text-3); }
/* La zona de agarre real: invisible, 7px de ancho y alto completo.
   Mucho más fácil de acertar que la rayita. */
.gv-table th::after{
  content:''; position:absolute; top:0; right:0; width:7px; height:100%;
  cursor:col-resize; user-select:none;
}
.gv-table th:last-child::before,
.gv-table th:last-child::after{ display:none; }

/* Mientras se arrastra, el cursor de columna manda en toda la página
   y no se selecciona texto sin querer. Lo pone shared/tables.js. */
body.gv-resizing{ cursor:col-resize !important; user-select:none !important; }

/* Ordenar por columna: el encabezado entero es el botón, salvo los
   7px del borde, que ensanchan. Marcado por shared/tables.js. */
.gv-table th[data-sortable]{ cursor:pointer; user-select:none; }
.gv-table th.gv-sorted{ color:var(--text); }
.gv-table__arrow{ margin-left:var(--sp-1); font-size:8px; color:var(--accent-text); vertical-align:middle; }

/* ===========================================================
   MODAL
   =========================================================== */
.gv-modal-ov{
  position:fixed; inset:0; z-index:var(--z-modal);
  display:flex; align-items:center; justify-content:center;
  padding:var(--sp-5);
  background:rgba(4,7,14,.62);
  backdrop-filter:blur(4px);
}
.gv-modal-ov[hidden]{ display:none; }
.gv-modal{
  position:relative;
  display:flex; flex-direction:column;
  width:min(560px,100%); max-height:88vh;
  background:var(--surface);
  border:1px solid var(--border-2);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-pop);
  overflow:hidden;
  animation:gv-modal-in var(--t-slow) var(--ease) both;
}
@keyframes gv-modal-in{ from{ opacity:0; transform:translateY(10px) scale(.985); } }
.gv-modal--lg{ width:min(880px,100%); }
.gv-modal--xl{ width:min(1080px,100%); }

.gv-modal__head{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--border);
}
.gv-modal__title{ font-family:var(--font-display); font-size:var(--fs-lg); font-weight:700; }
.gv-modal__x{
  display:grid; place-items:center; flex:0 0 auto;
  width:var(--h-sm); height:var(--h-sm);
  border:1px solid var(--border-2); border-radius:var(--r-sm);
  color:var(--text-3); cursor:pointer;
  transition:color var(--t), background var(--t);
}
.gv-modal__x:hover{ color:var(--text); background:var(--surface-2); }
.gv-modal__body{ padding:var(--sp-5); overflow-y:auto; }
.gv-modal__foot{
  display:flex; align-items:center; justify-content:flex-end; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  border-top:1px solid var(--border);
}
.gv-modal__foot .gv-sp{ flex:1; }

/* ===========================================================
   AVISO FLOTANTE (toast)
   -----------------------------------------------------------
   Va por encima del modal a propósito (--z-toast > --z-modal).
   Antes el hub lo tenía en 50 contra un modal en 100: un aviso
   disparado al guardar un formulario quedaba tapado justo cuando
   más falta hacía.
   =========================================================== */
.gv-toast{
  position:fixed; left:50%; bottom:var(--sp-6);
  z-index:var(--z-toast);
  transform:translateX(-50%) translateY(16px);
  display:flex; align-items:center; gap:var(--sp-3);
  max-width:min(520px,90vw);
  padding:var(--sp-3) var(--sp-5);
  background:var(--surface);
  border:1px solid var(--border-2);
  border-left:3px solid var(--accent);
  border-radius:var(--r-md);
  box-shadow:var(--shadow-pop);
  color:var(--text);
  font-size:var(--fs-md);
  opacity:0; pointer-events:none;
  transition:opacity var(--t-slow), transform var(--t-slow);
}
/* `.show` es el alias histórico: el hub ya lo usaba desde su propio CSS y su
   JS lo sigue poniendo. Se acepta para que ninguna herramienta se rompa al
   empezar a cargar este archivo. En código nuevo va `.is-on`. */
.gv-toast.is-on,
.gv-toast.show{ opacity:1; transform:translateX(-50%) translateY(0); }
.gv-toast--success{ border-left-color:var(--success); }
.gv-toast--danger,
.gv-toast-error{ border-left-color:var(--danger); }

/* ===========================================================
   GLOBO DE AYUDA
   -----------------------------------------------------------
   El disparador es el NOMBRE del campo o de la columna, con
   subrayado punteado (patrón Google Ads). No hay ícono "?".
   Se usa `text-decoration` y no `border-bottom` para no ocupar
   alto ni mover el layout.

   El globo vive en <body>, nunca dentro del elemento: adentro de
   una tabla con scroll el contenedor lo recorta y se lee la mitad
   del texto. Bug real de Conversion Lab, 2026-07-21.
   =========================================================== */
.gv-help{
  cursor:help;
  text-decoration:underline dotted;
  text-decoration-color:var(--text-3);
  text-underline-offset:3px;
}
.gv-tip{
  position:fixed; left:0; top:0; z-index:var(--z-tip);
  max-width:290px;
  padding:var(--sp-2) var(--sp-3);
  background:var(--surface);
  border:1px solid var(--border-2);
  border-radius:var(--r-sm);
  box-shadow:var(--shadow-pop);
  color:var(--text);
  font-family:var(--font-body);
  font-size:var(--fs-sm);
  font-weight:400;
  line-height:1.45;
  text-align:left;
  /* pre-wrap: el globo también muestra listas (un cambio por línea).
     Con `normal` los saltos se comen y queda un párrafo ilegible. */
  white-space:pre-wrap;
  pointer-events:none;
  opacity:0; visibility:hidden;
  transition:opacity var(--t-fast), visibility 0s linear var(--t-fast);
}
.gv-tip.is-on{ opacity:1; visibility:visible; transition:opacity var(--t-fast); }

/* ===========================================================
   CARGANDO Y VACÍO
   =========================================================== */
.gv-spinner{
  width:34px; height:34px;
  border:3px solid var(--border);
  border-top-color:var(--accent);
  border-radius:var(--r-full);
  animation:gv-spin .8s linear infinite;
}
@keyframes gv-spin{ to{ transform:rotate(360deg); } }

.gv-empty{
  display:flex; flex-direction:column; align-items:center;
  gap:var(--sp-3);
  max-width:360px; margin:0 auto;
  padding:var(--sp-10) var(--sp-5);
  text-align:center;
  color:var(--text-2);
}
.gv-empty__ic{
  display:grid; place-items:center;
  width:56px; height:56px;
  border:1px solid var(--accent-line);
  border-radius:var(--r-lg);
  background:var(--accent-soft);
  color:var(--accent-text);
}
.gv-empty__title{ font-family:var(--font-display); font-size:var(--fs-lg); font-weight:600; color:var(--text); }
.gv-empty p{ font-size:var(--fs-base); line-height:var(--lh); }
