/* Ported from design-directorio/Direccion_B.dc.html ("B · Cercano"), with the
   flat --paper background reconciled into a three-tier ivory/paper ramp.
   Revised after a post-MVP audit: login/hero redesign, border-radius and
   focus-state consolidation, contrast fixes, extra breakpoint. */
:root {
  --ink: #14150F;
  --ink-2: #2E3128;
  --gris: #6C6D5F;
  /* Was #9A9B8B: measured under WCAG AA it fails as text color on --marfil.
     Aligned to --gris, which is already used safely for body-sized text
     elsewhere in this file. */
  --gris-2: #6C6D5F;
  /* The old --gris-2 value, kept only for purely decorative marks (dots,
     icons) that never carry text and don't need AA contrast. */
  --gris-decor: #9A9B8B;
  /* Darker than --gris specifically for avatar backgrounds: white text on
     --gris alone measures under 4.5:1. */
  --avatar-dark: #4A4B3F;
  --marfil: #F7F4E9;
  --paper: #FFFFFF;
  --paper-2: #FBF9F2;
  /* Text color used on dark/terracota fills (buttons, active chips, avatars). */
  --on-accent: #FFFFFF;
  --line: rgba(20, 21, 15, .10);
  --terracota: #C25E3B;
  --terracota-d: #8C4327;
  --terracota-wash: rgba(194, 94, 59, .08);
  /* Naranja franco (no el terracota más apagado/rojizo de arriba) -- solo
     para los puntos de ciudad del mapa de aliados, para que se lean como una
     capa distinta de los puntos de país. */
  --naranja-ciudad: #E8862D;
  --sans: 'Roboto', 'Helvetica Neue', Arial, sans-serif;
  --sh: 0 1px 2px rgba(20, 21, 15, .04), 0 8px 24px -12px rgba(20, 21, 15, .10);
  /* Consolidated radius scale (was 7 unrelated values across the file). */
  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 22px;
  --r-pill: 100px;
  --focus-ring: 0 0 0 3px rgba(194, 94, 59, .35);
  /* Custom easings: the built-in CSS ones are too weak to feel intentional. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
}

* { box-sizing: border-box; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  font-family: var(--sans);
  background: var(--marfil);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }

input, select, textarea, button { font-family: var(--sans); }

/* Prevent iOS Safari auto-zoom on focus */
input[type="text"], input[type="email"], input[type="password"],
input[type="date"], input[type="search"], select, textarea {
  font-size: 16px;
}

button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--terracota);
  outline-offset: 2px;
}

/* ---------- Loading screen ---------- */
.pantalla-carga {
  min-height: 100dvh;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: var(--marfil);
}
.spinner {
  width: 28px;
  height: 28px;
  border: 3px solid var(--line);
  border-top-color: var(--terracota);
  border-radius: 50%;
  /* linear is correct for constant rotation; faster (.6s vs .8s) makes the
     load feel quicker at the same real wait time. */
  animation: girar .6s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }
.pantalla-carga p { font-size: 13px; color: var(--gris); margin: 0; }

/* Screens fade in the moment they go from display:none to displayed — this
   fires exactly once per transition automatically, no JS needed, and turns
   a hard cut into a deliberate entrance instead of "feeling broken".
   .pantalla-carga is index.html's own brief pre-login check; .auth-page is
   the login page; .wrap is the CRM shell in app.html. */
.pantalla-carga, .auth-page, .wrap {
  animation: entrada-pantalla 220ms var(--ease-out) both;
}
@keyframes entrada-pantalla {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes entrada-tarjeta {
  from { opacity: 0; transform: scale(0.96); }
  to { opacity: 1; transform: scale(1); }
}

/* ---------- Login form fields (used inside .auth-panel__inner) ---------- */
.login-sub {
  position: relative;
  font-size: 13.5px;
  color: var(--gris);
  margin: 0 0 24px;
  font-weight: 300;
  line-height: 1.5;
}

.login-microsoft {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 11px 16px;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease;
}
.login-microsoft:hover { border-color: var(--gris-decor); }
.login-microsoft:disabled { opacity: .6; cursor: default; }

.login-divisor {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 18px 0;
  font-size: 11.5px;
  color: var(--gris);
}
.login-divisor::before, .login-divisor::after { content: ''; flex: 1; height: 1px; background: var(--line); }

.login-form { display: flex; flex-direction: column; gap: 14px; }
.campo { display: flex; flex-direction: column; gap: 6px; }

.login-label { font-size: 12px; color: var(--gris); }

/* Scoped to .campo (each field's wrapper), not .login-card, so the same
   input treatment works both inside the simple notice card (app.html's
   error/retry screen) and inside the full-page auth layout below, which has
   no card wrapper at all. */
.campo input {
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper-2);
  padding: 12px 14px;
  color: var(--ink);
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.campo input::placeholder { color: var(--gris-decor); }
.campo input:focus-visible {
  outline: none;
  border-color: var(--terracota);
  box-shadow: var(--focus-ring);
}
.campo input[aria-invalid="true"] { border-color: var(--terracota-d); }

.login-error {
  color: var(--terracota-d);
  background: var(--terracota-wash);
  font-size: 12.5px;
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  line-height: 1.4;
}
.login-error--info { color: var(--ink-2); background: var(--paper-2); }

.login-submit { width: 100%; margin-top: 4px; }

.login-forgot {
  align-self: center;
  font-size: 12.5px;
  color: var(--gris);
  margin-top: 6px;
  transition: color 150ms ease;
}

/* ---------- Auth page (login.html): split screen, its own page now ---------- */
.auth-page {
  min-height: 100dvh;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.auth-brand {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  color: var(--marfil);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 48px;
}
.auth-brand::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(720px, 90vw);
  height: 480px;
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, rgba(194, 94, 59, .22), rgba(194, 94, 59, 0));
  pointer-events: none;
}

.auth-brand__word { position: relative; font-size: 26px; font-weight: 400; }
.auth-brand__word b { font-weight: 500; }
.auth-brand__word sup { font-size: .5em; font-weight: 500; vertical-align: -.28em; margin-left: 2px; color: var(--terracota); }

