/* ══════════════════════════════════════════════════════════════
   APEX DASHBOARD V3 · COMPONENTES
   Cada bloque es un componente del Design System. Si una pantalla
   necesita algo nuevo, se agrega acá, no dentro de la pantalla.
   ══════════════════════════════════════════════════════════════ */

/* ── Surface ─────────────────────────────────────────────────── */
.surface{
  background:var(--surface);
  border:1px solid var(--hair);
  border-radius:var(--r-lg);
  box-shadow:var(--sh-2);
  transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.surface--flat{box-shadow:none}
.surface--sunken{background:var(--surface-2);box-shadow:none}
.surface-pad{padding:var(--sp-6)}

/* ── GlassSurface ────────────────────────────────────────────── */
.glass{
  background:var(--glass-bg);
  -webkit-backdrop-filter:saturate(var(--glass-sat)) blur(var(--glass-blur));
  backdrop-filter:saturate(var(--glass-sat)) blur(var(--glass-blur));
  border:1px solid var(--glass-line);
}
.glass--dark{
  background:var(--glass-dark-bg);
  border-color:var(--glass-dark-line);
  color:#fff;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{background:var(--surface)}
  .glass--dark{background:rgba(16,16,19,.88)}
}

/* ── Icon ────────────────────────────────────────────────────── */
.ico{
  width:var(--icon);height:var(--icon);flex:0 0 auto;
  stroke:currentColor;stroke-width:var(--icon-stroke);fill:none;
  stroke-linecap:round;stroke-linejoin:round;
}
.ico--sm{width:var(--icon-sm);height:var(--icon-sm)}
.ico--lg{width:22px;height:22px}

/* ── Avatar ──────────────────────────────────────────────────── */
.avatar{
  width:30px;height:30px;border-radius:var(--r-full);flex:0 0 auto;
  display:grid;place-items:center;
  background:linear-gradient(140deg,var(--avatar-g1),var(--avatar-g2));
  color:#fff;font-size:var(--fs-xs);font-weight:var(--fw-bold);letter-spacing:.02em;
}
/* El avatar de la topbar es un control real (menú de cuenta), no un adorno.
   Neutro: identificar al usuario no es una advertencia. */
.avatar--btn{
  cursor:pointer;border:1px solid rgba(255,255,255,.18);
  transition:border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.avatar--btn:hover{border-color:rgba(255,255,255,.4)}
.avatar--btn:active{transform:scale(.96)}

/* ── Button ──────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-3);
  height:34px;padding:0 var(--sp-6);border-radius:var(--r-sm);
  font-size:var(--fs-sm);font-weight:var(--fw-medium);
  background:var(--ink);color:var(--panel);
  transition:opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
             background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn:hover{opacity:.88}
.btn:active{transform:scale(.98)}
.btn--accent{background:var(--apex-accent);color:var(--apex-accent-ink)}
.btn--ghost{background:transparent;color:var(--ink);border:1px solid var(--line)}
.btn--ghost:hover{background:var(--surface-3);opacity:1}
.btn--soft{background:var(--surface-3);color:var(--ink)}
.btn--sm{height:28px;padding:0 var(--sp-5);font-size:var(--fs-xs)}
.btn[disabled]{opacity:.4;pointer-events:none}

/* botón de icono */
.iconbtn{
  width:34px;height:34px;border-radius:var(--r-sm);
  display:grid;place-items:center;position:relative;color:var(--ink-2);
  transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.iconbtn:hover{background:var(--surface-3);color:var(--ink)}
.iconbtn--on-photo{color:rgba(255,255,255,.88)}
.iconbtn--on-photo:hover{background:rgba(255,255,255,.12);color:#fff}
.iconbtn .notif{
  position:absolute;top:6px;right:6px;width:7px;height:7px;border-radius:50%;
  background:var(--apex-accent);box-shadow:0 0 0 2px rgba(0,0,0,.35);
}

/* ── Segmented control (Lista | Visual) ──────────────────────
   Elección entre modos equivalentes, no jerárquicos. */
.seg{
  display:inline-flex;gap:2px;padding:2px;
  background:var(--surface-3);border-radius:var(--r-sm);flex:0 0 auto;
}
.seg button{
  height:30px;padding:0 var(--sp-6);border-radius:var(--r-xs);
  font-size:var(--fs-sm);font-weight:var(--fw-medium);color:var(--ink-2);
  transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.seg button:hover{color:var(--ink)}
.seg button.is-on{
  background:var(--surface);color:var(--ink);font-weight:var(--fw-semi);
  box-shadow:var(--sh-1);
}

/* ── Tabs ────────────────────────────────────────────────────
   Secciones del mismo objeto. La marca del activo es una línea
   de acento fina: acá el color sí comunica posición, no adorna. */
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--hair);margin:var(--sp-8) 0 var(--sp-6)}
.tab{
  position:relative;padding:var(--sp-4) var(--sp-5);
  font-size:var(--fs-sm);font-weight:var(--fw-medium);color:var(--ink-2);
  transition:color var(--dur-fast) var(--ease);
}
.tab:hover{color:var(--ink)}
.tab.is-on{color:var(--ink);font-weight:var(--fw-semi)}
.tab.is-on::after{
  content:'';position:absolute;left:var(--sp-4);right:var(--sp-4);bottom:-1px;
  height:2px;border-radius:2px;background:var(--ink);
}

/* ── Controles de vidrio sobre fotografía ────────────────────
   Buscador y chip de fecha comparten exactamente esta receta para
   que se lean como una sola familia integrada a la topbar:
   translucidez real + brillo interno superior + borde finísimo. */
.glass--ctl{
  background:var(--glass-ctl-bg);
  -webkit-backdrop-filter:saturate(170%) blur(var(--glass-ctl-blur));
  backdrop-filter:saturate(170%) blur(var(--glass-ctl-blur));
  border:1px solid var(--glass-ctl-line);
  box-shadow:inset 0 1px 0 var(--glass-ctl-hi), 0 2px 14px rgba(0,0,0,.22);
  color:#fff;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass--ctl{background:rgba(24,24,28,.86)}
}

.searchfield{
  display:flex;align-items:center;gap:var(--sp-4);
  flex:1;max-width:430px;
  height:38px;padding:0 var(--sp-5) 0 var(--sp-6);border-radius:var(--r-full);
  min-width:0;color:rgba(255,255,255,.66);
  font-size:var(--fs-sm);cursor:text;
  transition:border-color var(--dur) var(--ease), background var(--dur) var(--ease),
             box-shadow var(--dur) var(--ease);
}
.searchfield:hover{
  background:rgba(255,255,255,.13);
  border-color:rgba(255,255,255,.26);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22), 0 4px 18px rgba(0,0,0,.26);
}
.searchfield .ico{opacity:.8}
/* Nunca envuelve: si no hay lugar, se recorta con puntos suspensivos.
   En el iPhone real el texto se partía en dos líneas y rompía la
   altura de la topbar. */
.sf-t{
  flex:1;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  text-align:left;
}
.searchfield .kbd{
  margin-left:auto;font-size:var(--fs-2xs);font-weight:var(--fw-medium);
  padding:3px 7px;border-radius:var(--r-xs);letter-spacing:.02em;
  background:rgba(255,255,255,.13);color:rgba(255,255,255,.8);
  border:1px solid rgba(255,255,255,.10);
}

/* La tarjeta de fecha se eliminó: no aportaba información operativa
   suficiente para ocupar ese espacio, y "Semana N" menos todavía.
   La fecha vive ahora como una línea discreta sobre el saludo
   (.hero-fecha en layout.css). */

/* ── Input / Select ──────────────────────────────────────────── */
.input,.select{
  height:34px;padding:0 var(--sp-5);border-radius:var(--r-sm);
  background:var(--surface);border:1px solid var(--line);color:var(--ink);
  font-size:var(--fs-input);outline:none;
  transition:border-color var(--dur-fast) var(--ease);
  /* Los controles de formulario traen un ancho intrínseco propio: un
     `<select>` quiere el ancho de su opción más larga y un
     `<input type="date">` en iOS trae el ancho del formato de fecha
     completo. `width:100%` NO baja de ese mínimo, así que sin esto un
     control puede empujar su columna y provocar scroll horizontal
     real. No cambia nada en desktop, donde el contenedor siempre es
     más ancho que el control. */
  min-width:0;max-width:100%;
}
.input::placeholder{color:var(--ink-3)}
.input:focus,.select:focus{border-color:var(--ink-3)}
.select{
  cursor:pointer;padding-right:var(--sp-9);appearance:none;-webkit-appearance:none;
  background-image:
    linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),
    linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);
  background-position:calc(100% - 15px) 15px, calc(100% - 10px) 15px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;
}

/* ── Badge / StatusBadge ─────────────────────────────────────── */
.badge{
  display:inline-grid;place-items:center;min-width:19px;height:19px;padding:0 var(--sp-2);
  border-radius:var(--r-full);background:var(--apex-accent);color:#fff;
  font-size:var(--fs-2xs);font-weight:var(--fw-bold);
}
.badge--mute{background:var(--surface-3);color:var(--ink-2)}

.status{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  height:22px;padding:0 var(--sp-4);border-radius:var(--r-xs);
  font-size:var(--fs-xs);font-weight:var(--fw-medium);
  background:var(--surface-3);color:var(--ink-2);white-space:nowrap;
}
.status i{width:5px;height:5px;border-radius:50%;background:currentColor;flex:0 0 auto}
.status--ok{background:var(--ok-soft);color:var(--ok)}
.status--warn{background:var(--warn-soft);color:var(--warn)}
.status--danger{background:var(--danger-soft);color:var(--danger)}
.status--info{background:var(--info-soft);color:var(--info)}
.status--accent{background:var(--apex-accent-soft);color:var(--apex-accent)}

/* ── CardHeader (título + acción) ────────────────────────────── */
.card-head{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-6) var(--sp-6) var(--sp-5);
}
.card-head .t-h2{flex:1;min-width:0}
.card-link{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  font-size:var(--fs-sm);color:var(--ink-2);
  transition:color var(--dur-fast) var(--ease), gap var(--dur-fast) var(--ease);
}
.card-link:hover{color:var(--ink);gap:var(--sp-3)}