.auth-brand__body { position: relative; max-width: 30ch; }
.auth-brand__eyebrow {
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--terracota);
  font-weight: 500; margin-bottom: 12px;
}
.auth-brand__title { position: relative; font-size: 30px; line-height: 1.3; font-weight: 300; margin: 0; }
.auth-brand__title em { font-style: italic; font-weight: 300; color: var(--terracota); }

.auth-brand__foot { position: relative; font-size: 11.5px; color: rgba(247, 244, 233, .55); letter-spacing: .02em; }

.auth-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--marfil);
  padding: 24px;
}
.auth-panel__inner {
  width: 100%;
  max-width: 360px;
  transform-origin: center;
  animation: entrada-tarjeta 240ms var(--ease-out) both;
}
/* The wordmark repeats here for mobile only, once the brand panel is
   hidden — see the breakpoint below. */
.auth-panel__inner > .word { display: none; margin-bottom: 28px; }
.auth-panel__inner h1 { font-size: 22px; font-weight: 500; margin: 0 0 6px; }

@media (max-width: 860px) {
  .auth-page { grid-template-columns: 1fr; }
  .auth-brand { display: none; }
  .auth-panel__inner > .word { display: block; }
}

/* ---------- App shell ---------- */
.wrap { max-width: 1440px; margin: 0 auto; background: var(--marfil); }

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 44px;
  flex-wrap: wrap;
  gap: 10px;
}

.word { font-size: 16.5px; font-weight: 400; }
.word b { font-weight: 500; }
.word sup { font-size: .52em; font-weight: 500; vertical-align: -.28em; margin-left: 1px; }

.topbar__right { display: flex; align-items: center; gap: 14px; }

.navpill {
  font-size: 12.5px;
  color: var(--gris);
  background: var(--paper-2);
  border-radius: var(--r-pill);
  padding: 8px 18px;
  font-weight: 400;
}

.link-btn {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 8px 16px;
  font-size: 12.5px;
  color: var(--ink-2);
  cursor: pointer;
  transition: background 150ms ease, transform 160ms ease-out;
}
.link-btn:active { transform: scale(0.97); }
.link-btn:disabled { opacity: .6; cursor: default; }

/* ---------- Hero (banner exacto del demo Directorio_Contactos_SMCMA_DEMO_fichas.html) ---------- */
.logo { display: block; background: url("../assets/logo-smcma.png") no-repeat center/contain; height: 24px; width: 160px; }
.logo--hero { height: 34px; width: 227px; }

.hero {
  min-height: auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  position: relative;
  overflow: hidden;
  width: 100%;
  padding: 0;
}

.hero__bg { position: absolute; inset: 0; z-index: 0; }
.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 58%;
  opacity: .30;
  filter: saturate(.7) contrast(1.02);
}
.hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Tiene que llegar a --marfil 100% opaco bastante ANTES del borde
     inferior (85%, no 100%) -- si el ultimo tramo opaco es solo el pixel
     final, siempre queda un resto de foto visible justo pegado a .ia por
     como se percibe un degradado (aunque matematicamente ya casi llegue a
     1.0). Terminar antes deja una franja solida real de --marfil antes de
     que empiece .ia, sin costura pegada al anillo de foco de .ia__box. */
  background: linear-gradient(180deg,
    rgba(245, 246, 242, .50) 0%, rgba(245, 246, 242, .30) 26%,
    rgba(245, 246, 242, .38) 55%, var(--marfil) 85%);
}

.hero__inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 1040px;
  padding: clamp(52px, 6vw, 74px) clamp(20px, 5vw, 64px) clamp(40px, 4.4vw, 54px);
}

.hero__top { display: flex; flex-direction: column; align-items: center; gap: 14px; margin-bottom: 22px; }

.hero .eyebrow {
  position: relative;
  font-size: 12.5px;
  font-weight: 300;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--gris-decor);
}

.hero h1 {
  position: relative;
  font-family: var(--sans);
  font-weight: 500;
  font-size: clamp(30px, 4.4vw, 48px);
  line-height: 1.14;
  letter-spacing: .005em;
  margin: 0 auto;
  max-width: 15em;
  color: #2E3728;
  text-wrap: balance;
}