/* ── KpiCard ─────────────────────────────────────────────────── */
.kpi{padding:var(--sp-6);display:flex;flex-direction:column;gap:var(--sp-4)}
.kpi-top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-5)}
.kpi-main{min-width:0;flex:1}
.kpi-label{font-size:var(--fs-sm);color:var(--ink-2);font-weight:var(--fw-medium)}
.kpi-value{font-size:var(--fs-kpi);font-weight:var(--fw-semi);line-height:1}
.kpi-spark{flex:0 0 auto;height:30px;width:58px}
.kpi-foot{display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-xs);color:var(--ink-3)}
.delta{display:inline-flex;align-items:center;gap:3px;font-weight:var(--fw-semi)}
.delta--up{color:var(--ok)}
.delta--down{color:var(--danger)}

/* ── OperationalMetric ───────────────────────────────────────── */
.metric{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-5) var(--sp-6);
}
.metric-ico{
  width:34px;height:34px;border-radius:var(--r-sm);flex:0 0 auto;
  display:grid;place-items:center;background:var(--surface-2);color:var(--ink-2);
}
.metric-body{min-width:0}
.metric-value{font-size:var(--fs-metric);font-weight:var(--fw-semi);line-height:1.1}
.metric-label{font-size:var(--fs-xs);color:var(--ink-2);margin-top:2px;line-height:1.3}

/* ── AttentionItem ───────────────────────────────────────────── */
.attention{display:flex;flex-direction:column}
.att-item{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-5) var(--sp-6);
  border-top:1px solid var(--hair);
  text-align:left;width:100%;
  transition:background var(--dur-fast) var(--ease);
}
.att-item:hover{background:var(--surface-2)}
.att-ico{
  width:26px;height:26px;border-radius:var(--r-full);flex:0 0 auto;
  display:grid;place-items:center;
}
.att-ico--danger{background:var(--danger-soft);color:var(--danger)}
.att-ico--warn{background:var(--warn-soft);color:var(--warn)}
.att-ico--accent{background:var(--apex-accent-soft);color:var(--apex-accent)}
.att-ico--info{background:var(--info-soft);color:var(--info)}
.att-body{flex:1;min-width:0}
.att-title{font-size:var(--fs-body);font-weight:var(--fw-medium);line-height:1.3}
.att-context{font-size:var(--fs-xs);color:var(--ink-3);margin-top:2px}
.att-action{
  display:inline-flex;align-items:center;gap:var(--sp-2);flex:0 0 auto;
  font-size:var(--fs-sm);color:var(--ink-2);
  transition:color var(--dur-fast) var(--ease), gap var(--dur-fast) var(--ease);
}
.att-item:hover .att-action{color:var(--ink);gap:var(--sp-3)}

/* ── DataList ────────────────────────────────────────────────── */
.dlist{display:flex;flex-direction:column}
.drow{
  display:grid;align-items:center;gap:var(--sp-5);
  padding:var(--sp-4) var(--sp-6);
  border-top:1px solid var(--hair);
  font-size:var(--fs-sm);width:100%;text-align:left;
  transition:background var(--dur-fast) var(--ease);
}
.drow:hover{background:var(--surface-2)}
.dthumb{
  width:34px;height:34px;border-radius:var(--r-xs);flex:0 0 auto;
  background:var(--surface-2);overflow:hidden;display:grid;place-items:center;padding:2px;
}
.dthumb img{max-width:100%;max-height:100%;object-fit:contain}
.dname{font-weight:var(--fw-medium);line-height:1.3;letter-spacing:-.008em}
.dsub{font-size:var(--fs-xs);color:var(--ink-3);margin-top:1px}
.dtime{font-size:var(--fs-xs);color:var(--ink-3);white-space:nowrap}
.dval{font-weight:var(--fw-semi);white-space:nowrap}
.ta-right{text-align:right}

/* ── Charts ──────────────────────────────────────────────────── */
.chart{width:100%;padding:0 var(--sp-6) var(--sp-6)}
.chart svg{width:100%;height:auto;display:block;overflow:visible}
.legend{display:flex;align-items:center;gap:var(--sp-6);flex-wrap:wrap;padding:0 var(--sp-6) var(--sp-5)}
.legend-item{display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-xs);color:var(--ink-2)}
.legend-item i{width:8px;height:8px;border-radius:2px;display:block;flex:0 0 auto}
.legend-item i.round{border-radius:50%}
/* punto de leyenda dentro del donut: el color va inline, la forma acá */
.legend-dot{display:block;flex:0 0 auto}

.donut-wrap{display:flex;align-items:center;gap:var(--sp-6);padding:0 var(--sp-6) var(--sp-6)}
.donut-legend{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--sp-4)}
.donut-legend .row{display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-xs)}
.donut-legend .row .nm{flex:1;color:var(--ink-2)}
.donut-legend .row .pc{font-weight:var(--fw-semi);color:var(--ink)}

/* ── SideSheet ───────────────────────────────────────────────── */
.scrim{
  position:fixed;inset:0;z-index:70;background:rgba(0,0,0,.4);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
  opacity:0;pointer-events:none;transition:opacity var(--dur) var(--ease);
}
.scrim[data-open="true"]{opacity:1;pointer-events:auto}
.sheet{
  position:fixed;top:0;right:0;bottom:0;z-index:75;width:min(720px,94vw);
  display:flex;flex-direction:column;background:var(--panel);
  border-left:1px solid var(--line);box-shadow:var(--sh-3);
  transform:translateX(101%);transition:transform var(--dur-slow) var(--ease);
}
.sheet[data-open="true"]{transform:none}
.sheet-head{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-5) var(--sp-7);border-bottom:1px solid var(--hair);flex-shrink:0;
}
.sheet-body{flex:1;overflow-y:auto;padding:var(--sp-7)}
.sheet-foot{
  display:flex;align-items:center;justify-content:flex-end;gap:var(--sp-4);
  padding:var(--sp-5) var(--sp-7) calc(var(--sp-5) + env(safe-area-inset-bottom));
  border-top:1px solid var(--hair);flex-shrink:0;
}
@media (max-width:860px){
  /* ── Sheets en el teléfono ──────────────────────────────────
     La sheet sube desde abajo y ocupa casi toda la pantalla. Tres
     cosas que hay que garantizar y que no son automáticas:

     1. `dvh` y no `vh`. Con `vh`, la barra de Safari que aparece y
        desaparece al hacer scroll cambia la altura y la sheet salta.
     2. El cuerpo es el ÚNICO que scrollea. Si scrollea la sheet
        entera, el footer de acciones se va de pantalla.
     3. El último campo del formulario no puede quedar debajo del
        footer: el cuerpo lleva un colchón del alto del footer. */
  .sheet{
    width:100%;top:auto;height:92dvh;max-height:92dvh;border-left:none;
    border-radius:var(--r-2xl) var(--r-2xl) 0 0;transform:translateY(101%);
    overflow:hidden;                 /* el scroll vive en .sheet-body */
  }
  .sheet[data-open="true"]{transform:none}

  .sheet-head{
    padding:var(--sp-5) var(--sp-6);
    position:sticky;top:0;z-index:2;
    background:var(--panel);         /* no se transparenta al scrollear */
  }
  .sheet-body{
    padding:var(--sp-6);
    /* colchón: sin esto el último input queda tapado por el footer */
    padding-bottom:calc(var(--sp-9) + env(safe-area-inset-bottom));
    -webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;     /* no arrastra la página de atrás */
  }
  .sheet-foot{
    padding:var(--sp-5) var(--sp-6) calc(var(--sp-5) + env(safe-area-inset-bottom));
    background:var(--panel);
    flex-wrap:wrap;
  }
  /* La acción principal es la última del footer y ocupa toda la fila:
     con el teclado abierto es lo único que tiene que alcanzarse con
     el pulgar sin buscar. */
  .sheet-foot > .btn:last-child{flex:1 0 100%;min-height:46px;order:-1}
  .sheet-foot > .btn{min-height:44px}

  /* Con el teclado virtual abierto la ventana se achica: la sheet se
     adapta al viewport real en vez de quedar con una altura fija que
     deja las acciones abajo del teclado. */
  @supports (height:100dvh){
    .sheet{height:min(92dvh, calc(100dvh - var(--sp-5)))}
  }
}