.hero h1 em { font-style: italic; font-weight: 400; color: #8C4327; }

.sub {
  position: relative;
  font-size: clamp(15px, 1.35vw, 16.5px);
  font-weight: 300;
  color: #414534;
  max-width: 56ch;
  margin: 16px auto 0;
  line-height: 1.7;
}

.stats {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0;
  margin-top: 28px;
  border: 1px solid rgba(20, 21, 15, .14);
  border-radius: var(--r-md);
  overflow: hidden;
  background: rgba(255, 255, 255, .66);
  backdrop-filter: blur(8px);
}

.stat { padding: 16px 26px; border-left: 1px solid rgba(20, 21, 15, .12); min-width: 134px; background: none; border-radius: 0; flex: none; max-width: none; }
.stat:first-child { border-left: none; }
.stat b { display: block; font-family: var(--sans); font-weight: 500; font-size: clamp(22px, 2.2vw, 28px); color: #465439; }
.stat span { display: block; font-size: 11.5px; color: var(--gris); margin-top: 3px; font-weight: 300; line-height: 1.35; }

/* ---------- Barra de pestañas Comercial / Aliados internacionales ---------- */
/* La página siempre está en EXACTAMENTE una de las dos -- nunca hay una
   vista "todos" combinada -- así que esto es navegación primaria (como
   .admin-tab/.admin-tab--activa), no un filtro de toolbar. */
.dirtabs { display: flex; justify-content: center; gap: 8px; margin-top: 16px; flex-wrap: wrap; }
.dirtab {
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  background: rgba(255, 255, 255, .66);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 9px 20px;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}
.dirtab--activa { color: var(--paper); background: var(--ink); border-color: var(--ink); }
@media (hover: hover) and (pointer: fine) {
  .dirtab:not(.dirtab--activa):hover { border-color: var(--terracota); }
}

/* ---------- AI assistant ---------- */
.ia { padding: 16px 44px 12px; }

.ia__box {
  display: flex;
  gap: 10px;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 6px 6px 6px 20px;
  align-items: center;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.ia__box:focus-within {
  border-color: var(--terracota);
  box-shadow: var(--focus-ring);
}

.ia__box input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  padding: 10px 0;
  color: var(--ink);
}

.ia__box input:focus-visible { outline: none; } /* the visible ring lives on .ia__box itself */
.ia__box .btn-primary { margin-top: 0; padding: 11px 20px; font-size: 13.5px; white-space: nowrap; }

.ia__out {
  margin-top: 12px;
  background: var(--paper);
  border-radius: var(--r-md);
  padding: 16px 20px;
  box-shadow: var(--sh);
  /* Anchored to .ia__box above it, like a popover — it should visibly grow
     from its trigger, not materialize from nowhere. Fires on every
     hidden→shown flip (see asistente-ia.js), which is once per question. */
  transform-origin: top center;
  animation: entrada-popover 180ms var(--ease-out) both;
}
@keyframes entrada-popover {
  from { opacity: 0; transform: scale(0.97) translateY(-4px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}

/* No longer a single <p> -- asistente-ia.js's renderizarMarkdownSeguro()
   emits its own p/ol/ul/strong blocks, so line breaks are explicit and
   pre-wrap would just double them up. */
.ia__texto { font-size: 14px; color: var(--ink-2); line-height: 1.6; }
.ia__texto p { margin: 0 0 10px; }
.ia__texto ol, .ia__texto ul { margin: 0 0 10px; padding-left: 22px; }
.ia__texto li { margin-bottom: 4px; }
.ia__texto p:last-child, .ia__texto ol:last-child, .ia__texto ul:last-child { margin-bottom: 0; }
.ia__texto strong { color: var(--ink); font-weight: 600; }
.ia__cargando { margin: 0; font-size: 13px; color: var(--gris); }
.ia__error { margin: 0; font-size: 13px; color: var(--terracota-d); }

.ia__cliente-link {
  margin-top: 10px;
  background: var(--terracota-wash);
  color: var(--terracota-d);
  border: none;
  border-radius: var(--r-pill);
  padding: 8px 16px;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
}

/* ---------- Toolbar / filters ---------- */
/* Sin esto, el "display" propio de .toolbar le gana al atributo [hidden] en
   la cascada -- mismo bug que ya se repitió varias veces en este archivo
   (.vista-aliados, .vista-evento, .banner-vista-previa), siempre el mismo
   arreglo. */
.toolbar[hidden] { display: none; }
.toolbar { display: flex; align-items: center; gap: 16px; padding: 18px 44px; flex-wrap: wrap; }

.search { position: relative; flex: 1; max-width: 320px; }
.search input {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--paper-2);
  padding: 11px 16px 11px 38px;
  color: var(--ink);
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.search input:focus-visible { outline: none; border-color: var(--terracota); box-shadow: var(--focus-ring); }
.search svg { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); width: 14px; height: 14px; color: var(--gris-decor); }

.fselect {
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--paper);
  padding: 9px 16px;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color 150ms ease;
}

/* Event chips are the primary way to find people ("quién asistió a X"); el
   socio es un recorte secundario, no el eje principal — se ve más chico y
   apagado a propósito. */
.fselect--secondary {
  font-size: 11.5px;
  color: var(--gris);
  border-color: transparent;
  background: var(--paper-2);
  padding: 7px 14px;
}


.results-row[hidden] { display: none; }
.results-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 44px; margin: 0 0 4px; flex-wrap: wrap; }
.results-line { padding: 0; font-size: 12px; color: var(--gris-2); margin: 0; }

/* ---------- Master / detail body ---------- */
/* align-items: start is required here: by default grid rows stretch every
   column to match the tallest one, which would force .sidebar and .detail
   to always be the same height and defeats their own independent scrolling
   below. */
.body { display: grid; grid-template-columns: 344px 1fr; gap: 20px; padding: 8px 44px 44px; align-items: start; }
/* Mismo gotcha que .overlay y .vista-evento: sin esto, el propio "display:
   grid" de .body le gana al atributo [hidden] en la cascada, y activar un
   filtro de evento (aplicarModoEvento() pone .body.hidden = true) no
   ocultaba la lista vieja -- quedaba pisada debajo de la vista de evento. */
.body[hidden] { display: none; }

/* Sticks in place and scrolls internally once its own content is taller
   than the viewport allows — selecting client #90 no longer requires
   scrolling the whole page back up to see the list again, because the list
   never left the screen to begin with. */
.sidebar {
  display: flex; flex-direction: column; gap: 8px;
  position: sticky;
  top: 16px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.citem {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: var(--r-sm); cursor: pointer;
  transition: background 150ms ease, transform 120ms ease-out;
}
.citem:active { transform: scale(0.985); }
.citem.active { background: var(--terracota-wash); }
.citem__av {
  width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 500; color: var(--terracota-d);
  background: var(--paper); border: 1.5px solid var(--terracota);
}
.citem.active .citem__av { background: var(--terracota); color: var(--on-accent); }
.citem__name { font-size: 13.5px; font-weight: 500; }
.citem__meta { font-size: 11.5px; color: var(--gris); margin-top: 2px; font-weight: 300; }
.citem__n { margin-left: auto; font-size: 11px; color: var(--gris-2); background: var(--paper-2); border-radius: var(--r-pill); padding: 2px 9px; }

.lista-vacia, .detalle-vacio, .detalle-cargando, .detalle-error {
  font-size: 13.5px; color: var(--gris); font-weight: 300; padding: 20px;
}
.detalle-error { color: var(--terracota-d); }

.back-btn {
  display: none;
  background: none;
  border: none;
  color: var(--terracota-d);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  margin-bottom: 14px;
  padding: 0;
}

/* Same sticky + own-scroll treatment as .sidebar: a long ficha (many
   contacts) scrolls on its own without dragging the client list out of
   view, and vice versa. */
.detail {
  background: var(--paper-2); border-radius: var(--r-lg); padding: 32px 34px; box-shadow: var(--sh);
  position: sticky;
  top: 16px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.dhead { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; flex-wrap: wrap; gap: 12px; }
.dhead h2 { font-size: 23px; font-weight: 500; margin: 0; }

.socio-tags { display: flex; gap: 8px; flex-wrap: wrap; }
.socio-tag {
  display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink-2);
  background: var(--paper); border-radius: var(--r-pill); padding: 6px 14px 6px 6px;
}
.socio-tag--muted { color: var(--gris); background: var(--paper-2); }

/* Mismo molde de pill que .evbadge (mismo tamaño de fuente, mismo
   border-radius), pero un vocabulario de color propio -- terracota/gris ya
   significan "socio"/"evento" en otras partes de esta misma ficha, así que
   el tipo de relación necesita su propia señal para no confundirse con
   esas. Solo se pinta cuando el tipo NO es 'comercial' (ver tipoBadgeHtml
   en directorio.js): el caso comercial es el default silencioso. */
.tipo-badge {
  font-size: 11px;
  font-weight: 500;
  border-radius: var(--r-pill);
  padding: 5px 12px;
  display: inline-flex;
  align-items: center;
}
/* NO usa --terracota-wash (mismo motivo que antes de la migración 0024, que
   fusionó aliado_potencial/aliado_estrategico en un solo valor): ese wash es
   el MISMO que .citem.active usa para resaltar la fila seleccionada del
   sidebar -- el badge quedaría casi invisible sobre su propia fila activa.
   Anillo terracota sin relleno en su lugar, para que se note sin competir. */
.tipo-badge--aliado-internacional { color: var(--ink-2); background: var(--paper); box-shadow: 0 0 0 1.5px var(--terracota-d) inset; }
.socio-av {
  width: 22px; height: 22px; border-radius: 50%; background: var(--terracota); color: var(--on-accent);
  font-size: 10px; font-weight: 600; display: flex; align-items: center; justify-content: center;
}

.ccard {
  background: var(--paper); border-radius: var(--r-md); padding: 20px 22px; margin-bottom: 14px; box-shadow: var(--sh);
  /* Each card is a freshly created element on every ficha render (innerHTML
     replace), so this always plays on entry. Stagger delay comes from an
     inline --stagger set per card in contactCardHtml(). animation-fill-mode
     "both" already covers the pre-start opacity, no separate rule needed. */
  animation: entrada-card 220ms var(--ease-out) both;
  animation-delay: var(--stagger, 0ms);
}
@keyframes entrada-card {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.ccard__top { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.ccard__left { display: flex; gap: 12px; }
.ccard__av {
  width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 12.5px; font-weight: 600; color: var(--on-accent);
}
.ccard__name { font-size: 15.5px; font-weight: 500; }
.ccard__cargo { font-size: 12.5px; color: var(--gris); margin-top: 1px; font-weight: 300; }
.ccard__socio { font-size: 11.5px; color: var(--gris-2); margin-top: 2px; font-weight: 300; }
.ccard__badges { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }

/* Color = asistio (mismo verde/rojo que el punto de la vista de evento,
   ver .ve-dot--verde/--rojo) -- base gris es el estado "sin confirmar" (la
   consulta que trajo este contacto no pidió la columna asistio), nunca se
   asume asistió/no asistió sin el dato real. */
.evbadge {
  font-size: 11px; font-weight: 500; color: var(--gris-2); background: var(--paper-2);
  border-radius: var(--r-pill); padding: 5px 12px; display: flex; align-items: center; gap: 6px;
}
.evbadge i { width: 6px; height: 6px; border-radius: 50%; background: var(--gris-decor); }
.evbadge--asistio { color: #3F7D4A; background: rgba(63, 125, 74, .12); }
.evbadge--asistio i { background: #3F7D4A; }
.evbadge--no-asistio { color: #B3261E; background: rgba(179, 38, 30, .12); }
.evbadge--no-asistio i { background: #B3261E; }
.evbadge--muted { color: var(--gris-2); background: var(--paper-2); }
.evbadge--muted i { background: var(--gris-decor); }
/* The one badge matching the currently active event chip, so it doesn't get
   lost among a contact's other events. */
.evbadge--activo { font-weight: 700; box-shadow: 0 0 0 1.5px var(--terracota-d) inset; }

/* Contact matching the active event filter floats to the top of the ficha
   (see ordenarPorEventoActivo in directorio.js) and gets called out visually;
   the rest fade back instead of competing for attention. */
.ccard {
  transition: opacity 150ms ease, box-shadow 150ms ease, background 150ms ease;
}
.ccard--match { box-shadow: 0 0 0 1.5px var(--terracota), var(--sh); background: var(--terracota-wash); }
.ccard--sinmatch { opacity: .5; }

.fields { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px 24px; margin-top: 16px; padding-left: 50px; }
.fld__k { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--gris-2); margin-bottom: 4px; }
.fld__v { font-size: 13.5px; color: var(--ink-2); font-weight: 300; overflow-wrap: anywhere; }
.fld__v a { color: var(--terracota-d); }

.hist { margin-top: 12px; padding-left: 50px; font-size: 12px; color: var(--gris); font-weight: 300; }
.hist-item { margin-bottom: 4px; }
.hist-item b { color: var(--terracota-d); font-weight: 500; }
.hist-autor { color: var(--gris-2); }
.hist-vacio { margin: 0; font-size: 12px; color: var(--gris-2); font-weight: 300; }

.form-interaccion { display: flex; gap: 8px; margin-top: 14px; padding-left: 50px; flex-wrap: wrap; }
.form-interaccion input[type="date"],
.form-interaccion input[type="text"] {
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.form-interaccion input[type="date"] {
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--paper-2);
  padding: 8px 14px; color: var(--ink-2);
}
.form-interaccion input[type="text"] {
  flex: 1; min-width: 160px; border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--paper-2); padding: 8px 16px; color: var(--ink);
}
.form-interaccion input:focus-visible { outline: none; border-color: var(--terracota); box-shadow: var(--focus-ring); }
.form-interaccion-error { flex-basis: 100%; margin: 0; font-size: 12px; color: var(--terracota-d); }

.btn-sm {
  border: none; background: var(--terracota); color: var(--on-accent); border-radius: var(--r-pill);
  padding: 8px 16px; font-size: 12.5px; font-weight: 500; cursor: pointer; white-space: nowrap;
  transition: background 150ms ease, transform 160ms ease-out;
}
.btn-sm:active { transform: scale(0.97); }
.btn-sm:disabled { opacity: .6; cursor: default; }

.btn-primary {
  margin-top: 0;
  background: var(--terracota);
  color: var(--on-accent);
  border: none;
  border-radius: var(--r-pill);
  padding: 13px 20px;
  font-size: 14.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background 150ms ease, transform 160ms ease-out;
}
.btn-primary:active { transform: scale(0.97); }
.btn-primary:disabled { opacity: .6; cursor: default; }

/* ---------- Hover states, gated to real pointers ---------- */
/* Touch devices fire :hover on tap and it stays "stuck" until the next
   unrelated tap elsewhere — a real bug, not just polish, on an app this
   spec says will mostly be opened from a phone. Every :hover rule in the
   file lives here, gated behind a real fine pointer. */
@media (hover: hover) and (pointer: fine) {
  .link-btn:hover { background: var(--paper-2); }
  .login-forgot:hover { color: var(--terracota-d); }
  .campo input:hover { border-color: var(--gris-decor); }
  .search input:hover { border-color: var(--gris-decor); }
  .fselect:hover { border-color: var(--gris-decor); }
  .citem:hover { background: var(--paper-2); }
  .form-interaccion input:hover { border-color: var(--gris-decor); }
  .btn-sm:hover { background: var(--terracota-d); }
  .btn-primary:hover { background: var(--terracota-d); }
}

/* ---------- Mobile: master/detail view swap ---------- */
@media (max-width: 768px) {
  .topbar, .hero__inner, .ia, .toolbar, .results-row { padding-left: 20px; padding-right: 20px; }
  .body { grid-template-columns: 1fr; padding: 8px 20px 32px; }
  /* Only one of sidebar/detail is ever visible at a time on mobile (the view
     swap below), so the sticky independent-scroll trick from desktop is
     unnecessary here — let the single visible panel scroll with the page
     like a normal screen, instead of trapping it in its own scroll box. */
  .sidebar, .detail {
    position: static;
    max-height: none;
    overflow-y: visible;
  }
  .detail { padding: 22px 18px; }
  .fields { grid-template-columns: 1fr; padding-left: 0; }
  .hist, .form-interaccion { padding-left: 0; }

  .hero__inner { padding: 22px 20px 18px; }
  .hero h1 { font-size: 22px; }
  .hero .eyebrow { font-size: 10px; }
  .sub { font-size: 13px; }
  .stats { margin-top: 16px; }

  .detail { display: none; }

  body.vista-detalle .sidebar { display: none; }
  body.vista-detalle .hero,
  body.vista-detalle .ia,
  body.vista-detalle .toolbar,
  body.vista-detalle .results-row { display: none; }
  body.vista-detalle .detail { display: block; }
  body.vista-detalle .back-btn { display: inline-flex; }

  /* Directional entrance for the primary mobile navigation gesture: drilling
     into a ficha arrives from the right, going back to the list arrives from
     the left — the same spatial-consistency cue as native list/detail nav.
     Short (200ms) because this is the single most-repeated interaction in
     the app on mobile. Fires on the display:none -> displayed flip, which
     only happens on navigation (not on every content re-render inside). */
  body.vista-detalle .detail {
    animation: entrada-detalle-movil 200ms var(--ease-out) both;
  }
  body:not(.vista-detalle) .sidebar {
    animation: entrada-lista-movil 200ms var(--ease-out) both;
  }
  @keyframes entrada-detalle-movil {
    from { opacity: 0; transform: translateX(16px); }
    to { opacity: 1; transform: translateX(0); }
  }
  @keyframes entrada-lista-movil {
    from { opacity: 0; transform: translateX(-16px); }
    to { opacity: 1; transform: translateX(0); }
  }
}

@media (max-width: 480px) {
  .login-card { padding: 32px 24px 28px; }
  .hero h1 { font-size: 19px; }
  .stats { display: grid; grid-template-columns: 1fr 1fr; }
  .stat { min-width: 0; max-width: none; }
  .toolbar { gap: 10px; }
}

/* ---------- Bulk Excel upload (carga-excel.js) ---------- */
.results-row__acciones { display: flex; align-items: center; gap: 10px; }

.btn-sm--secundario {
  background: var(--paper);
  color: var(--ink-2);
  border: 1px solid var(--line);
}

.btn-sm--peligro {
  background: var(--paper);
  color: var(--terracota-d);
  border: 1px solid var(--terracota-d);
}
@media (hover: hover) and (pointer: fine) {
  .btn-sm--peligro:hover { background: var(--terracota-wash); }
}

.dhead__acciones { display: flex; gap: 8px; flex-basis: 100%; }
.ccard__acciones-admin { display: flex; gap: 8px; margin-top: 10px; }

.banner-vista-previa[hidden] { display: none; }
.banner-vista-previa {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
  background: var(--ink);
  color: var(--paper);
  font-size: 13px;
  padding: 10px 20px;
  text-align: center;
}
.banner-vista-previa button {
  background: var(--paper);
  color: var(--ink);
  border: none;
  border-radius: var(--r-pill);
  padding: 6px 14px;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}

.overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
/* .overlay's own "display: flex" otherwise beats the bare [hidden] rule in
   the cascade (same specificity, author sheet loads after the UA one) --
   this is why closing it (JS sets .hidden = true) had no visible effect. */
.overlay[hidden] { display: none; }
.overlay__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20, 21, 15, .45);
  animation: entrada-pantalla 180ms var(--ease-out) both;
}
.overlay__panel {
  position: relative;
  width: 100%;
  max-width: 560px;
  max-height: min(80vh, 640px);
  overflow-y: auto;
  background: var(--paper);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  padding: 28px 30px;
  animation: entrada-tarjeta 200ms var(--ease-out) both;
}
.overlay__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; gap: 12px; }
.overlay__head h2 { font-size: 19px; font-weight: 500; margin: 0; }
.overlay__cerrar {
  background: none; border: none; font-size: 22px; line-height: 1; color: var(--gris);
  cursor: pointer; padding: 4px 8px;
}
.overlay__ayuda { font-size: 13px; color: var(--gris); line-height: 1.55; margin: 0 0 14px; }
.overlay__ayuda--error { color: var(--terracota-d); }
.overlay__acciones { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }

#cargaInput {
  display: block;
  width: 100%;
  font-size: 13px;
  color: var(--ink-2);
  padding: 10px 0;
}

.carga-resumen { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 4px; }
.carga-resumen__item { background: var(--paper-2); border-radius: var(--r-sm); padding: 12px 14px; }
.carga-resumen__item b { display: block; font-size: 20px; font-weight: 500; }
.carga-resumen__item span { font-size: 11.5px; color: var(--gris); }
.carga-resumen__item--muted { opacity: .75; }
.carga-resumen__item--error b { color: var(--terracota-d); }

.carga-errores {
  margin-top: 14px;
  max-height: 160px;
  overflow-y: auto;
  background: var(--terracota-wash);
  border-radius: var(--r-sm);
  padding: 10px 14px;
}
.carga-error-fila { font-size: 12px; color: var(--terracota-d); line-height: 1.6; }

.carga-cambios { margin-top: 14px; }
.carga-cambios summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  padding: 8px 0;
}
.carga-cambios__lista {
  max-height: 260px;
  overflow-y: auto;
  background: var(--paper-2);
  border-radius: var(--r-sm);
  padding: 4px 14px;
}
.carga-cambio-fila { padding: 8px 0; border-bottom: 1px solid var(--line); }
.carga-cambio-fila:last-child { border-bottom: none; }
.carga-cambio-fila__titulo { font-size: 12.5px; font-weight: 500; color: var(--ink); }
.carga-cambio-fila__lista { margin: 4px 0 0; padding-left: 18px; font-size: 12px; color: var(--gris); line-height: 1.6; }

/* ---------- Formulario manual "Agregar contacto" / "Nuevo evento" (agregar-contacto.js, crear-evento.js) ---------- */
.form-agregar-contacto { display: flex; flex-direction: column; gap: 14px; margin-top: 4px; }
/* País/ciudad solo aplican al modo aliado internacional -- ver
   agregar-contacto.js, que fija data-modo según qué botón abrió el overlay. */
.form-agregar-contacto[data-modo="comercial"] .campo-aliado { display: none; }
.form-agregar-contacto label { display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; color: var(--gris); font-weight: 500; }
.form-agregar-contacto input,
.form-agregar-contacto select,
.form-agregar-contacto textarea {
  font-family: inherit;
  font-size: 14px;
  color: var(--ink);
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper-2);
  resize: vertical;
}
.form-agregar-contacto input:focus-visible,
.form-agregar-contacto select:focus-visible,
.form-agregar-contacto textarea:focus-visible {
  outline: none;
  border-color: var(--terracota);
  box-shadow: var(--focus-ring);
}
.form-agregar-contacto__ayuda { font-size: 12px; color: var(--gris); margin: 0; }
.form-agregar-contacto__error { font-size: 12.5px; color: var(--terracota-d); margin: 0; }