/* ── CommandPalette ──────────────────────────────────────────── */
.cmdk{
  position:fixed;inset:0;z-index:90;display:flex;justify-content:center;
  /* `dvh` y no `vh`: con `vh` la barra de Safari y el teclado dejan la
     paleta calculada para una ventana que ya no existe. */
  padding-top:12dvh;opacity:0;pointer-events:none;
  transition:opacity var(--dur) var(--ease);
}
.cmdk[data-open="true"]{opacity:1;pointer-events:auto}
.cmdk-bg{position:absolute;inset:0;background:rgba(0,0,0,.42);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
/* ══════════════════════════════════════════════════════════════
   FLASH NEGRO AL ESCRIBIR — causa y corrección
   ──────────────────────────────────────────────────────────────
   Síntoma: en iPhone, cada letra tipeada en el buscador hacía
   parpadear la pantalla en negro.

   La receta completa estaba acá:

     · `.cmdk-box` tenía la clase `glass`, o sea `backdrop-filter`.
     · Más `overflow:hidden` y `transform`, que la fuerzan a tener su
       propia capa de composición.
     · Y `#cmdk-list`, DESCENDIENTE de esa caja, se reescribía entero
       con `innerHTML` en cada tecla, creando `<img>` nuevos cada vez.

   Un elemento con `backdrop-filter` tiene que volver a muestrear y
   desenfocar todo lo que se pinta detrás. Cuando el subárbol que vive
   ADENTRO se destruye y se reconstruye, WebKit invalida la capa y
   rerasteriza. En el frame en que la fuente del backdrop todavía no
   está lista, pinta negro. Es un bug conocido de WebKit, y lo que lo
   dispara es la combinación: contenido que cambia dentro de un
   backdrop-filter con capa propia.

   CORRECCIÓN: el vidrio pasa a `::before`, una capa hermana del
   contenido —no ancestro— que no tiene hijos y no cambia nunca. La
   lista puede reconstruirse todas las veces que quiera: ya no
   invalida el backdrop de nadie. El aspecto es idéntico.
   ══════════════════════════════════════════════════════════════ */
.cmdk-box{
  position:relative;width:min(600px,94vw);
  /* `--vv` la pone app.js con la altura del viewport VISIBLE (la que
     deja libre el teclado). Sin JS o sin soporte, cae a `dvh`. */
  max-height:min(62dvh, calc(var(--vv, 100dvh) - 22dvh));
  display:flex;flex-direction:column;border-radius:var(--r-xl);
  overflow:hidden;box-shadow:var(--sh-3);
  border:1px solid var(--glass-line);
  transform:translateY(-8px) scale(.99);transition:transform var(--dur-slow) var(--ease);
}
/* la capa de vidrio: sin hijos, sin cambios, detrás de todo */
.cmdk-box::before{
  content:'';position:absolute;inset:0;z-index:0;
  background:var(--glass-bg);
  -webkit-backdrop-filter:saturate(var(--glass-sat)) blur(var(--glass-blur));
  backdrop-filter:saturate(var(--glass-sat)) blur(var(--glass-blur));
  pointer-events:none;
}
/* el contenido va encima de esa capa */
.cmdk-input,
.cmdk-list{position:relative;z-index:1}
/* Sin backdrop-filter el vidrio se cae a un sólido legible. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .cmdk-box::before{background:var(--surface)}
}
.cmdk[data-open="true"] .cmdk-box{transform:none}
.cmdk-input{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-6) var(--sp-7);border-bottom:1px solid var(--hair);
}
/* ══════════════════════════════════════════════════════════════
   16px NO ES DECORATIVO
   iOS Safari hace zoom automático al enfocar cualquier input con
   `font-size` menor a 16px. Esto estaba en 15px, y ese único píxel
   es toda la explicación del "se ve agrandado/zoomeado" al tocar el
   campo y escribir: Safari ampliaba la página entera.

   No se arregla con `user-scalable=no` —eso le saca al usuario la
   posibilidad de hacer zoom, que es un problema de accesibilidad
   real— sino subiendo el tamaño al mínimo que Safari no castiga.
   ══════════════════════════════════════════════════════════════ */
.cmdk-input input{
  flex:1;border:none;background:none;outline:none;
  font-size:var(--fs-input-grande);letter-spacing:-.01em;
}
.cmdk-list{overflow-y:auto;padding:var(--sp-3)}
.cmdk-group{
  font-size:var(--fs-2xs);font-weight:var(--fw-semi);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--ink-3);padding:var(--sp-4) var(--sp-5) var(--sp-2);
}
.cmdk-row{
  display:flex;align-items:center;gap:var(--sp-5);width:100%;text-align:left;
  padding:var(--sp-4) var(--sp-5);border-radius:var(--r-sm);
  transition:background var(--dur-fast) var(--ease);
}
.cmdk-row:hover,.cmdk-row[data-sel="true"]{background:var(--surface-3)}

/* ── Toast ───────────────────────────────────────────────────── */
.toast{
  position:fixed;left:50%;bottom:24px;z-index:100;
  transform:translate(-50%,12px);opacity:0;pointer-events:none;
  padding:var(--sp-4) var(--sp-7);border-radius:var(--r-full);
  background:var(--ink);color:var(--panel);
  font-size:var(--fs-sm);font-weight:var(--fw-medium);
  box-shadow:var(--sh-3);max-width:88vw;text-align:center;
  transition:opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.toast[data-open="true"]{opacity:1;transform:translate(-50%,0)}

/* ══════════════════════════════════════════════════════════════
   MODAL DE CONFIRMACIÓN
   ──────────────────────────────────────────────────────────────
   Para las dos operaciones que no se pueden deshacer con un click:
   anular una venta y borrar un gasto. No es un sheet: un sheet es
   para trabajar, un modal es para decidir una sola cosa.

   Por encima de la sheet (75) y del scrim (70), porque se abre DESDE
   una sheet y tiene que taparla.

   Sin `backdrop-filter` en la caja: el contenido se reescribe con
   `innerHTML` en cada apertura, y ésa es exactamente la receta del
   flash negro de WebKit que corregimos en la paleta. El velo del fondo
   sí puede desenfocar: no cambia nunca.
   ══════════════════════════════════════════════════════════════ */
.modal{
  position:fixed;inset:0;z-index:110;
  display:grid;place-items:center;
  padding:var(--sp-7);
  opacity:0;pointer-events:none;
  transition:opacity var(--dur) var(--ease);
}
.modal[data-open="true"]{opacity:1;pointer-events:auto}
.modal-bg{
  position:absolute;inset:0;
  background:rgba(0,0,0,.52);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
}
.modal-box{
  position:relative;z-index:1;
  width:min(460px,100%);max-height:88dvh;overflow-y:auto;
  padding:var(--sp-8);border-radius:var(--r-xl);
  background:var(--panel);border:1px solid var(--line);
  box-shadow:var(--sh-3);
  transform:translateY(8px) scale(.985);
  transition:transform var(--dur) var(--ease);
  overscroll-behavior:contain;
}
.modal[data-open="true"] .modal-box{transform:none}
@media (max-width:860px){
  /* El modal se apoya abajo, al alcance del pulgar, igual que el
     popover de cuenta. Y respeta el área segura. */
  .modal{
    place-items:end center;
    padding:var(--sp-5) var(--sp-5) calc(var(--sp-5) + env(safe-area-inset-bottom));
  }
  .modal-box{width:100%;max-height:82dvh;padding:var(--sp-7) var(--sp-6)}
  /* las acciones a todo el ancho: decidir con una mano */
  .modal-box .btn{min-height:44px;flex:1}
}
@media (prefers-reduced-motion:reduce){
  .modal-box{transition:none;transform:none}
}

/* ── Estados ─────────────────────────────────────────────────── */
.skeleton{
  background:var(--surface-2);border-radius:var(--r-md);position:relative;overflow:hidden;
}
.skeleton::after{
  content:'';position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,var(--surface-3),transparent);
  animation:apex-sweep 1.3s infinite;
}
@keyframes apex-sweep{100%{transform:translateX(100%)}}

.state{padding:var(--sp-10) var(--sp-7);text-align:center}
.state-title{font-size:var(--fs-h2);font-weight:var(--fw-semi);margin-bottom:var(--sp-3)}
.state-desc{font-size:var(--fs-sm);color:var(--ink-2);max-width:46ch;margin:0 auto var(--sp-6);line-height:1.6}
.state code{
  font-family:var(--font-num);font-size:var(--fs-xs);
  background:var(--surface-3);padding:2px 6px;border-radius:var(--r-xs);
}

/* aparición suave de secciones */
.reveal{opacity:0;transform:translateY(10px)}
.reveal[data-in="true"]{
  opacity:1;transform:none;
  transition:opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}

/* ══════════════════════════════════════════════════════════════
   TOOLTIP DE MINI GRÁFICO
   Las KpiCard no llevan ejes ni etiquetas fijas: el dato puntual
   aparece sólo al pasar por encima.
   ══════════════════════════════════════════════════════════════ */
.spark-tip{
  position:fixed;z-index:120;transform:translateX(-50%);
  display:flex;flex-direction:column;gap:1px;align-items:center;
  padding:var(--sp-3) var(--sp-5);border-radius:var(--r-sm);
  background:var(--ink);color:var(--panel);
  box-shadow:var(--sh-3);pointer-events:none;white-space:nowrap;
  opacity:0;transition:opacity var(--dur-fast) var(--ease);
}
.spark-tip[data-open="true"]{opacity:1}
.spark-tip-l{font-size:var(--fs-2xs);opacity:.7;letter-spacing:.02em}
.spark-tip-v{font-size:var(--fs-sm);font-weight:var(--fw-semi);font-variant-numeric:tabular-nums}
.kpi-spark{overflow:visible}
.spark-bar,.spark-dot{transition:opacity var(--dur-fast) var(--ease), r var(--dur-fast) var(--ease)}
.spark-bar.is-active{opacity:1}
.spark-dot.is-active{r:2.6}
.spark-hit{cursor:crosshair}

/* ══════════════════════════════════════════════════════════════
   FILTROS
   Un grupo = un eje. Inventario e integridad nunca se mezclan.
   ══════════════════════════════════════════════════════════════ */
.fgroup{display:flex;align-items:center;gap:var(--sp-5);min-width:0;flex-wrap:wrap}
.fgroup-label{
  font-size:var(--fs-2xs);font-weight:var(--fw-semi);letter-spacing:var(--ls-label);
  text-transform:uppercase;color:var(--ink-3);flex:0 0 auto;
}
.fgroup-chips{display:flex;gap:var(--sp-2);flex-wrap:wrap;min-width:0}
.fchip{
  display:inline-flex;align-items:center;gap:var(--sp-3);
  height:30px;padding:0 var(--sp-5);border-radius:var(--r-sm);
  font-size:var(--fs-sm);font-weight:var(--fw-medium);
  background:var(--surface);border:1px solid var(--line);color:var(--ink-2);
  transition:background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
             color var(--dur-fast) var(--ease);
  white-space:nowrap;
}
.fchip:hover:not([disabled]){border-color:var(--ink-3);color:var(--ink)}
.fchip.is-on{background:var(--ink);border-color:var(--ink);color:var(--panel)}
.fchip[disabled]{opacity:.45;cursor:not-allowed}
.fchip-count{font-size:var(--fs-2xs);opacity:.62}
.tag-future,.tag-inferred{
  display:inline-flex;align-items:center;height:16px;padding:0 5px;border-radius:4px;
  font-size:9.5px;font-weight:var(--fw-bold);letter-spacing:.06em;text-transform:uppercase;
  background:var(--info-soft);color:var(--info);
}
.tag-inferred{background:var(--warn-soft);color:var(--warn);margin-left:var(--sp-2)}

/* ══════════════════════════════════════════════════════════════
   STOCK
   ══════════════════════════════════════════════════════════════ */
.stock-summary{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  padding:var(--sp-6) var(--sp-7);gap:var(--sp-6);
}
.sstat{min-width:0}
.sstat-v{font-size:var(--fs-kpi);font-weight:var(--fw-semi);line-height:1}
.sstat-l{font-size:var(--fs-xs);color:var(--ink-2);margin-top:var(--sp-2)}

.stock-toolbar{display:flex;flex-direction:column;gap:var(--sp-5)}
.stock-toolbar-top{display:flex;align-items:center;gap:var(--sp-4);flex-wrap:wrap}
.searchbox{
  display:flex;align-items:center;gap:var(--sp-4);
  height:36px;padding:0 var(--sp-5);border-radius:var(--r-sm);
  background:var(--surface);border:1px solid var(--line);
  flex:1;min-width:230px;max-width:420px;color:var(--ink-3);
  transition:border-color var(--dur-fast) var(--ease);
}
.searchbox:focus-within{border-color:var(--ink-3)}
.searchbox-input{
  flex:1;min-width:0;border:none;background:none;outline:none;
  font-size:var(--fs-input);color:var(--ink);
}
.searchbox-input::placeholder{color:var(--ink-3)}
.searchbox-input::-webkit-search-cancel-button{cursor:pointer}
.stock-filters{
  display:flex;gap:var(--sp-8);flex-wrap:wrap;
  padding:var(--sp-5) var(--sp-6);
  background:var(--surface-2);border:1px solid var(--hair);border-radius:var(--r-md);
}
.stock-count{padding:0 var(--sp-2)}

/* grilla del modo VISUAL */
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:var(--sp-5)}
/* La cola de Marketplace llega a cientos de tarjetas. `content-visibility`
   le dice al navegador que no pinte las que están lejos del viewport;
   `contain-intrinsic-size` le da el alto estimado para que la barra de
   scroll no baile. Sin librerías ni virtualización a mano. */
.pcard{
  content-visibility:auto;
  contain-intrinsic-size:auto 320px;
  display:flex;flex-direction:column;text-align:left;
  background:var(--surface);border:1px solid var(--hair);border-radius:var(--r-lg);
  overflow:hidden;box-shadow:var(--sh-2);
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
             border-color var(--dur) var(--ease);
}
.pcard:hover{transform:translateY(-3px);box-shadow:var(--sh-3);border-color:var(--line)}
.pcard-photo{
  position:relative;aspect-ratio:1/1;background:var(--surface-2);
  display:grid;place-items:center;overflow:hidden;
}
/* Esqueleto mientras la foto viaja. Un bloque gris plano se lee como
   "card rota"; un barrido lento se lee como "está viniendo". Se
   detiene solo: cuando la imagen carga, el `img` la tapa. */
.pcard-photo::before{
  content:'';position:absolute;inset:0;
  background:linear-gradient(100deg,
    var(--surface-2) 0%, var(--surface-3) 50%, var(--surface-2) 100%);
  background-size:220% 100%;
  animation:esqueleto 1.5s ease-in-out infinite;
}
@keyframes esqueleto{
  from{background-position:140% 0}
  to{background-position:-40% 0}
}
/* con la imagen cargada el esqueleto se va */
.pcard-photo img[data-cargada]{opacity:1}
.pcard-photo:has(img[data-cargada])::before{animation:none;opacity:0}

/* Mismo patrón que `.foco-foto`, por el mismo motivo: el porcentaje
   de `max-height` contra un alto derivado de `aspect-ratio` no es
   confiable. Acá se notaba menos porque casi todas las fotos son
   cuadradas, pero una vertical se habría recortado igual. */
.pcard-photo img{
  position:absolute;inset:0;z-index:1;
  width:100%;height:100%;
  padding:8%;box-sizing:border-box;
  object-fit:contain;object-position:center;
  transition:transform var(--dur-slow) var(--ease), opacity var(--dur-slow) var(--ease);
}
/* Sólo se oculta si JS está vivo para volver a mostrarla. Sin
   `[data-js]` una falla del script dejaría la cola en blanco. */
html[data-js] .pcard-photo img{opacity:0}
html[data-js] .pcard-photo img[data-cargada]{opacity:1}

/* Navegadores sin `:has()` no apagan el esqueleto por selector, pero
   la imagen igual lo tapa: queda debajo y no se ve. */
@media (prefers-reduced-motion:reduce){
  .pcard-photo::before{animation:none}
  html[data-js] .pcard-photo img{opacity:1;transition:none}
}
.pcard:hover .pcard-photo img{transform:scale(1.04)}
.pcard-flag{
  position:absolute;top:var(--sp-4);left:var(--sp-4);
  padding:3px 7px;border-radius:var(--r-xs);
  font-size:var(--fs-2xs);font-weight:var(--fw-semi);
}
.pcard-flag--out{background:var(--danger-soft);color:var(--danger)}
.pcard-body{padding:var(--sp-5) var(--sp-5) var(--sp-6);display:flex;flex-direction:column;gap:var(--sp-2)}
.pcard-name{font-size:var(--fs-body);font-weight:var(--fw-medium);line-height:1.32;min-height:2.3em}
.pcard-meta{font-size:var(--fs-xs);color:var(--ink-3)}
.pcard-row{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-4);margin-top:var(--sp-3)}
.pcard-price{font-size:var(--fs-body);font-weight:var(--fw-semi);letter-spacing:var(--ls-num)}
.pcard-stock{font-size:var(--fs-xs);color:var(--ink-2)}
.pcard-int{
  display:flex;align-items:center;gap:var(--sp-2);margin-top:var(--sp-3);
  padding-top:var(--sp-3);border-top:1px solid var(--hair);
  font-size:var(--fs-xs);color:var(--warn);
}

/* placeholder de foto: explícito, nunca una imagen inventada */
.nophoto{
  display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);
  color:var(--ink-3);font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;
}
.nophoto .ico{width:26px;height:26px;stroke-width:1.2}

/* ══════════════ FICHA DE PRODUCTO ══════════════ */
.ficha-top{display:grid;grid-template-columns:minmax(0,290px) minmax(0,1fr);gap:var(--sp-8);align-items:start}
.ficha-photo{
  aspect-ratio:1/1;background:var(--surface-2);border:1px solid var(--hair);
  border-radius:var(--r-lg);display:grid;place-items:center;padding:7%;overflow:hidden;
}
.ficha-photo img{max-width:100%;max-height:100%;object-fit:contain}
.ficha-nums{display:flex;gap:var(--sp-9);margin:var(--sp-7) 0 var(--sp-6)}
.ficha-num{font-size:var(--fs-kpi);font-weight:var(--fw-semi);line-height:1}
.ficha-chips{display:flex;gap:var(--sp-3);flex-wrap:wrap}
.ficha-tabbody{padding-top:var(--sp-2)}
.fields{display:flex;flex-direction:column}
.field{
  display:grid;grid-template-columns:minmax(0,170px) minmax(0,1fr);gap:var(--sp-6);
  padding:var(--sp-5) 0;border-bottom:1px solid var(--hair);align-items:baseline;
}
.field:last-child{border-bottom:none}
.field-l{font-size:var(--fs-sm);color:var(--ink-2)}
.field-v{font-size:var(--fs-body);font-weight:var(--fw-medium)}
.hint{font-size:var(--fs-xs);color:var(--ink-3);line-height:1.5;margin-top:var(--sp-4)}

.fotos-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--sp-5)}
.foto-slot{
  aspect-ratio:1/1;background:var(--surface-2);border:1px solid var(--hair);
  border-radius:var(--r-md);display:grid;place-items:center;padding:8%;overflow:hidden;
}
.foto-slot img{max-width:100%;max-height:100%;object-fit:contain}
.foto-slot--empty{
  border-style:dashed;gap:var(--sp-3);text-align:center;
  font-size:var(--fs-xs);color:var(--ink-3);
}

/* campo en modo edición: el layout no cambia, sólo el control */
.field--edit{align-items:center}
.field--edit .input,.field--edit .select{width:100%}
.req{
  font-size:var(--fs-2xs);font-weight:var(--fw-semi);color:var(--ink-3);
  letter-spacing:.04em;margin-left:var(--sp-2);
}

/* bloque de función futura: se distingue sin gritar */
.futuro-bloque{
  margin-top:var(--sp-7);padding:var(--sp-6);
  background:var(--surface-2);border:1px dashed var(--line);border-radius:var(--r-md);
}
.futuro-head{
  display:flex;align-items:center;gap:var(--sp-3);
  font-size:var(--fs-sm);font-weight:var(--fw-semi);color:var(--ink-2);
  margin-bottom:var(--sp-4);
}

/* checklist de requisitos — derivado de integridad real */
.checklist{display:flex;flex-direction:column;gap:var(--sp-3)}
.checklist-item{
  display:flex;align-items:center;gap:var(--sp-3);
  font-size:var(--fs-sm);color:var(--ink-2);
}
.checklist-item.is-ok{color:var(--ok)}
.checklist-item.is-missing{color:var(--warn)}
.checklist-item.is-future{color:var(--ink-3)}

/* ══════════════════════════════════════════════════════════════
   AVISO DE MODO DEMO
   Si no hay sesión, las pantallas con datos privados muestran
   números simulados. Decirlo no es opcional.
   ══════════════════════════════════════════════════════════════ */
.aviso-demo{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-5) var(--sp-6);border-radius:var(--r-md);
  background:var(--info-soft);color:var(--info);font-size:var(--fs-sm);
}
.aviso-demo > div{flex:1;line-height:1.5}
.aviso-demo b{font-weight:var(--fw-semi)}
.aviso-demo .btn{background:var(--info);color:#fff;flex:0 0 auto}

.login-error{
  margin-top:var(--sp-5);padding:var(--sp-4) var(--sp-5);border-radius:var(--r-sm);
  background:var(--danger-soft);color:var(--danger);font-size:var(--fs-sm);
}

/* ══════════════════════════════════════════════════════════════
   VENTAS
   ══════════════════════════════════════════════════════════════ */
.venta-medio{display:flex;flex-direction:column;gap:3px;align-items:flex-start;min-width:0}

.venta-producto{
  display:flex;align-items:center;gap:var(--sp-5);
  padding:var(--sp-5);border-radius:var(--r-md);
}
.venta-producto-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.venta-producto-meta{display:flex;gap:var(--sp-3);margin-top:var(--sp-3);flex-wrap:wrap}

.venta-resultados{
  margin-top:var(--sp-4);max-height:320px;overflow-y:auto;
  border:1px solid var(--hair);border-radius:var(--r-md);padding:var(--sp-2);
}

/* El bloque de dólares es el único lugar del sistema donde se habla
   de conversión, y deja claro que la hace la persona, no el sistema. */
.venta-usd{
  margin:var(--sp-6) 0;padding:var(--sp-6);border-radius:var(--r-md);
  border:1px solid var(--info-soft);
}
.venta-usd-head{
  display:flex;align-items:center;gap:var(--sp-3);
  font-size:var(--fs-sm);font-weight:var(--fw-semi);color:var(--info);
  margin-bottom:var(--sp-5);
}
.venta-usd-precio{
  font-size:var(--fs-metric);font-weight:var(--fw-semi);
  padding:var(--sp-3) 0;
}

.venta-resumen{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--sp-5);
  padding:var(--sp-6);border-radius:var(--r-md);margin-top:var(--sp-7);
}
.venta-resumen > div{display:flex;flex-direction:column;gap:var(--sp-2);min-width:0}
.venta-resumen-v{font-size:var(--fs-h2);font-weight:var(--fw-semi);letter-spacing:var(--ls-num)}