@media (max-width: 480px) {
  .overlay__panel { padding: 22px 18px; }
  .carga-resumen { grid-template-columns: 1fr 1fr; }
}

/* ---------- AI assistant example questions ---------- */
.ia__ejemplos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.ia__ejemplo {
  background: rgba(255, 255, 255, .55);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 7px 14px;
  font-size: 12px;
  color: var(--ink-2);
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .ia__ejemplo:hover { background: var(--paper); border-color: var(--terracota); }
}

/* ---------- Vista de evento (directorio.js: aplicarModoEvento) ---------- */
/* Mismo gotcha que .overlay: sin esto, su propio "display" le gana al
   atributo [hidden] en la cascada y togglear .hidden no hace nada visible. */
.vista-evento[hidden] { display: none; }
.vista-evento {
  background: var(--paper-2);
  border-radius: var(--r-lg);
  padding: 32px 34px;
  box-shadow: var(--sh);
}

/* ---------- Pestaña "Aliados internacionales" (directorio.js: renderVistaAliados) ---------- */
/* Mismo motivo exacto que .vista-evento arriba (y que .overlay más arriba
   todavía): sin esto, el "display" propio de .vista-aliados le gana al
   atributo [hidden] en la cascada y togglear .hidden no hace nada visible --
   este bug ya se repitió varias veces en este archivo, siempre con el mismo
   arreglo. */