/* ══════════════════════════════════════════════════════════════
   MARKETPLACE
   ══════════════════════════════════════════════════════════════ */
.market-arranque{
  display:flex;align-items:center;gap:var(--sp-6);width:100%;text-align:left;
  padding:var(--sp-6) var(--sp-7);border-radius:var(--r-lg);
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--sh-2);
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.market-arranque:hover{transform:translateY(-2px);box-shadow:var(--sh-3)}
.market-arranque-ico{
  width:42px;height:42px;border-radius:var(--r-md);flex:0 0 auto;
  display:grid;place-items:center;background:var(--ink);color:var(--panel);
}
.market-arranque-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.market-arranque-t{font-size:var(--fs-h2);font-weight:var(--fw-semi);letter-spacing:-.018em}
.market-arranque-d{font-size:var(--fs-sm);color:var(--ink-2)}

/* ── modo foco ──────────────────────────────────────────────── */
.foco-progreso{
  width:64px;height:4px;border-radius:99px;background:var(--surface-3);
  overflow:hidden;flex:0 0 auto;
}
.foco-progreso i{display:block;height:100%;background:var(--ink);transition:width var(--dur) var(--ease)}

.foco-body{display:flex;flex-direction:column;gap:var(--sp-6)}
/* ══════════════════════════════════════════════════════════════
   LA FOTO DEL MODO PUBLICAR
   ──────────────────────────────────────────────────────────────
   POR QUÉ `contain` no funcionaba, aunque estaba declarado.

   La versión anterior era:

       .foco-foto     { aspect-ratio:4/5; max-height:52vh; overflow:hidden }
       .foco-foto img { max-width:100%; max-height:100%; object-fit:contain }

   Dos cosas se combinaban mal:

   1. `max-height:100%` en el `img` es un PORCENTAJE, y se resuelve
      contra la altura del contenedor. Pero la altura del contenedor
      venía de `aspect-ratio`, que en la primera pasada de layout es
      indefinida. Cuando eso pasa, el porcentaje se trata como
      `none`: el `img` quedaba sin techo y se dibujaba a su alto
      intrínseco. Safari es el más estricto acá.

   2. Ese `img` más alto que la caja, más `overflow:hidden`, recorta
      todo lo que sobra. De ahí que del Toyota se viera sólo la
      franja de arriba del blister.

   Y `object-fit:contain` no hacía nada: al estar el alto en `auto`,
   la caja del `img` YA tenía la proporción de la imagen, así que no
   había nada que ajustar. Estaba declarado y era decorativo.

   LA CORRECCIÓN: darle al `img` una caja definida —el 100% × 100% de
   un contenedor que sí tiene alto definido— y dejar que `contain`
   haga su trabajo de verdad. Con cualquier proporción de foto: las
   reales no son todas iguales. El aire pasa al padding del `img`,
   que con `box-sizing:border-box` se descuenta del área de ajuste.
   ══════════════════════════════════════════════════════════════ */
.foco-foto{
  position:relative;
  aspect-ratio:1/1;max-height:46vh;background:var(--surface-2);
  border:1px solid var(--hair);border-radius:var(--r-lg);
  display:grid;place-items:center;overflow:hidden;
  /* en un contenedor flex en columna el alto tiene que respetarse:
     sin esto el flex puede encogerlo y romper la proporción */
  flex:0 0 auto;
}
.foco-foto img{
  position:absolute;inset:0;
  width:100%;height:100%;
  padding:4%;box-sizing:border-box;
  object-fit:contain;          /* ahora sí ajusta: la caja no tiene la proporción de la foto */
  object-position:center;
}
.foco-acciones-foto{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--sp-4)}

.foco-campo{
  padding:var(--sp-5);border-radius:var(--r-md);
  background:var(--surface-2);border:1px solid var(--hair);
}
.foco-campo-head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);
  margin-bottom:var(--sp-3);
}
.foco-campo-v{font-size:var(--fs-h2);font-weight:var(--fw-medium);letter-spacing:-.015em;line-height:1.35}
.foco-campo-v--texto{font-size:var(--fs-body);font-weight:var(--fw-regular);color:var(--ink-2)}

.foco-foot{gap:var(--sp-3)}
.foco-foot .btn:last-child{flex:1}

@media (max-width:860px){
  .venta-resumen{grid-template-columns:repeat(2,minmax(0,1fr))}

  /* ── La foto en el modo Publicar ────────────────────────────
     La tarea de esta pantalla ES mirar la foto: es lo que se va a
     publicar. Estaba a `1/1` con techo de 38vh, o sea que en un
     teléfono quedaba una franja baja con mucho aire alrededor.

     Ahora el recuadro es vertical (4/5) y llega a 52vh. El resto
     de la pantalla sigue alcanzando: título, precio y descripción
     son compactos y el botón de publicada está fijado abajo.

     `object-fit:contain` NO se toca. La regla de fotografía de
     producto es que el blister se ve entero: recortarlo para
     "llenar" la caja es exactamente lo que no se hace. */
  /* Recuadro vertical y más alto: la tarea de esta pantalla es
     mirar la foto. El aire lo pone el padding del `img`. */
  .foco-foto{aspect-ratio:4/5;max-height:52vh}
  .foco-foto img{padding:3%}

  /* menos aire entre bloques: el alto va a la foto */
  .foco-body{gap:var(--sp-5)}
  .foco-campo{padding:var(--sp-4) var(--sp-5)}

  /* en el celular el botón de publicada tiene que ser grande:
     se usa de pie, con una mano */
  .foco-foot{flex-wrap:wrap}
  .foco-foot .btn:last-child{flex:1 0 100%;height:46px;order:-1}
  .market-arranque{padding:var(--sp-5)}
}
/* ── Paleta en el teléfono ──────────────────────────────────────
   El campo es el protagonista: se apoya cerca del borde superior
   —donde el pulgar no lo tapa y el teclado no lo empuja— y ocupa el
   ancho disponible. */
@media (max-width:860px){
  .cmdk{padding-top:max(var(--sp-6), env(safe-area-inset-top));align-items:flex-start}
  .cmdk-box{width:calc(100% - var(--sp-5) * 2);max-height:calc(var(--vv, 100dvh) - var(--sp-9))}
  .cmdk-input{padding:var(--sp-5) var(--sp-6)}
  .cmdk-row{padding:var(--sp-5)}          /* target táctil cómodo */
}

/* ── Tarjeta de KPI angosta ───────────────────────────────────────
   La condición de este bloque NO es «el teléfono es chico», es «la
   tarjeta de KPI quedó angosta». Son dos cosas distintas y mezclarlas
   fue el error: estas reglas vivían en `max-width:430px` y no entraban
   en un iPhone de 440px, que es justo donde hacen falta.

   Con la fila a dos columnas la tarjeta mide (ancho − 42) / 2, y por
   debajo de 640px el número de 27px ya no entra al lado del sparkline
   (la cuenta completa está en tokens.css). Entonces el sparkline baja
   al renglón de abajo y toma el ancho completo. Es la misma información
   en el mismo orden de lectura: etiqueta, número, tendencia,
   comparación. No es un rediseño: es la misma tarjeta reacomodada.

   Por encima de 640px no cambia nada, así que desktop queda idéntico. */
@media (max-width:640px){
  .kpi{padding:var(--sp-5);gap:var(--sp-3)}
  .kpi-top{flex-direction:column;align-items:stretch;gap:var(--sp-3)}
  .kpi-spark{width:100%;height:26px}
}

@media (max-width:430px){
  /* Esto SÍ es teléfono chico, y se queda acá: la foto cede un poco
     para que el precio siga entrando sin scroll. En un 440 la pantalla
     es más alta y 52vh entra bien. */
  .foco-foto{max-height:46vh}
  .foco-foto img{padding:2.5%}
}

/* Formularios. `.form-group` / `.form-group-t` / `.dropzone` se
   eliminaron en la misma limpieza: eran del prototipo de alta de
   producto de stock.js, que se reemplazó por los formularios de
   forms.js. La zona de arrastre además prometía una subida de fotos
   que todavía no existe. */
.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-5)}
/* `min-width:0`: como ítem de `.form-grid` su mínimo automático es el
   del contenido, así que sin esto se desborda de su columna aunque la
   columna sí pueda encogerse. */
.fg{display:flex;flex-direction:column;gap:var(--sp-3);min-width:0}
.fg label{font-size:var(--fs-sm);font-weight:var(--fw-medium);color:var(--ink-2)}
.fg .input,.fg .select{width:100%}
.sheet--wide{width:min(880px,96vw)}

@media (max-width:1240px){
  .stock-summary{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:860px){
  .stock-summary{grid-template-columns:repeat(2,minmax(0,1fr));padding:var(--sp-5)}
  .sstat-v{font-size:var(--fs-metric)}
  .stock-toolbar-top{gap:var(--sp-3)}
  /* `min-width:0`: los 230px de piso del buscador son razonables en
     desktop, pero en un teléfono de 320px más los padding del panel
     se vuelven un ancho mínimo imposible de cumplir. */
  .searchbox{max-width:none;order:-1;width:100%;flex:1 0 100%;min-width:0}
  .stock-toolbar-top .select{flex:1}
  .stock-toolbar-top .seg{flex:1}
  .stock-toolbar-top .seg button{flex:1}

  /* Target táctil de los filtros. 30px es cómodo con un mouse y no
     con un pulgar: 34 es donde los chips dejan de fallarse. Sube la
     caja, no la tipografía: la escala del diseño no cambia. */
  .fchip{height:34px}
  .seg button{height:34px}
  .stock-filters{
    flex-direction:column;gap:var(--sp-5);
    padding:var(--sp-5);
  }
  .fgroup{flex-direction:column;align-items:flex-start;gap:var(--sp-3);width:100%}
  .fgroup-chips{
    flex-wrap:nowrap;overflow-x:auto;width:100%;
    padding-bottom:var(--sp-2);-webkit-overflow-scrolling:touch;
    scrollbar-width:none;
  }
  .fgroup-chips::-webkit-scrollbar{display:none}
  .pgrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-4)}
  .ficha-top{grid-template-columns:minmax(0,1fr);gap:var(--sp-6)}
  .ficha-photo{max-width:260px}
  .field{grid-template-columns:minmax(0,1fr);gap:var(--sp-2);padding:var(--sp-4) 0}
  .form-grid{grid-template-columns:minmax(0,1fr)}
  .sheet--wide{width:100%}
}
/* Antes acá había `.pgrid` y `.stock-summary` a UNA columna por debajo
   de 420px. Es la misma decisión que se revisó en los KPI: a una
   columna, una tarjeta de producto ocupa ~300px de alto y el stock
   (171 productos) se vuelve un rollo infinito; y cuatro números
   apilados comen una pantalla. A dos columnas la tarjeta mide 173px,
   que es el ancho normal de una grilla de productos en un teléfono, y
   el nombre sigue entrando con truncado. Se quedan en dos columnas. */

/* ══════════════════════════════════════════════════════════════
   PANTALLAS DE OPERACIÓN
   Gastos · Consignaciones · Pedidos Web · Deudas · Estadísticas.
   Todo con tokens: cero valores de color, espacio o radio a mano.
   ══════════════════════════════════════════════════════════════ */

/* Las dos líneas de una fila de datos se apilan. Sin esto el
   subtítulo se pega al nombre en la misma línea. */
.dname,.dsub{display:block}

/* ── Grillas de composición ─────────────────────────────────── */
.grid{display:grid;gap:var(--sp-5);align-items:start}
.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.g23{grid-template-columns:minmax(0,1.45fr) minmax(0,1fr)}

/* Gastos es la única pantalla que mete una tabla de SEIS columnas
   dentro de una grilla de dos. Con `g23` la tabla recibe el 59% del
   ancho, y a 1280px eso son ~610px para 596px de columnas fijas más
   los gaps: desbordaba y el encabezado se pisaba con el contenido.

   Ahora la tabla va a ancho completo hasta que haya lugar de verdad
   para las dos cosas. El corte es 1560px, no 1240px: medido sobre lo
   que la tabla realmente necesita, no sobre un breakpoint redondo.
   Debajo de eso, el reparto por categoría va abajo, que es donde
   mejor se lee igual. */
.gastos-layout{grid-template-columns:minmax(0,1fr)}
@media (min-width:1560px){
  .gastos-layout{grid-template-columns:minmax(0,1.45fr) minmax(0,1fr)}
}

/* ── Encabezado de lista ────────────────────────────────────── */
.lh{
  display:grid;align-items:center;gap:var(--sp-5);
  padding:var(--sp-4) var(--sp-6);
  font-size:var(--fs-xs);font-weight:var(--fw-medium);
  color:var(--ink-3);text-transform:uppercase;letter-spacing:.06em;
}
.lh .ta-right{text-align:right}

/* ── Sublista dentro de fichas ──────────────────────────────── */
.list{display:flex;flex-direction:column}
.sec-t{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4)}

/* ── Barra horizontal comparativa ───────────────────────────── */
.hbar{
  /* tercera columna `auto`: el valor puede llevar el porcentaje al
     lado y un ancho fijo lo recortaría. La barra es la que cede. */
  display:grid;grid-template-columns:minmax(0,132px) minmax(0,1fr) auto;
  align-items:center;gap:var(--sp-4);
  padding:var(--sp-3) 0;font-size:var(--fs-sm);
}
.hbar .nm{color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hbar .tr{height:8px;border-radius:var(--r-full);background:var(--surface-3);overflow:hidden}
.hbar .tr > i{
  display:block;height:100%;border-radius:var(--r-full);
  background:var(--serie-1);
  transition:width var(--dur-slow) var(--ease);
}
.hbar .vl{text-align:right;font-weight:var(--fw-semi);white-space:nowrap}
/* participación sobre el total del período: acompaña al monto sin
   competir con él */
.hbar-pct{color:var(--ink-3);font-weight:var(--fw-regular);font-size:var(--fs-xs)}

/* ── Barra de progreso simple ───────────────────────────────── */
.bar{
  display:block;height:6px;border-radius:var(--r-full);
  background:var(--surface-3);overflow:hidden;
}
.bar > i{
  display:block;height:100%;border-radius:var(--r-full);
  background:var(--serie-1);
  transition:width var(--dur-slow) var(--ease);
}
.bar > i.ok{background:var(--ok)}
.bar > i.warn{background:var(--warn)}

/* ── Deudas ─────────────────────────────────────────────────── */
.deuda-barra{display:block;min-width:0}
.deuda-barra-top{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--sp-3);margin-bottom:var(--sp-2);
}
/* La moneda es información, no decoración: sólo se marca lo que
   no es pesos, porque el peso es el caso normal de la caja. */
.tag-moneda{
  display:inline-block;margin-left:var(--sp-2);
  padding:1px 5px;border-radius:var(--r-xs);
  font-size:10px;font-weight:var(--fw-semi);letter-spacing:.04em;
  background:var(--info-soft);color:var(--info);
  vertical-align:middle;
}

/* ── Consignaciones: el flujo real de la mercadería ─────────── */
.consig-flujo{
  display:flex;align-items:stretch;gap:var(--sp-2);
  padding:var(--sp-6);margin-bottom:var(--sp-5);
}
.consig-paso{
  flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;
  padding:var(--sp-4) var(--sp-5);
  border-radius:var(--r-sm);background:var(--surface-2);
}
.consig-paso-n{font-size:var(--fs-metric);font-weight:var(--fw-semi);line-height:1.1}
.consig-paso-t{font-size:var(--fs-sm);font-weight:var(--fw-medium)}
.consig-paso-d{font-size:var(--fs-xs);color:var(--ink-3);line-height:1.4}
.consig-flecha{
  display:grid;place-items:center;flex:0 0 auto;
  color:var(--ink-3);opacity:.55;
}

/* ── Pedidos Web ────────────────────────────────────────────── */
.pedidos-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));
  gap:var(--sp-5);
}
.pedido-card{
  display:flex;flex-direction:column;gap:var(--sp-3);
  padding:var(--sp-6);text-align:left;width:100%;
  background:var(--surface);border:1px solid var(--hair);
  border-radius:var(--r-md);box-shadow:var(--sh-1);
  transition:transform var(--dur-fast) var(--ease),
             box-shadow var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease);
}
.pedido-card:hover{transform:translateY(-2px);box-shadow:var(--sh-2);border-color:var(--hair)}
.pedido-card:active{transform:translateY(0)}
.pedido-head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3)}
.pedido-cliente{font-size:var(--fs-body);font-weight:var(--fw-medium);line-height:1.3}
.pedido-contacto{display:block}
.pedido-thumbs{display:flex;align-items:center;gap:var(--sp-2);margin:var(--sp-2) 0}
.pedido-thumb{
  width:38px;height:38px;flex:0 0 auto;border-radius:var(--r-xs);
  background:var(--surface-2);overflow:hidden;display:grid;place-items:center;padding:2px;
}
.pedido-thumb img{max-width:100%;max-height:100%;object-fit:contain}
.pedido-mas{font-size:var(--fs-xs);color:var(--ink-3)}
.pedido-pie{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--sp-3);padding-top:var(--sp-3);border-top:1px solid var(--hair);
}

/* ── Venta anulada ──────────────────────────────────────────────
   La evidencia queda, pero se lee como anulada de un vistazo. */
.es-anulada{text-decoration:line-through;text-decoration-thickness:1px;color:var(--ink-3)}
.aviso-anulada{
  display:flex;gap:var(--sp-4);align-items:flex-start;
  padding:var(--sp-5) var(--sp-6);margin-bottom:var(--sp-6);
  border-radius:var(--r-sm);
  background:var(--danger-soft);color:var(--ink-2);
  font-size:var(--fs-sm);line-height:1.5;
}
.aviso-anulada b{color:var(--ink);font-weight:var(--fw-semi)}

/* ── Subir o cambiar la foto del producto ─────────────────────
   Se apoya sobre la esquina de la foto, no al lado: el gesto es
   "tocar la foto para cambiarla", igual que en el panel actual, y no
   le roba espacio a la ficha. Cuando no hay foto el recuadro ya dice
   "Sin foto", así que el botón alcanza para entender qué hacer. */
.ficha-photo{position:relative}
.foto-cambiar{
  position:absolute;left:var(--sp-3);right:var(--sp-3);bottom:var(--sp-3);
  display:flex;align-items:center;justify-content:center;gap:var(--sp-3);
  min-height:34px;padding:0 var(--sp-5);border-radius:var(--r-sm);
  background:rgba(12,12,14,.72);color:#fff;
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  font-size:var(--fs-xs);font-weight:var(--fw-medium);
  opacity:0;transition:opacity var(--dur-fast) var(--ease);
}
.ficha-photo:hover .foto-cambiar,
.foto-cambiar:focus-visible{opacity:1}
.foto-cambiar[disabled]{opacity:1;pointer-events:none}
@media (hover:none){
  /* sin hover no hay forma de descubrirlo: queda siempre visible */
  .foto-cambiar{opacity:1;min-height:40px}
}

/* ── Ficha: bloque de datos junto a la foto ─────────────────── */
.ficha-info{min-width:0;display:flex;flex-direction:column;gap:var(--sp-3)}