.vista-aliados[hidden] { display: none; }
.vista-aliados {
  background: var(--paper-2);
  border-radius: var(--r-lg);
  padding: 32px 34px;
  box-shadow: var(--sh);
}

/* Equivalente propio de .toolbar/.results-row (compartidos, pero escondidos
   en esta pestaña -- ver aplicarModo en directorio.js) para el buscador y
   las acciones (agregar aliado, descargar reporte) que sí aplican acá. */
.aliados-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.aliados-toolbar__acciones { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.aliados-layout { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(280px, 1fr); gap: 28px; align-items: start; }

.aliados-mapa { min-width: 0; }
.aliados-mapa__lienzo { width: 100%; }
.aliados-mapa__lienzo svg path { fill: var(--marfil); stroke: var(--line); stroke-width: 0.5; }

/* El trazo (stroke) --paper importa: separarPuntosSuperpuestos (mapa-aliados.js)
   ya evita casi todo el solape real, pero en clusters muy densos (Benelux)
   puede quedar un resto -- el borde marca dónde termina un punto y empieza
   el de al lado aunque las áreas se toquen. */
.mapa-punto { fill: var(--terracota); stroke: var(--paper); stroke-width: 1; cursor: pointer; }
.mapa-punto--activa { fill: var(--terracota-d); }
@media (hover: hover) and (pointer: fine) {
  .mapa-punto:hover { fill: var(--terracota-wash); }
}
/* Al seleccionar un país real, todos los DEMÁS puntos de país se esconden --
   el foco pasa a las ciudades de ese país (ver marcarEstadoCirculosPais en
   directorio.js). */
.mapa-punto--oculto { display: none; }

/* Puntos de ciudad (posición real cuando existe en ANCLAS_CIUDAD, si no un
   anillo decorativo alrededor del país -- ver dibujarPuntosCiudad en
   mapa-aliados.js). Naranja franco, pedido explícito para distinguirlos del
   terracota de los países (más apagado/rojizo) -- ahora que los países no
   seleccionados se esconden (.mapa-punto--oculto), estos son lo único que
   queda visible además del punto del país activo. cursor:default (no
   clickeables, a diferencia de .mapa-punto). */
.mapa-punto-ciudad { fill: var(--naranja-ciudad); stroke: var(--paper); stroke-width: 1; cursor: default; }

.aliados-panel { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
.aliados-quitar-filtro { align-self: flex-start; }

.aliados-lista-paises { display: flex; flex-direction: column; gap: 2px; max-height: 220px; overflow-y: auto; }
.pais-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-family: inherit;
  font-size: 13px;
  color: var(--ink-2);
  background: none;
  border: none;
  border-radius: var(--r-sm);
  padding: 7px 10px;
  cursor: pointer;
  text-align: left;
}
.pais-item--activa { background: var(--terracota-wash); color: var(--ink); font-weight: 500; }
.pais-item__n { flex: 0 0 auto; font-size: 11px; color: var(--gris-2); background: var(--paper); border-radius: var(--r-pill); padding: 2px 9px; }
@media (hover: hover) and (pointer: fine) {
  .pais-item:not(.pais-item--activa):hover { background: var(--paper); }
}

/* Sin esto, un país con muchos contactos (ej. una firma grande con decenas
   de personas cargadas) empuja el panel entero a ocupar todo el alto de la
   pantalla -- scroll interno acotado en vez de eso, mismo criterio que ya
   usa .aliados-lista-paises arriba. */
.aliados-tarjetas { display: flex; flex-direction: column; gap: 10px; max-height: min(55vh, 520px); overflow-y: auto; padding-right: 2px; }
.aliado-card {
  background: var(--paper);
  border-radius: var(--r-md);
  padding: 14px 16px;
  box-shadow: var(--sh);
}
.aliado-card__nombre { font-size: 13.5px; font-weight: 500; color: var(--ink); }
.aliado-card__cargo { font-size: 12.5px; color: var(--gris); margin-top: 1px; font-weight: 300; }
.aliado-card__empresa { font-size: 11.5px; color: var(--gris-2); margin-top: 2px; font-weight: 300; }
.aliado-card__pais { font-size: 11.5px; color: var(--gris-2); margin-top: 2px; font-weight: 300; }
.aliado-card__contacto { font-size: 12px; color: var(--ink-2); margin-top: 6px; }
.aliado-card [data-role="ver-ficha-completa"] { margin-top: 10px; }

/* position:sticky + top:0 lo clava arriba del scroll de .aliados-tarjetas
   mientras se ven las tarjetas de ESA ciudad, y lo suelta apenas termina ese
   grupo -- comportamiento de "encabezado de sección" (como el de una app de
   contactos por letra). Antes era texto gris chico que se perdía entre
   tarjetas al scrollear; con esto queda imposible de no notar cuándo cambia
   de ciudad. background:var(--paper-2) (el fondo real del panel) es
   necesario para que las tarjetas no se transparenten debajo al scrollear. */
.aliados-grupo-ciudad__titulo {
  display: flex; align-items: center; gap: 8px;
  position: sticky; top: 0; z-index: 1;
  background: var(--paper-2);
  border-left: 3px solid var(--terracota);
  padding: 5px 0 5px 10px; margin: 0 0 8px;
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
  color: var(--ink);
}
.aliados-grupo-ciudad__n { font-size: 11px; font-weight: 400; text-transform: none; color: var(--gris-2); background: var(--paper); border-radius: var(--r-pill); padding: 1px 8px; }
.aliados-grupo-ciudad .aliado-card + .aliado-card { margin-top: 10px; }

@media (max-width: 900px) {
  .aliados-layout { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
  .vista-aliados { padding: 22px 18px; }
}

.ve-head { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
.ve-head h2 { font-size: 22px; font-weight: 500; margin: 0; }
.ve-head .ve-invitar { margin-left: auto; }
.ve-volver {
  background: none;
  border: none;
  color: var(--terracota-d);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  padding: 0;
}

.ve-resumen { margin-bottom: 24px; }
.ve-barra {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--line);
  overflow: hidden;
  margin-bottom: 8px;
}
.ve-barra__fill {
  height: 100%;
  background: var(--terracota);
  border-radius: var(--r-pill);
  transition: width 300ms var(--ease-out);
}
.ve-stats { margin: 0; font-size: 13.5px; color: var(--ink-2); }
.ve-stats b { font-weight: 600; }

.ve-tabla-wrap { overflow-x: auto; }
.ve-tabla { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.ve-tabla th {
  text-align: left;
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--gris);
  font-weight: 500;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.ve-tabla td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  color: var(--ink-2);
  vertical-align: middle;
}
.ve-tabla tbody tr:last-child td { border-bottom: none; }

/* Verde/rojo son deliberadamente distintos de --terracota (que ya significa
   "evento" en las badges de la ficha) para no confundir dos señales
   distintas -- acá el color ES el dato (asistió/no asistió), con el
   "title" del span como respaldo no visual. */
.ve-dot {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  transition: transform 160ms ease-out;
}
.ve-dot--verde { background: #3F7D4A; }
.ve-dot--rojo { background: #B3261E; }

.ve-dot-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: none;
  border-radius: 50%;
  cursor: pointer;
  transition: background 150ms ease;
}
/* Círculo gris real (no casi-blanco como --paper-2) -- necesita leerse a
   simple vista como "esto se puede presionar", no solo insinuarse. */
.ve-dot-btn:hover { background: var(--line); }
.ve-dot-btn:hover .ve-dot { transform: scale(1.2); }
.ve-dot-btn:active .ve-dot { transform: scale(1); }
.ve-dot-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.ve-dot-btn:disabled { cursor: default; opacity: .6; }
.ve-dot-btn:disabled:hover { background: none; }
.ve-dot-btn:disabled:hover .ve-dot { transform: none; }

/* ---------- Panel de administración (admin.js) ---------- */
.overlay__panel--admin {
  display: flex;
  flex-direction: column;
  max-width: 640px;
  height: min(85vh, 680px);
}
.overlay__panel--admin .overlay__head { flex: 0 0 auto; }

.admin-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; flex: 0 0 auto; }
.admin-tab {
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--gris);
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 7px 14px;
  cursor: pointer;
}
.admin-tab--activa { color: var(--paper); background: var(--ink); border-color: var(--ink); }

.admin-contenido { flex: 1; min-height: 0; overflow-y: auto; }

.admin-lista { display: flex; flex-direction: column; gap: 2px; }
.admin-fila {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 4px;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.admin-fila:last-child { border-bottom: none; }
.admin-fila__nombre { flex: 1 1 160px; font-size: 13px; color: var(--ink); font-weight: 500; }
.admin-fila__valor { flex: 0 0 auto; font-size: 12.5px; color: var(--ink-2); min-width: 110px; }
.admin-fila__control { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.admin-vacio { color: var(--gris); font-style: italic; }

.admin-input {
  font-family: inherit;
  font-size: 13px;
  color: var(--ink);
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper-2);
  width: 150px;
}
.admin-input:focus-visible { outline: none; border-color: var(--terracota); box-shadow: var(--focus-ring); }

.admin-agregar-correo { display: flex; gap: 8px; margin-bottom: 14px; flex: 0 0 auto; }
.admin-agregar-correo .admin-input { flex: 1; width: auto; }

/* ---------- "Nuevo evento": formulario + Excel de invitados (importar-evento.js) ---------- */
.overlay__panel--evento {
  display: flex;
  flex-direction: column;
  max-width: 560px;
  height: min(88vh, 720px);
}
.overlay__panel--evento .overlay__head { flex: 0 0 auto; }
.evento-modal__scroll { flex: 1; min-height: 0; overflow-y: auto; }

.evento-import__separador {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0;
  color: var(--gris);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .04em;
}
.evento-import__separador::before,
.evento-import__separador::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

.evento-import__drop {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border: 2px dashed var(--line);
  border-radius: var(--r-md);
  padding: 22px 16px;
  cursor: pointer;
  color: var(--gris);
  font-size: 13px;
  transition: border-color 150ms ease, background 150ms ease;
}
.evento-import__drop b { color: var(--ink-2); }
.evento-import__drop--activo,
.evento-import__drop:hover {
  border-color: var(--terracota);
  background: var(--paper-2);
}

.evento-import__acciones-lista { display: flex; gap: 8px; margin: 14px 0 8px; }

.evento-import-lista { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto; }
.evento-import-fila {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  cursor: pointer;
}
.evento-import-fila:last-child { border-bottom: none; }
.evento-import-fila__nombre { flex: 1; color: var(--ink); font-weight: 500; }
.evento-import-fila__estado { font-size: 11px; color: var(--gris); text-transform: uppercase; letter-spacing: .03em; }
.evento-import-fila__estado--nuevo { color: var(--terracota-d); }
.evento-import-fila__invitados { font-size: 12px; color: var(--gris); white-space: nowrap; }

/* ---------- Selector "Invitar contactos" (vista de evento) ---------- */
/* Mismo motivo que .overlay__panel--wa (antes en whatsapp-demo): altura fija
   + flex column para que la lista scrollee sola y el buscador/botón queden
   anclados -- .overlay__panel por defecto scrollea todo el panel entero. */
.overlay__panel--invitar {
  display: flex;
  flex-direction: column;
  max-width: 480px;
  height: min(80vh, 600px);
}
.overlay__panel--invitar .overlay__head { flex: 0 0 auto; }

.invitar-contactos { display: flex; flex-direction: column; gap: 12px; min-height: 0; flex: 1; }
.invitar-contactos > input[type="text"] {
  font-family: inherit;
  font-size: 14px;
  color: var(--ink);
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper-2);
  flex: 0 0 auto;
}
.invitar-contactos > input[type="text"]:focus-visible {
  outline: none;
  border-color: var(--terracota);
  box-shadow: var(--focus-ring);
}
.invitar-contactos__lista {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 6px;
}
.invitar-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--ink-2);
  cursor: pointer;
}
.invitar-item:hover { background: var(--paper-2); }
.invitar-item--ya { color: var(--gris); }
.invitar-item input[type="checkbox"] { flex: 0 0 auto; }

/* ---------- Ficha completa en overlay (§6, directorio.js: abrirFichaClienteOverlay/abrirFichaContactoOverlay) ---------- */
/* El panel default (.overlay__panel, max-width 560px / max-height ~640px) se
   queda chico para una tarjeta de contacto con historial de interacciones
   completo -- mismo motivo que --admin/--evento/--invitar de arriba, un poco
   más ancho/alto. */
.overlay__panel--ficha { max-width: 640px; max-height: min(90vh, 780px); }

@media (max-width: 768px) {
  .vista-evento { padding: 22px 18px; }
}