@media (max-width:1240px){
  .g23{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:860px){
  .g2,.g23,.gastos-layout{grid-template-columns:minmax(0,1fr)}
  .consig-flujo{flex-direction:column}
  .consig-flecha{transform:rotate(90deg);padding:var(--sp-1) 0}
  .pedidos-grid{grid-template-columns:minmax(0,1fr)}
  .hbar{grid-template-columns:minmax(0,96px) minmax(0,1fr) auto;gap:var(--sp-3)}
  .lh{display:none}
}

/* ── Pedido web → venta ─────────────────────────────────────────
   Un renglón por ítem del pedido. Si un renglón no se puede
   convertir se marca y bloquea el botón: media conversión sería
   peor que ninguna. */
.conv-lista{display:flex;flex-direction:column;gap:var(--sp-4)}
.conv-linea{
  padding:var(--sp-5) var(--sp-6);
  border:1px solid var(--hair);border-radius:var(--r-sm);
  background:var(--surface-2);
}
.conv-linea.tiene-problema{
  border-color:var(--danger);
  background:var(--danger-soft);
}
.conv-cab{display:flex;align-items:center;gap:var(--sp-4);margin-bottom:var(--sp-4)}
.conv-linea.tiene-problema .conv-cab{margin-bottom:0}
.conv-problema{
  display:flex;align-items:flex-start;gap:var(--sp-3);
  margin-top:var(--sp-4);font-size:var(--fs-sm);color:var(--ink-2);line-height:1.5;
}

/* ══════════════════════════════════════════════════════════════
   LOGIN · Liquid Glass
   ──────────────────────────────────────────────────────────────
   Vidrio sobre fotografía automotriz. La estructura es la misma que
   ya estaba aprobada; lo que cambia es el material.

   Cero naranja en superficie. El acento de Apex aparece sólo como
   un trazo de luz en el foco y en el hover del botón — una pista de
   marca, no un bloque de color. Un botón naranja sólido sobre
   vidrio oscuro rompe la ilusión de material: pasa a ser un sticker
   pegado encima del vidrio en vez de parte de él.
   ══════════════════════════════════════════════════════════════ */
.login{
  position:fixed;inset:0;z-index:300;
  display:none;place-items:center;
  padding:var(--sp-7);
  background:var(--shell-bg);
  overflow-y:auto;
}
.login[data-open="true"]{display:grid}

/* Lo que pinta el navegador por su cuenta, neutralizado en un solo
   lugar. Safari en iOS dibuja un destello al tocar un control y usa
   `accent-color` para varios estados; sin esto, el color del sistema
   se cuela igual por más que el CSS propio esté limpio. */
.login,
.login input,
.login button,
.login a{
  -webkit-tap-highlight-color:transparent;
  accent-color:var(--login-focus-borde);
}
/* Cualquier control del login que tome foco y no tenga una regla
   propia cae acá: indicación visible, y fría. */
.login :focus-visible{
  outline:2px solid var(--login-focus-borde);
  outline-offset:2px;
}

.login-bg{
  position:absolute;inset:0;
  /* recorta todo lo decorativo que sangra: el halo y el zoom de la
     foto. Sin esto `.login` queda con desplazamiento horizontal real. */
  overflow:hidden;
  /* la foto ambienta; el contraste lo ponen los velos, no ella.
     `filter` no cambia la geometría de la caja, así que puede quedar acá
     y alcanza también al halo. */
  filter:saturate(104%) contrast(102%);
}
/* ── El zoom de la foto va ADENTRO, no en la capa ────────────────
   Antes esto era `transform:scale(1.04)` sobre `.login-bg`. El problema:
   `overflow:hidden` recorta a los HIJOS, no a la propia caja. Un
   elemento de 440px escalado a 1.04 mide 457,6 y sobresale ~8,8px de
   cada lado, y el área scrolleable de un contenedor se calcula con las
   cajas YA TRANSFORMADAS de sus descendientes. Como `.login` tiene
   `overflow-y:auto` —y cuando un eje no es `visible` el otro se computa
   `auto`—, la pantalla de acceso quedaba con ~9px de desplazamiento
   horizontal de verdad. Medido en un iPhone de 440px: scroll 449.

   Mismo resultado visual, misma razón de ser (evitar bordes si la foto
   no cubre exacto), pero la caja escalada ahora es hija de la capa que
   recorta, así que no puede ensanchar nada. Es el mismo patrón que ya
   usa `.ambient::before`. */
.login-bg::before{
  content:'';position:absolute;inset:0;
  background-image:var(--login-image), var(--login-fallback);
  background-size:cover;background-position:center 45%;
  transform:scale(1.04);
}
/* Doble velo: uno radial que hunde los bordes y centra la atención,
   otro vertical que garantiza legibilidad venga la foto que venga. */
.login-bg::after{
  content:'';position:absolute;inset:0;
  background:
    radial-gradient(112% 86% at 50% 46%, rgba(6,6,8,.34) 0%, rgba(6,6,8,.80) 62%, rgba(6,6,8,.93) 100%),
    linear-gradient(180deg, rgba(6,6,8,.62) 0%, rgba(6,6,8,.72) 55%, rgba(6,6,8,.90) 100%);
}
/* Luz fría de fondo, muy contenida: da volumen sin colorear. */
/* Vive dentro de `.login-bg`, que lo recorta. Los porcentajes ahora
   son de esa capa, que mide exactamente el viewport. */
.login-glow{
  position:absolute;top:-22%;right:-14%;width:70%;height:70%;
  max-width:860px;max-height:860px;border-radius:50%;
  background:radial-gradient(circle, rgba(190,205,240,.11) 0%, rgba(190,205,240,0) 64%);
  pointer-events:none;
}

/* ── La card ─────────────────────────────────────────────────── */
.login-card{
  position:relative;z-index:1;width:100%;max-width:404px;
  padding:var(--sp-9) var(--sp-8) var(--sp-8);
  border-radius:26px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.075) 0%, rgba(255,255,255,.022) 42%, rgba(255,255,255,.045) 100%),
    rgba(16,16,20,.44);
  border:1px solid var(--lg-borde);
  /* el canto: más claro arriba, casi negro abajo */
  box-shadow:
    inset 0 1px 0 var(--lg-borde-luz),
    inset 0 -1px 0 rgba(0,0,0,.30),
    0 2px 8px rgba(0,0,0,.30),
    0 30px 60px -20px rgba(0,0,0,.70),
    0 60px 120px -40px rgba(0,0,0,.85);
  backdrop-filter:blur(var(--lg-blur)) saturate(var(--lg-sat));
  -webkit-backdrop-filter:blur(var(--lg-blur)) saturate(var(--lg-sat));
  animation:login-entra var(--dur-slow) var(--ease-out) both;
}
/* Reflejo de borde superior: la línea de luz que tiene el vidrio
   real donde el canto atrapa la iluminación del ambiente. */
.login-card::before{
  content:'';position:absolute;inset:0;border-radius:inherit;
  padding:1px;pointer-events:none;
  background:linear-gradient(160deg,
    rgba(255,255,255,.34) 0%, rgba(255,255,255,.06) 30%,
    rgba(255,255,255,0) 58%, rgba(255,255,255,.08) 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
}
@keyframes login-entra{
  from{opacity:0;transform:translateY(16px) scale(.985);filter:blur(2px)}
  to{opacity:1;transform:none;filter:none}
}

/* El logo es la marca: tiene que tener presencia, no ser un sello. */
.login-logo{
  display:block;height:92px;width:auto;max-width:78%;
  margin:0 auto var(--sp-8);
  object-fit:contain;
  filter:drop-shadow(0 6px 22px rgba(0,0,0,.55));
}

.login-t{
  font-size:27px;font-weight:var(--fw-semi);letter-spacing:-.024em;
  color:var(--login-ink);text-align:center;line-height:1.14;
}
.login-s{
  font-size:var(--fs-sm);color:var(--login-ink-3);
  text-align:center;margin-top:var(--sp-3);margin-bottom:var(--sp-8);
  letter-spacing:.01em;
}

/* ── Campos ──────────────────────────────────────────────────── */
.login-fg{margin-bottom:var(--sp-5)}
.login-fg label{
  display:block;font-size:var(--fs-xs);font-weight:var(--fw-medium);
  color:var(--login-ink-2);margin-bottom:var(--sp-3);letter-spacing:.005em;
}
.login-input{
  width:100%;height:48px;padding:0 var(--sp-5);
  border-radius:13px;
  background:var(--lg-bg);
  border:1px solid var(--lg-borde);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), inset 0 -1px 0 rgba(0,0,0,.18);
  color:var(--login-ink);font-family:var(--font);font-size:var(--fs-input);
  backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
  transition:border-color var(--dur-fast) var(--ease),
             background var(--dur-fast) var(--ease),
             box-shadow var(--dur-fast) var(--ease);
}
.login-input::placeholder{color:var(--login-ink-3)}
.login-input:hover{background:var(--lg-bg-fuerte)}
/* Foco: blanco frío. El borde sube de luminosidad y lo acompaña un
   halo neutro muy tenue. Nada de color de marca acá — en Safari,
   incluso un rgba naranja al 16% se lee como un contorno cobrizo
   alrededor del campo. */
.login-input:focus{
  outline:none;
  background:var(--lg-bg-fuerte);
  border-color:var(--login-focus-borde);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.12),
    0 0 0 3px var(--login-focus-glow);
}
.login-input:-webkit-autofill,
.login-input:-webkit-autofill:hover,
.login-input:-webkit-autofill:focus{
  -webkit-text-fill-color:var(--login-ink);
  -webkit-box-shadow:0 0 0 1000px var(--login-autofill-bg) inset;
  caret-color:var(--login-ink);
}

.login-pass{position:relative}
.login-pass .login-input{padding-right:50px}
.login-ojo{
  position:absolute;right:7px;top:50%;transform:translateY(-50%);
  width:36px;height:36px;border-radius:10px;
  display:grid;place-items:center;color:var(--login-ink-3);
  transition:color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.login-ojo:hover{color:var(--login-ink);background:rgba(255,255,255,.08)}
.login-ojo:focus-visible{outline:2px solid var(--login-focus-borde);outline-offset:1px}
.login-ojo .ico{width:19px;height:19px}

/* ── Botón ───────────────────────────────────────────────────── */
/* Vidrio, no naranja. Contraste por texto blanco sobre material
   oscuro; el acento de marca sólo se insinúa al interactuar. */
.login-btn{
  position:relative;overflow:hidden;
  width:100%;height:50px;margin-top:var(--sp-7);
  border-radius:14px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.13) 0%, rgba(255,255,255,.045) 100%),
    rgba(20,20,25,.62);
  border:1px solid var(--lg-borde-luz);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.20),
    inset 0 -1px 0 rgba(0,0,0,.34),
    0 10px 26px -10px rgba(0,0,0,.70);
  backdrop-filter:blur(14px) saturate(140%);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  color:#fff;font-family:var(--font);font-size:var(--fs-body);
  font-weight:var(--fw-semi);letter-spacing:-.004em;
  display:flex;align-items:center;justify-content:center;gap:var(--sp-4);
  transition:background var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease),
             box-shadow var(--dur-fast) var(--ease),
             transform var(--dur-fast) var(--ease);
}
/* Un filo de luz que cruza el canto superior al interactuar. Antes
   era naranja; ahora es luz blanca, que es lo que haría un canto de
   vidrio real bajo iluminación neutra. */
.login-btn::after{
  content:'';position:absolute;top:0;left:12%;right:12%;height:1px;
  background:linear-gradient(90deg,
    rgba(255,255,255,0) 0%, rgba(255,255,255,.70) 50%, rgba(255,255,255,0) 100%);
  opacity:0;transition:opacity var(--dur) var(--ease);
}
.login-btn:hover:not(:disabled){
  background:
    linear-gradient(180deg, rgba(255,255,255,.18) 0%, rgba(255,255,255,.07) 100%),
    rgba(26,26,32,.70);
  border-color:rgba(255,255,255,.30);
}
.login-btn:hover:not(:disabled)::after{opacity:1}
.login-btn:focus-visible{
  outline:none;
  border-color:var(--login-focus-borde);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.24),
    0 0 0 3px var(--login-focus-glow);
}
.login-btn:focus-visible::after{opacity:1}
.login-btn:active:not(:disabled){transform:scale(.988)}
.login-btn:disabled{opacity:.62;cursor:default}
.login-btn-t{line-height:1}
.login-btn .spinner{display:none}
.login-btn[data-cargando="true"] .spinner{display:block}

.spinner{
  width:16px;height:16px;border-radius:50%;flex:0 0 auto;
  border:2px solid rgba(255,255,255,.30);
  border-top-color:#fff;
  animation:girar .7s linear infinite;
}
@keyframes girar{to{transform:rotate(360deg)}}

/* ── Error ───────────────────────────────────────────────────── */
/* Sin error NO hay caja. `display:flex` pisaba el atributo `hidden`
   y dejaba una franja rojiza vacía flotando en la card. */
.login .login-error{
  margin-top:var(--sp-5);
  display:flex;align-items:flex-start;gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  border-radius:12px;
  background:var(--danger-soft);
  border:1px solid rgba(200,20,50,.34);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
  backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
  color:var(--login-error-ink);font-size:var(--fs-sm);line-height:1.45;
  animation:error-entra var(--dur) var(--ease-out) both;
}
.login .login-error[hidden]{display:none}
@keyframes error-entra{
  from{opacity:0;transform:translateY(-5px)}
  to{opacity:1;transform:none}
}

/* Safari viejo y Firefox sin backdrop-filter: el vidrio se cae a un
   sólido oscuro legible en vez de quedar transparente e ilegible. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .login-card{background:rgba(14,14,18,.93)}
  .login-btn{background:rgba(26,26,32,.95)}
  .login-input{background:rgba(255,255,255,.09)}
}

@media (max-width:480px){
  .login{padding:var(--sp-5);place-items:start center}
  .login-card{
    max-width:none;padding:var(--sp-8) var(--sp-6) var(--sp-7);
    margin-top:max(var(--sp-8), env(safe-area-inset-top));
    border-radius:22px;
  }
  .login-logo{height:72px;margin-bottom:var(--sp-7)}
  .login-t{font-size:24px}
  /* el tamaño lo pone `--fs-input`; acá sólo la altura */
  .login-input{height:50px}
}
@media (prefers-reduced-motion:reduce){
  .login-card{animation:none}
  .login .login-error{animation:none}
}

/* ══════════════════════════════════════════════════════════════
   POPOVER DE CUENTA
   El avatar de la topbar es el único acceso a la cuenta en todo el
   panel. No navega a ninguna pantalla: abre esto.
   ══════════════════════════════════════════════════════════════ */
.pop-scrim{position:fixed;inset:0;z-index:180;display:none}
.pop-scrim[data-open="true"]{display:block}

.pop{
  position:fixed;z-index:181;
  min-width:252px;max-width:min(300px, calc(100vw - var(--sp-6) * 2));
  padding:var(--sp-4);
  border-radius:var(--r-lg);
  /* El popover flota sobre el hero, o sea sobre una fotografía que
     elige el usuario y que puede ser de cualquier color. El vidrio
     solo no alcanza: se apoya sobre una capa opaca del color de la
     superficie, y el vidrio queda encima como material.

     Sin esto, el texto quedaba gris sobre foto y en Safari era
     directamente ilegible. El diseño no cambia; cambia cuánto deja
     pasar. */
  background:
    linear-gradient(var(--glass-bg), var(--glass-bg)),
    var(--surface);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.14),
    0 18px 48px -12px rgba(0,0,0,.42),
    0 2px 8px rgba(0,0,0,.18);
  opacity:0;transform:translateY(-6px) scale(.97);
  pointer-events:none;
  transform-origin:top right;
  transition:opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.pop[data-open="true"]{opacity:1;transform:none;pointer-events:auto}

.pop-id{
  display:flex;align-items:center;gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-4) var(--sp-5);
}
.pop-id .avatar{width:38px;height:38px;font-size:var(--fs-sm)}
.pop-id-txt{min-width:0;flex:1}
.pop-id-n{
  display:block;font-size:var(--fs-body);font-weight:var(--fw-semi);
  color:var(--ink);line-height:1.2;
}
.pop-id-r{display:block;font-size:var(--fs-xs);color:var(--ink-2);margin-top:1px}
/* `--ink-3` es el tono para datos secundarios sobre una superficie
   plana. Acá hay vidrio de por medio, así que el email y el rol
   suben a `--ink-2`: siguen siendo secundarios, pero se leen. */
.pop-mail{
  display:block;padding:0 var(--sp-4) var(--sp-4);
  font-size:var(--fs-xs);color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.pop-sep{height:1px;background:var(--hair);margin:var(--sp-2) 0}
.pop-item{
  width:100%;display:flex;align-items:center;gap:var(--sp-4);
  padding:var(--sp-4);border-radius:var(--r-sm);
  font-size:var(--fs-sm);color:var(--ink);text-align:left;
  transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.pop-item:hover{background:var(--surface-2);color:var(--ink)}
/* Cerrar sesión conserva el tratamiento destructivo: es lo único
   del menú que no se puede deshacer con otro click. */
.pop-item .ico{width:17px;height:17px;flex:0 0 auto}
.pop-item--salir{color:var(--danger)}
.pop-item--salir:hover{background:var(--danger-soft);color:var(--danger)}

@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .pop{background:var(--surface)}
}

@media (max-width:560px){
  /* en mobile el popover se apoya abajo, al alcance del pulgar */
  .pop{
    left:var(--sp-5);right:var(--sp-5);top:auto !important;
    bottom:calc(var(--bottomnav-h, 64px) + var(--sp-5));
    max-width:none;transform-origin:bottom center;
    transform:translateY(8px) scale(.98);
  }
  .pop[data-open="true"]{transform:none}
}

/* ══════════════════════════════════════════════════════════════
   CONFIGURACIÓN
   ══════════════════════════════════════════════════════════════ */
.cfg-sec{margin-bottom:var(--sp-9)}
.cfg-sec-cab{margin-bottom:var(--sp-5)}
.cfg-sec-t{font-size:var(--fs-h2);font-weight:var(--fw-semi);letter-spacing:-.018em}
.cfg-sec-d{
  font-size:var(--fs-sm);color:var(--ink-2);line-height:1.55;
  margin-top:var(--sp-2);max-width:60ch;
}

.cfg-imgs{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));
  gap:var(--sp-5);align-items:start;
}
.cfg-img{overflow:hidden;display:flex;flex-direction:column}

/* El preview muestra la imagen en la proporción en la que se va a
   usar: apaisada. Una miniatura cuadrada mentiría sobre el encuadre. */
.cfg-img-preview{
  position:relative;aspect-ratio:21/9;
  background-size:cover;background-position:center;
  background-color:var(--surface-3);
  border-bottom:1px solid var(--hair);
}
.cfg-img-preview.es-default{
  background-image:var(--hero-fallback);
  display:grid;place-items:center;
}
.cfg-img-vacio{
  display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);
  color:rgba(255,255,255,.55);font-size:var(--fs-xs);
}
.cfg-img-vacio .ico{width:26px;height:26px}
.cfg-img-badge{
  position:absolute;top:var(--sp-4);left:var(--sp-4);
  padding:3px 9px;border-radius:var(--r-full);
  background:rgba(8,8,10,.62);
  border:1px solid rgba(255,255,255,.16);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  font-size:10px;font-weight:var(--fw-medium);letter-spacing:.04em;
  color:rgba(255,255,255,.90);text-transform:uppercase;
}

.cfg-img-body{padding:var(--sp-6);flex:1;display:flex;flex-direction:column}
.cfg-img-t{font-size:var(--fs-body);font-weight:var(--fw-semi);letter-spacing:-.01em}
.cfg-img-d{font-size:var(--fs-sm);color:var(--ink-2);margin-top:var(--sp-2);line-height:1.5}
.cfg-img-body .hint{margin-top:var(--sp-3);margin-bottom:0}
.cfg-img-acciones{
  display:flex;flex-wrap:wrap;gap:var(--sp-3);
  margin-top:auto;padding-top:var(--sp-5);
}
.cfg-img-estado{
  margin-top:var(--sp-4);padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-sm);font-size:var(--fs-xs);line-height:1.45;
}
.cfg-img-estado.es-info{background:var(--info-soft);color:var(--info)}
.cfg-img-estado.es-ok{background:var(--ok-soft);color:var(--ok)}
.cfg-img-estado.es-warn{background:var(--warn-soft);color:var(--warn)}
.cfg-img-estado.es-error{background:var(--danger-soft);color:var(--danger)}

@media (max-width:640px){
  .cfg-imgs{grid-template-columns:minmax(0,1fr)}
  .cfg-img-acciones .btn{flex:1}
}

/* ══════════════════════════════════════════════════════════════
   DIAGNÓSTICO DE LAYOUT — NO TIENE ESTILOS ACÁ, A PROPÓSITO
   ──────────────────────────────────────────────────────────────
   El panel de `scripts/diagnostico.js` se pinta con estilos en línea
   y no depende de esta hoja. La razón es concreta y ya nos costó una
   ronda de validación: `body` tiene `overflow:hidden` y `.shell` mide
   `100dvh`, así que un elemento agregado al final de `body` SIN
   estilos queda debajo del viewport, en un `body` que no scrollea, y
   es invisible sin lanzar un solo error. Si el navegador servía esta
   hoja desde caché, el panel desaparecía en silencio.

   Un instrumento de medición no puede depender de lo que mide. Si
   alguna vez hace falta cambiarle el aspecto, se cambia en
   diagnostico.js, que es el único lugar donde vive.
   ══════════════════════════════════════════════════════════════ */

/* Sello de versión de esta hoja. Sin efecto visual: lo lee el panel
   de diagnóstico para saber si el navegador está ejecutando esta
   versión o una cacheada. Ver la nota en tokens.css. */
:root{ --build-components: cierre-2; }
