/* SG App CSS - base layout + componentes scopeados al shell de la app */

/* Skip-link: vive FUERA de .app-shell a propósito (es el primer elemento
   focuseable del body, antes del shell) — por eso no puede usar los
   tokens --sg-* (solo existen dentro de .app-shell) y lleva sus propios
   valores fijos, coherentes con la paleta del sistema. */
.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 10000;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  padding: 0.7143em 1.1429em;
  background: #2563eb;
  color: #ffffff;
  font: 600 0.875em/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  border-radius: 6px;
  text-decoration: none;
  transition: top 0.15s ease-out;
}

.skip-link:focus {
  top: 0.5714em;
  left: 0.5714em;
  width: auto;
  height: auto;
  overflow: visible;
  clip-path: none;
  white-space: normal;
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}

@font-face {
  font-family: "Instrument Sans";
  src: url("../fonts/instrument-sans-latin-wght-normal.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist";
  src: url("../fonts/geist-latin-wght-normal.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   Bloque F · F1 — Design tokens (norte: Notion / Claude)
   Paleta neutra cálida + acento azul. Los nombres de variable
   se conservan desde A5/D5 (no se rompe nada) y se re-valúan;
   se agregan escalas nuevas (spacing, tipografía, sombras, z)
   que F2–F5 van a consumir. Una sola fuente de verdad de estilo.
   ============================================================ */
.app-shell {
  /* — Superficies — */
  --sg-surface: #ffffff;
  --sg-soft: #f7f6f4;
  --sg-sunken: #f0efec;

  /* — Texto — */
  --sg-ink: #37352f;
  --sg-muted: #63625c;
  --sg-faint: #8f8d87;

  /* — Bordes — */
  --sg-border: #e9e8e4;
  --sg-border-strong: #d6d5d0;

  /* — Acento — */
  --sg-accent: #2563eb;
  --sg-accent-hover: #1d4ed8;
  --sg-accent-soft: #eef4ff;
  --sg-accent-ink: #1e4fc0;
  --sg-on-accent: #ffffff;

  /* — Estados — */
  --sg-success: #0f7b4f;
  --sg-success-soft: #e7f4ee;
  --sg-danger: #c0362c;
  --sg-danger-soft: #fbeeec;
  --sg-warning: #9a5b04;
  --sg-warning-soft: #fdf3e3;

  /* — Radios — */
  --sg-radius-sm: 6px;
  --sg-radius: 8px;
  --sg-radius-lg: 10px;
  --sg-radius-xl: 12px;
  --sg-radius-pill: 999px;

  /* — Spacing (escala de 4px) — */
  --sg-space-1: 0.25em;
  --sg-space-2: 0.5em;
  --sg-space-3: 0.75em;
  --sg-space-4: 1em;
  --sg-space-5: 1.25em;
  --sg-space-6: 1.5em;
  --sg-space-7: 2em;
  --sg-space-8: 3em;
  --sg-gap: var(--sg-space-3);

  /* — Tipografía — (idénticas a prototype/css/app.css: display = Instrument
     Sans, cuerpo/datos = Geist, mismos fallbacks) */
  --sg-font-display: "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --sg-font-body: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  --sg-font-data: var(--sg-font-body);
  --sg-font: var(--sg-font-body);
  --sg-text-2xs: 0.6875em;
  --sg-text-xs: 0.75em;
  --sg-text-sm: 0.8125em;
  --sg-text-md: 0.875em;
  --sg-text-base: 0.875em;
  --sg-text-lg: 1em;
  --sg-text-xl: 1.1875em;
  --sg-text-2xl: 1.5em;
  --sg-weight-normal: 400;
  --sg-weight-medium: 500;
  --sg-weight-strong: 550;
  --sg-weight-semibold: 600;
  --sg-weight-display: 650;
  --sg-leading-tight: 1.25;
  --sg-leading-normal: 1.5;

  /* — Sombras (sutiles; nunca borde 1px + sombra ancha en el mismo elemento) — */
  --sg-shadow-sm: 0 1px 2px rgba(15, 15, 15, 0.04);
  --sg-shadow: 0 1px 3px rgba(15, 15, 15, 0.06), 0 1px 2px rgba(15, 15, 15, 0.04);
  --sg-shadow-md: 0 4px 14px rgba(35, 33, 28, 0.16);
  --sg-shadow-pop: 0 8px 24px rgba(35, 33, 28, 0.13), 0 2px 6px rgba(35, 33, 28, 0.08);

  /* — Foco accesible — */
  --sg-ring: 0 0 0 3px rgba(37, 99, 235, 0.25);

  /* — Escala z semántica (la consumen drawer/dialog/toast en F2–F3) — */
  --sg-z-dropdown: 1000;
  --sg-z-sticky: 1100;
  --sg-z-drawer: 1200;
  --sg-z-backdrop: 1300;
  --sg-z-modal: 1400;
  --sg-z-toast: 1500;
  --sg-z-tooltip: 1600;
  --sg-z-popover: var(--sg-z-dropdown);
  --sg-z-fab: var(--sg-z-toast);
  --sg-z-nav: var(--sg-z-sticky);

  min-height: 100vh;
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 0;

  /* Baseline propio: independencia del tema activo */
  font-family: var(--sg-font);
  font-size: medium;
  line-height: var(--sg-leading-normal);
  color: var(--sg-ink);
  background: var(--sg-surface);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.app-shell,
.app-shell *,
.app-shell *::before,
.app-shell *::after {
  box-sizing: border-box;
}

.app-shell img,
.app-shell svg,
.app-shell video {
  max-width: 100%;
  height: auto;
}

/* Aislamiento del tema: neutralizar lo que el tema filtra en
   elementos "desnudos" (sin clase del contrato) dentro del shell.
   :where() => especificidad 0, no compite con los componentes. */
.app-shell :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, figure, blockquote, pre) {
  margin: 0;
}

.app-shell :where(ul, ol) {
  padding: 0;
  list-style: none;
}

.app-shell :where(a) {
  color: inherit;
  text-decoration: none;
}

.app-shell :where(button, input, select, textarea) {
  font: inherit;
  color: inherit;
}

.app-shell :where(.page-title, .page-actions, .panel-title, .btn, .icon-btn, .field-label, .sidebar-link, .sidebar-user-name, .sidebar-logout, .tab, .dlg-title) {
  font-family: var(--sg-font-display);
}

.app-shell :where(.table, .stat-value, .entity-figure-value, .def-value, .cell-strong, .cell-muted, .cell-sub, .badge, input, select, textarea) {
  font-family: var(--sg-font-data);
}

.app-shell :where(h1, h2, h3, h4, h5, h6) {
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
}

.app-shell [hidden] {
  display: none !important;
}

/* Foco accesible base (token-driven). Solo teclado (:focus-visible),
   no molesta al click de mouse. Lo heredan todos los controles. */
.app-shell :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--sg-accent);
  outline-offset: 2px;
}

/* ============================================================
   App shell (sidebar claro Notion, nav, swap logo↔símbolo,
   colapsar desktop/tablet, bottom nav + sheet en mobile).
   Estado en .app-shell: data-sidebar (expanded|collapsed).
   Lo cablea assets/js/sg-app.js.
   ============================================================ */

/* Ancho del sidebar como token local del shell, para que el grid
   y el estado colapsado compartan la misma fuente de verdad. */
.app-shell {
  --sg-sidebar-w: 264px;
  --sg-sidebar-w-collapsed: 76px;
  grid-template-columns: var(--sg-sidebar-w) 1fr;
}

.app-shell[data-sidebar="collapsed"] {
  grid-template-columns: var(--sg-sidebar-w-collapsed) 1fr;
}

.app-sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden auto;
  display: flex;
  flex-direction: column;
  padding: var(--sg-space-3);
  gap: var(--sg-space-2);
  border-right: 1px solid var(--sg-border);
  background: var(--sg-soft);
}

/* — Branding — */
.sidebar-branding {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
  padding: var(--sg-space-2);
  min-height: 44px;
}

.sidebar-branding .brand-mark {
  display: inline-flex;
  align-items: center;
  flex: none;
  text-decoration: none;
  color: inherit;
}

.sidebar-branding .brand-mark .sgc-brand {
  color: var(--sg-accent);
}

/* El wordmark es horizontal (~6.3:1): alto fijo, ancho automático. */
.sidebar-branding .brand-mark--full .sgc-brand {
  width: auto;
  height: 22px;
}

/* El símbolo es cuadrado (~1:1). */
.sidebar-branding .brand-mark--mini .sgc-brand {
  width: 28px;
  height: 28px;
}

/* Swap logo↔símbolo según estado del sidebar (CSS, no JS).
   El logo es autosuficiente: nunca lleva texto/descriptor al lado.
   Nota: scopear con .sidebar-branding para ganarle en especificidad a
   `.sidebar-branding .brand-mark { display:inline-flex }`. */
.sidebar-branding .brand-mark--mini { display: none; }
.app-shell[data-sidebar="collapsed"] .sidebar-branding .brand-mark--full { display: none; }
.app-shell[data-sidebar="collapsed"] .sidebar-branding .brand-mark--mini { display: inline-flex; }

/* Botón colapsar (desktop): empujado a la derecha del branding. */
.sidebar-collapse {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--sg-radius-sm);
  background: transparent;
  color: var(--sg-muted);
  cursor: pointer;
}

.sidebar-collapse:hover { background: var(--sg-sunken); color: var(--sg-ink); }
.sidebar-collapse .icon { width: 18px; height: 18px; }

/* — Navegación — */
.sidebar-nav { flex: 1 1 auto; min-height: 0; }

.sidebar-menu {
  display: flex;
  flex-direction: column;
  gap: 0.1429em;
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: 0.7143em;
  min-height: 40px;
  padding: 0.5em 0.7143em;
  border-radius: var(--sg-radius);
  color: var(--sg-muted);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-md);
  font-weight: var(--sg-weight-medium);
  white-space: nowrap;
}

.sidebar-link-icon {
  display: inline-flex;
  flex: none;
  color: var(--sg-faint);
}

.sidebar-link-icon .icon { width: 17px; height: 17px; stroke-width: 1.8; }

.sidebar-link-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-link:hover { background: var(--sg-sunken); color: var(--sg-ink); }

.sidebar-link.is-active {
  background: var(--sg-sunken);
  color: var(--sg-ink);
  font-weight: var(--sg-weight-semibold);
}

.sidebar-link.is-active .sidebar-link-icon { color: var(--sg-ink); }

/* — Usuario (pie del sidebar) — */
.sidebar-user {
  margin-top: auto;
  padding-top: var(--sg-space-2);
  border-top: 1px solid var(--sg-border);
  display: flex;
  flex-direction: column;
  gap: 0.1429em;
}

.sidebar-user-info {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
  padding: var(--sg-space-2);
  min-width: 0;
}

.sidebar-user-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: var(--sg-radius-pill);
  background: var(--sg-ink);
  color: var(--sg-on-accent);
  font-size: var(--sg-text-xs);
  font-weight: var(--sg-weight-semibold);
}

.sidebar-user .user-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
}

.sidebar-logout {
  display: flex;
  align-items: center;
  gap: var(--sg-space-3);
  min-height: 38px;
  padding: var(--sg-space-2) var(--sg-space-3);
  border-radius: var(--sg-radius-sm);
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
}

.sidebar-logout .sidebar-link-icon { color: var(--sg-faint); }
.sidebar-logout .icon { width: 18px; height: 18px; }
.sidebar-logout:hover { background: var(--sg-sunken); color: var(--sg-ink); }

/* — Estado colapsado (desktop): solo íconos centrados — */
.app-shell[data-sidebar="collapsed"] .sidebar-link-label,
.app-shell[data-sidebar="collapsed"] .sidebar-user-name,
.app-shell[data-sidebar="collapsed"] .sidebar-user-role {
  display: none;
}

.app-shell[data-sidebar="collapsed"] .sidebar-branding,
.app-shell[data-sidebar="collapsed"] .sidebar-link,
.app-shell[data-sidebar="collapsed"] .sidebar-user-info,
.app-shell[data-sidebar="collapsed"] .sidebar-logout {
  justify-content: center;
  gap: 0;
}

.app-shell[data-sidebar="collapsed"] .sidebar-collapse { margin-left: 0; }

/* Colapsado: el símbolo y el botón de expandir comparten el mismo lugar.
   Por defecto se ve el símbolo; al pasar el mouse (o con foco de teclado en
   la marca) aparece el botón EN SU LUGAR — nunca los dos a la vez. */
.app-shell[data-sidebar="collapsed"] .sidebar-collapse { display: none; }
.app-shell[data-sidebar="collapsed"] .sidebar-branding:hover .brand-mark--mini,
.app-shell[data-sidebar="collapsed"] .sidebar-branding:focus-within .brand-mark--mini {
  display: none;
}
.app-shell[data-sidebar="collapsed"] .sidebar-branding:hover .sidebar-collapse,
.app-shell[data-sidebar="collapsed"] .sidebar-branding:focus-within .sidebar-collapse {
  display: inline-flex;
}

/* Contenido (2ª columna del grid): topbar + main apilados */
.app-content {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* `.app-main` es su propio contenedor de scroll (igual que `.app-sidebar`,
   ya `height:100vh` + sticky): antes solo tenia `overflow-x:auto`, y por la
   regla de CSS que empareja overflow-x/overflow-y (si uno no es "visible"
   el otro pasa a "auto"), el navegador ya trataba a `.app-main` como
   contenedor de scroll de todos modos — pero como su alto era "auto" (se
   estiraba exactamente a la altura de su contenido, sin overflow propio),
   nunca llegaba a scrollear el SOLO; scrolleaba el documento entero, y ahi
   el sticky de sus hijos (`.page-header`, `.detail-top`, etc.) no funcionaba
   porque su ancestro de scroll (`.app-main`) nunca se movia relativo a si
   mismo. Fijando `height:100vh` (como el sidebar) `.app-main` pasa a
   scrollear de verdad puertas adentro, y el sticky si engancha.
   IMPORTANTE: el padding vive en `.app-shell .page` (mas abajo), NO aca —
   un contenedor de scroll con padding propio tiene un bug conocido de
   Chrome donde el padding-top/left no se scrollea junto con el contenido
   (queda como una franja fija revelando lo que hay detras, con filas de la
   tabla "asomando" por arriba del header sticky). Sacando el padding de
   `.app-main` y poniendolo en `.page` (un hijo normal que si scrollea
   entero) se evita el bug por completo. */
.app-main {
  min-width: 0;
  height: 100vh;
  overflow-y: auto;
  overflow-x: auto;
}

/* Estructura base de página. El padding vive aca (no en `.app-main`, que es
   el contenedor de scroll — ver comentario en `.app-main`). */
.app-shell .page {
  max-width: 100%;
  padding: var(--sg-space-5);
}

.app-shell .page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sg-space-4);
  padding-bottom: var(--sg-space-6);
}

/* Titulo de seccion/ficha y barra de acciones (cuando esta visible) quedan
   fijos arriba al scrollear: mismo criterio que el sidebar (`position:
   sticky`, ya usado ahi), no hay un contenedor de scroll propio (la pagina
   entera scrollea), asi que `top:0` alcanza. Fondo opaco para que el
   contenido de la tabla no se transparente por debajo al pasar.
   El respiro de arriba/abajo tiene que ser `padding` (parte de la caja con
   fondo opaco), no `margin`: un margen es transparente, asi que al quedar
   pegado arriba el contenido que sigue scrolleando detras asoma justo por
   ese hueco en vez de verse un espacio en blanco solido. */
.app-shell .page-header,
.app-shell .detail-top,
.app-shell .row-actions-bar--mobile {
  position: sticky;
  top: 0;
  z-index: var(--sg-z-sticky);
  background: var(--sg-surface);
  padding-top: var(--sg-space-3);
}

.app-shell .page-title {
  margin: 0;
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-2xl);
  font-weight: var(--sg-weight-display);
  letter-spacing: -0.015em;
  line-height: 1.2;
  text-wrap: balance;
}

.app-shell .page-subtitle {
  margin: 0.1429em 0 0;
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
}

.app-shell .page-actions {
  display: flex;
  flex: none;
  gap: var(--sg-space-2);
  padding-top: 0.1429em;
}

.app-shell .page-body {
  display: grid;
  gap: var(--sg-space-4);
}

/* Panel */
.app-shell .panel {
  background: transparent;
  border: 0;
  border-radius: 0;
  overflow: visible;
}

/*
 * Migracion UI Parte 6: separacion vertical entre paneles consecutivos fuera
 * de un `.page-body` con `gap` (p. ej. fichas de detalle que van directo en
 * `.page`, sin wrapper de grid). Mismo selector que `prototype/css/app.css`.
 */
.app-shell .panel + .panel {
  margin-top: var(--sg-space-6);
}

.app-shell .panel-head,
.app-shell .panel-toolbar,
.app-shell .panel-body {
  padding: 0;
}

.app-shell .panel-head {
  border-bottom: 0;
  background: transparent;
  margin-bottom: var(--sg-space-3);
}

.app-shell .panel-title {
  margin: 0;
  font-size: var(--sg-text-md);
  font-weight: var(--sg-weight-semibold);
  line-height: 1.25;
}

.app-shell .panel-desc {
  display: none;
}

/*
 * F3b (validado 2026-06-19): el panel se de-anida (sin caja) cuando su
 * cuerpo ya son tarjetas propias (KPIs). Cuando envuelve una sola tabla o
 * un grafico, el panel ES la tarjeta-seccion (marco unico, no anidacion) y
 * conserva caja. Restaurado 2026-07-07 (se habia perdido en el camino).
 */
.app-shell .panel:has(.chart-box) {
  background: var(--sg-surface);
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius-lg);
  padding: var(--sg-space-5);
}

.app-shell .panel:has(.table-wrap) {
  background: var(--sg-surface);
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius-lg);
  padding: 0;
  overflow: hidden;
}

.app-shell .panel:has(.table-wrap) > .panel-body {
  padding: 0;
}

.app-shell .panel:has(.table-wrap) .panel-head {
  padding: var(--sg-space-3) var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
}

/* Toolbar */
.app-shell .panel-toolbar {
  border-bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sg-gap);
  flex-wrap: wrap;
  padding-bottom: var(--sg-space-3);
  margin-bottom: var(--sg-space-3);
}

.app-shell .toolbar-left,
.app-shell .toolbar-right,
.app-shell .row-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.5714em;
  flex-wrap: wrap;
}

.app-shell .toolbar-right form,
.app-shell .row-actions form {
  margin: 0;
  display: inline-flex;
  align-items: center;
}

/*
 * Migracion UI Parte 6 (Detalles): cabecera de entidad (identidad + estado +
 * cifra dominante) y grid de definicion (label sobre dato), reemplazo de
 * `.detail-layout`/`.entity-summary`/`.attribute-row`. Mismos tokens que
 * `prototype/css/app.css` (`.entity-head`/`.entity-id`/`.entity-name`/
 * `.entity-meta`/`.entity-figure*`/`.def-grid`/`.def-label`/`.def-value`).
 */
.app-shell .detail-top {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
  padding-bottom: var(--sg-space-4);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/*
 * Mobile-first (override permitido sobre el prototipo estatico, que no
 * resuelve el desborde): back-link + botones no se achican ni se cortan
 * contra el borde de pantalla, se recorre la fila con scroll horizontal.
 */
.app-shell .detail-top > * {
  flex-shrink: 0;
}

.app-shell .detail-top form {
  display: inline-flex;
  flex-shrink: 0;
}

.app-shell .detail-top > .btn,
.app-shell .detail-top form > .btn {
  width: 40px;
  min-width: 40px;
  height: 36px;
  padding: 0;
  gap: 0;
  overflow: hidden;
  font-size: 0;
}

.app-shell .detail-top > .btn .icon,
.app-shell .detail-top form > .btn .icon {
  width: 16px;
  height: 16px;
  margin: 0;
  font-size: var(--sg-text-sm);
}

.app-shell .back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4286em;
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-sm);
  font-weight: 550;
  color: var(--sg-muted);
  text-decoration: none;
  padding: 0.4286em 0.7143em 0.4286em 0.4286em;
  border-radius: var(--sg-radius-md);
}

.app-shell .back-link:hover {
  background: var(--sg-sunken);
  color: var(--sg-ink);
}

.app-shell .back-link .icon {
  width: 16px;
  height: 16px;
}

.app-shell .detail-top .spacer {
  flex: 1;
}

.app-shell .entity-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sg-space-5);
  padding-bottom: var(--sg-space-5);
  margin-bottom: var(--sg-space-5);
  border-bottom: 1px solid var(--sg-border);
}

.app-shell .entity-id {
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-xs);
  font-weight: 600;
  color: var(--sg-faint);
  letter-spacing: .04em;
}

.app-shell .entity-name {
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-2xl);
  font-weight: 650;
  letter-spacing: -0.015em;
  line-height: 1.2;
  text-wrap: balance;
  display: flex;
  align-items: center;
  gap: 0.7143em;
  flex-wrap: wrap;
}

.app-shell .entity-ref {
  color: var(--sg-muted);
  font-size: var(--sg-text-lg);
  font-weight: var(--sg-weight-medium);
}

.app-shell .entity-meta {
  display: flex;
  gap: var(--sg-space-4);
  flex-wrap: wrap;
  margin-top: var(--sg-space-2);
  font-size: var(--sg-text-sm);
  color: var(--sg-muted);
}

.app-shell .entity-meta a {
  color: var(--sg-accent-ink);
  text-decoration: none;
}

.app-shell .entity-meta a:hover {
  text-decoration: underline;
}

.app-shell .entity-figure {
  text-align: right;
  flex: none;
}

.app-shell .entity-figure-label {
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-xs);
  font-weight: 500;
  color: var(--sg-muted);
}

.app-shell .entity-figure-value {
  font-size: 1.8571em;
  font-weight: 650;
  letter-spacing: -0.01em;
  line-height: 1.15;
  white-space: nowrap;
}

.app-shell .entity-figure-note {
  font-size: var(--sg-text-xs);
  color: var(--sg-muted);
  margin-top: 0.1429em;
}

.app-shell .def-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sg-space-4) var(--sg-space-5);
  margin-bottom: var(--sg-space-6);
}

.app-shell .def-grid > div {
  min-width: 0;
}

.app-shell .def-label {
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-xs);
  font-weight: 500;
  color: var(--sg-muted);
  margin-bottom: 0.1429em;
}

.app-shell .def-value {
  font-size: var(--sg-text-md);
  font-weight: 500;
  overflow-wrap: break-word;
}

.app-shell .def-value.is-strong {
  font-weight: 650;
}

@media (max-width: 760px) {
  .app-shell .entity-head {
    flex-direction: column;
    gap: var(--sg-space-3);
  }

  .app-shell .entity-figure {
    text-align: left;
  }

  .app-shell .def-grid {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 480px) {
  .app-shell {
    --sg-text-2xs: 0.75em;
    --sg-text-xs: 0.8125em;
    --sg-text-sm: 0.875em;
    --sg-text-md: 0.9375em;
    --sg-text-base: 0.9375em;
    --sg-text-lg: 1.0625em;
    --sg-text-xl: 1.25em;
  }
}

/*
 * Buscador + filtro + "Limpiar" en una sola fila, sin wrap, en cualquier
 * `.toolbar > .toolbar-right` (no solo Ventas: mismo patron para Cuotas y
 * el resto de listados migrados). Antes solo existia para
 * `.ventas-list-panel`, asi que los demas modulos heredaban el
 * `flex-wrap: wrap` generico de arriba y "Limpiar" se iba a una linea aparte.
 */
.app-shell .toolbar > .toolbar-right {
  flex-wrap: nowrap;
  /* Se estira para ocupar el resto del `.toolbar` (que suele tener un
     `.toolbar-left` vacio o angosto); sin esto, `.toolbar-right` quedaba
     del ancho de su propio contenido (inline-flex) y la barra de acciones,
     aunque tuviera `margin-left:auto`, no llegaba al borde derecho real
     del panel, sino que quedaba pegada al lado del boton de filtros. */
  flex: 1 1 auto;
}

.app-shell .toolbar > .toolbar-right .search {
  flex: 0 1 320px;
  width: 320px;
  max-width: 100%;
}

/* ============================================================
   Bloque F · F3a — Componentes: botones, formularios, badges.
   Estilo Notion/Claude sobre los módulos del contrato.
   `.btn` es residuo del Bloque D (pantallas aún sin migrar a
   `button`: Finanzas/Productos/Proveedores/Login/listados): se
   le da el aspecto del botón secundario para que no quede sin
   estilo, hasta que su migración HTML lo retire (queda en D).
   ============================================================ */

/* Botones */
.app-shell .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sg-space-2);
  min-height: 36px;
  padding: 0 1em;
  border: 1px solid transparent;
  border-radius: var(--sg-radius);
  background: var(--sg-surface);
  color: var(--sg-ink);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-semibold);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.app-shell .btn .icon {
  width: 15px;
  height: 15px;
  flex: none;
}

/* Secundario (y el aspecto por defecto del `.btn` heredado) */
.app-shell .btn,
.app-shell .btn-ghost {
  background: var(--sg-surface);
  border-color: var(--sg-border-strong);
  color: var(--sg-ink);
}

.app-shell .btn:hover,
.app-shell .btn-ghost:hover {
  background: var(--sg-sunken);
}

.app-shell .btn-ghost .icon {
  color: var(--sg-muted);
}

/* Primario (acento azul) */
.app-shell .btn-primary {
  background: var(--sg-accent);
  border-color: var(--sg-accent);
  color: var(--sg-on-accent);
}

.app-shell .btn-primary:hover {
  background: var(--sg-accent-hover);
  border-color: var(--sg-accent-hover);
}

.app-shell .btn-primary .icon {
  color: var(--sg-on-accent);
}

.app-shell .btn-danger {
  background: var(--sg-danger-soft);
  border-color: transparent;
  color: var(--sg-danger);
}

.app-shell .btn-danger:hover {
  background: #f6ded9;
}

.app-shell .btn-quiet {
  background: transparent;
  border-color: transparent;
  color: var(--sg-muted);
}

.app-shell .btn-quiet:hover {
  background: var(--sg-sunken);
  color: var(--sg-ink);
}

/* Deshabilitado */
.app-shell .btn:disabled,
.app-shell .btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

.app-shell .btn:active {
  translate: 0 1px;
}

.app-shell .btn:focus-visible {
  outline: none;
  box-shadow: var(--sg-ring);
}

/* Formularios */
.app-shell .form {
  display: grid;
  gap: var(--sg-space-4);
  max-width: 480px;
}

.app-shell .field {
  display: grid;
  gap: var(--sg-space-2);
  align-content: start;
}

/* Modificador de ancho: campos numéricos cortos (stock, % ganancia, cuotas,
   interés) no deberían estirarse al ancho completo del form (480px) solo
   porque el resto de los campos son de texto largo — el formato visual
   tiene que corresponder al contenido que va a recibir. No cambia el
   contrato (mismo bloque .field, variante de tamaño vía modificador BEM,
   igual patrón que .icon-btn.is-danger/.icon-btn.is-static). */
.app-shell .field--sm .control:not([type="checkbox"]):not([type="radio"]) {
  max-width: 6.5rem;
}

.app-shell .field-label {
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
  color: var(--sg-muted);
}

/* Marca visual de campo obligatorio (antes solo dependía de la burbuja nativa
   del navegador, invisible hasta el submit). No agrega texto nuevo al DOM:
   se deriva del propio atributo `required` del control. */
.app-shell .field:has(.control[required]):not(:has(.field-label .req)) > .field-label::after {
  content: " *";
  color: var(--sg-danger);
}

.app-shell .field-value {
  display: inline-block;
  padding: 0.1429em 0;
}

.app-shell .form-actions {
  display: flex;
  gap: var(--sg-space-2);
  flex-wrap: wrap;
  margin-top: var(--sg-space-2);
}

/* Campo marcado inválido (wizard de venta, aria-invalid dinámico por JS):
   refuerzo visual + programático, no solo el mensaje del dialog-alert. */
.app-shell .control[aria-invalid="true"],
.app-shell .predictive-field input[type="search"][aria-invalid="true"],
.app-shell .cart-search input[aria-invalid="true"] {
  border-color: var(--sg-danger);
}

.app-shell .field.is-invalid .field-label {
  color: var(--sg-danger);
}

.app-shell .field-error {
  margin: -0.2143em 0 0;
  color: var(--sg-danger);
  font-size: var(--sg-text-xs);
  line-height: 1.35;
}

.app-shell .field-error[hidden] {
  display: none;
}

.app-shell .form-status {
  grid-column: 1 / -1;
  padding: var(--sg-space-3);
  border-radius: var(--sg-radius);
  font-size: var(--sg-text-sm);
  line-height: 1.4;
}

.app-shell .form-status[data-tone="error"] {
  background: var(--sg-danger-soft);
  color: var(--sg-danger);
}

.app-shell .form-status[data-tone="success"] {
  background: var(--sg-success-soft);
  color: var(--sg-success);
}

.app-shell form.is-submitting {
  cursor: progress;
}

.app-shell form.is-submitting .control {
  transition: opacity 0.12s ease;
}

.app-shell .field-help {
  font-size: var(--sg-text-xs);
  color: var(--sg-muted);
}

/* field-group (contrato §F): agrupación semántica, una sola columna */
.app-shell .field-group {
  display: grid;
  gap: var(--sg-space-3);
  margin: 0;
  padding: 0;
  border: 0;
}

.app-shell .field-group-title {
  padding: 0;
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-semibold);
  color: var(--sg-ink);
}

/* summary — totales (contrato §F) */
.app-shell .summary {
  display: grid;
  gap: var(--sg-space-2);
}

.app-shell .summary-row,
.app-shell .summary-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sg-space-3);
}

.app-shell .summary-total {
  padding-top: var(--sg-space-2);
  border-top: 1px solid var(--sg-border);
  font-weight: var(--sg-weight-semibold);
  font-size: var(--sg-text-lg);
}

.app-shell .summary-label {
  color: var(--sg-muted);
}

.app-shell .summary-total .summary-label {
  color: var(--sg-ink);
}

.app-shell .dialog-alert {
  padding: var(--sg-space-3);
  border: 1px solid color-mix(in srgb, var(--sg-danger) 38%, #ffffff);
  border-radius: 8px;
  background: color-mix(in srgb, var(--sg-danger) 8%, #ffffff);
  color: var(--sg-danger);
  font-size: var(--sg-text-sm);
}

.app-shell .wizard-step {
  animation: sgc-wizard-step-in 0.15s ease-out;
}

/* .content-section ya define display:grid (specificity 0,2,0); sin esta regla
   (0,3,0) el atributo [hidden] nunca gana y los 4 pasos quedan todos visibles. */
.app-shell .wizard-step[hidden] {
  display: none;
}

@keyframes sgc-wizard-step-in {
  from { opacity: 0; transform: translateX(8px); }
  to { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell .wizard-step { animation: none; }
}

.app-shell .draft-confirm {
  position: absolute;
  inset: 0;
  z-index: calc(var(--sg-z-modal) + 1);
  display: grid;
  place-items: center;
  padding: var(--sg-space-4);
  background: rgba(23, 22, 19, 0.36);
}

.app-shell .draft-confirm[hidden] {
  display: none;
}

.app-shell .draft-confirm-panel {
  width: min(100%, 380px);
  display: grid;
  gap: var(--sg-space-3);
  padding: var(--sg-space-4);
  border-radius: 12px;
  background: var(--sg-surface);
  color: var(--sg-ink);
  box-shadow: var(--sg-shadow-pop);
}

.app-shell .draft-confirm-title {
  margin: 0;
  font-size: var(--sg-text-lg);
  font-weight: var(--sg-weight-semibold);
  line-height: var(--sg-leading-tight);
}

.app-shell .draft-confirm-text {
  margin: 0;
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
}

.app-shell .draft-confirm-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sg-space-2);
  flex-wrap: wrap;
}

/* icon-btn — botón de navegación/acción sin borde ni fondo, solo ícono
   Lucide + hover. Para "Atrás/Siguiente/Volver" y acciones de fila (quitar,
   editar, eliminar). No lleva texto: el ícono debe ser autoexplicativo. */
.app-shell .icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: transparent;
  border-radius: var(--sg-radius-sm);
  color: var(--sg-faint);
  cursor: pointer;
  transition: background-color 0.15s ease-out, color 0.15s ease-out;
}

.app-shell .icon-btn svg {
  width: 16px;
  height: 16px;
}

.app-shell .icon-btn:hover {
  background: var(--sg-sunken);
  color: var(--sg-ink);
}

.app-shell .icon-btn:focus-visible {
  outline: none;
  box-shadow: var(--sg-ring);
}

.app-shell .icon-btn.is-danger:hover {
  background: var(--sg-danger-soft);
  color: var(--sg-danger);
}

.app-shell .icon-btn.is-success {
  background: var(--sg-success-soft);
}

.app-shell .icon-btn.is-success:hover {
  background: var(--sg-success-soft);
  color: var(--sg-success);
}

/* Spinner minimo (feedback funcional de carga, no decoracion): gira un
   icono ya existente (`rotate-ccw`) mientras dura una subida real. */
.app-shell .is-spinning {
  animation: sgc-spin 1s linear infinite;
}

@keyframes sgc-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell .is-spinning { animation: none; }
}

.app-shell .comprobante-preview {
  max-width: 100%;
  max-height: 320px;
  border-radius: var(--sg-radius);
  border: 1px solid var(--sg-border);
  display: block;
  margin-bottom: var(--sg-space-3);
}

.app-shell .comprobante-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sg-space-2);
  padding: var(--sg-space-6) 0;
  color: var(--sg-muted);
}

.app-shell .comprobante-loading .icon {
  width: 28px;
  height: 28px;
}

/* Selector de archivo nativo con estilo propio: el widget del SO/navegador
   ("Seleccionar archivo" + texto gris) no se puede estilar, así que el
   `<input type="file">` real queda accesible pero visualmente oculto
   (`.sr-only`, sigue siendo focuseable/tabuleable) y un `<label for>`
   estilado como `.btn` dispara el picker nativo (comportamiento HTML
   estándar, sin JS). El nombre del archivo elegido se muestra aparte. */
.app-shell .comprobante-file-control {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
  flex-wrap: wrap;
}

.app-shell .comprobante-file-name {
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* Utilidad de accesibilidad ya usada en varias pantallas (headers de
   columna "Seleccionar") pero sin implementación — mismo patrón estándar
   (oculto visualmente, sigue en el DOM/tab order) en vez de `display:none`,
   que sacaría estos elementos de la validación/foco del teclado. */
.app-shell .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.app-shell .icon-btn.is-whatsapp {
  color: #0f7b4f;
}

.app-shell .icon-btn.is-whatsapp:hover {
  background: var(--sg-success-soft);
  color: #075e3a;
}

.app-shell .icon-btn.is-static,
.app-shell .icon-btn.is-static:hover,
.app-shell .btn.is-static,
.app-shell .btn.is-static:hover {
  background: transparent;
  color: var(--sg-muted);
  cursor: default;
}

/* wizard: Atrás/Siguiente son icon-button (sin texto, solo chevrón) */
.app-shell .wizard-nav {
  display: inline-flex;
  align-items: center;
  gap: var(--sg-space-1);
  margin-left: auto;
}

.app-shell .fab {
  position: fixed;
  right: max(var(--sg-space-5), env(safe-area-inset-right));
  bottom: max(var(--sg-space-5), env(safe-area-inset-bottom));
  z-index: var(--sg-z-toast);
  width: 56px;
  height: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--sg-radius-pill);
  background: var(--sg-accent);
  color: var(--sg-on-accent);
  box-shadow: var(--sg-shadow-md);
  text-decoration: none;
  transition: background-color 0.15s ease-out, transform 0.15s ease-out;
}

.app-shell .fab svg {
  width: 22px;
  height: 22px;
}

.app-shell .fab:hover {
  background: var(--sg-accent-hover);
  transform: translateY(-1px);
}

.app-shell .fab:focus-visible {
  outline: none;
  box-shadow: var(--sg-ring), var(--sg-shadow-md);
}

@media (prefers-reduced-motion: reduce) {
  .app-shell .fab {
    transition: background-color 0.15s ease-out;
  }

  .app-shell .fab:hover {
    transform: none;
  }
}

@media (max-width: 640px) {
  .app-shell .fab {
    right: max(var(--sg-space-4), env(safe-area-inset-right));
    bottom: max(var(--sg-space-4), env(safe-area-inset-bottom));
  }
}

/* Carrito dentro del dialog: filas compactas, no tabla ancha (una tabla de
   680px de min-width no entra en un dialog de ~480px sin verse rota). */
.app-shell .cart-line {
  display: grid;
  gap: var(--sg-space-2);
  padding: var(--sg-space-3) 0;
  border-bottom: 1px solid var(--sg-border);
  min-width: 0;
}

.app-shell [data-sg-cart-items] {
  display: grid;
  margin-top: var(--sg-space-1);
}

.app-shell .cart-picker {
  display: grid;
  gap: var(--sg-space-2);
  position: relative;
  z-index: 2;
}

.app-shell .cart-search,
.app-shell .predictive-field {
  position: relative;
}

.app-shell .cart-search input,
.app-shell .predictive-field input[type="search"] {
  width: 100%;
  min-height: 42px;
  padding: var(--sg-space-2) var(--sg-space-3);
  border: 1px solid var(--sg-border-strong);
  border-radius: 8px;
  background: var(--sg-surface);
  color: var(--sg-ink);
  font: inherit;
}

.app-shell .cart-search input:focus-visible,
.app-shell .predictive-field input[type="search"]:focus-visible {
  outline: none;
  border-color: var(--sg-accent);
  box-shadow: var(--sg-ring);
}

.app-shell .cart-results,
.app-shell .predictive-results {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  z-index: var(--sg-z-dropdown);
  display: grid;
  overflow: hidden;
  padding: var(--sg-space-1);
  border: 1px solid var(--sg-border-strong);
  border-radius: 8px;
  background: var(--sg-surface);
  box-shadow: var(--sg-shadow-pop);
}

.app-shell .cart-results[hidden],
.app-shell .predictive-results[hidden] {
  display: none;
}

.app-shell .cart-result {
  display: grid;
  gap: 0.1429em;
  width: 100%;
  padding: var(--sg-space-2);
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--sg-ink);
  text-align: left;
  cursor: pointer;
}

.app-shell .cart-result:hover,
.app-shell .cart-result[aria-selected="true"] {
  background: var(--sg-sunken);
}

.app-shell .cart-result:focus-visible {
  outline: none;
  background: var(--sg-sunken);
  box-shadow: inset 0 0 0 2px var(--sg-accent);
}

.app-shell .cart-result-title {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
}

.app-shell .cart-result-meta {
  color: var(--sg-muted);
  font-size: var(--sg-text-xs);
}

.app-shell .cart-line-main {
  display: grid;
  gap: var(--sg-space-1);
  min-width: 0;
}

.app-shell .cart-line-title {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
  color: var(--sg-ink);
}

.app-shell .cart-line-desc {
  width: 100%;
  min-height: 38px;
  padding: var(--sg-space-2) var(--sg-space-3);
  border: 1px solid var(--sg-border-strong);
  border-radius: 8px;
  background: var(--sg-surface);
  color: var(--sg-ink);
  font: inherit;
}

.app-shell .cart-line-meta {
  display: grid;
  grid-template-columns: 72px minmax(112px, 1fr) minmax(92px, auto) 40px;
  align-items: center;
  gap: var(--sg-space-3);
  min-width: 0;
}

.app-shell .cart-line-field {
  display: grid;
  gap: 0.2143em;
  min-width: 0;
}

.app-shell .cart-line-field-label {
  font-size: var(--sg-text-xs);
  color: var(--sg-muted);
}

.app-shell .cart-line-field input {
  width: 100%;
  min-width: 0;
  min-height: 34px;
  padding: 0.4286em 0.5714em;
  border: 1px solid var(--sg-border-strong);
  border-radius: 6px;
  background: var(--sg-surface);
  color: var(--sg-ink);
  font: inherit;
  font-size: var(--sg-text-sm);
}

.app-shell .cart-line-field input:focus-visible,
.app-shell .cart-line-desc:focus-visible {
  outline: none;
  border-color: var(--sg-accent);
  box-shadow: var(--sg-ring);
}

.app-shell .cart-line-field input[readonly] {
  background: var(--sg-soft);
  border-color: var(--sg-border);
  color: var(--sg-ink);
}

.app-shell .cart-line-subtotal {
  display: grid;
  gap: 0.2143em;
  min-width: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.app-shell [data-sg-cart-subtotal] {
  font-size: var(--sg-text-sm);
  white-space: nowrap;
}

@media (max-width: 480px) {
  .app-shell .cart-line-meta {
    grid-template-columns: minmax(68px, 0.75fr) minmax(104px, 1fr) 40px;
    gap: var(--sg-space-2);
  }
  .app-shell .cart-line-subtotal {
    grid-column: 1 / 3;
    text-align: left;
  }
}

/* Notices */
.app-shell .notice {
  display: flex;
  align-items: center;
  gap: 0.7143em;
  border: 1px solid var(--sg-border-strong);
  border-radius: 8px;
  padding: 0.7143em 1em;
  margin-bottom: var(--sg-space-4);
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
}

.app-shell .notice-success,
.app-shell .notice[data-tone="success"] {
  border-color: transparent;
  background: var(--sg-success-soft);
  color: var(--sg-success);
}

.app-shell .notice-error,
.app-shell .notice[data-tone="error"] {
  border-color: transparent;
  background: var(--sg-danger-soft);
  color: var(--sg-danger);
}

.app-shell .toast-region {
  position: fixed;
  inset: var(--sg-space-5) max(var(--sg-space-5), env(safe-area-inset-right)) auto auto;
  z-index: var(--sg-z-toast);
  display: grid;
  gap: var(--sg-space-2);
  width: min(360px, calc(100vw - var(--sg-space-5) * 2));
  pointer-events: none;
}

.app-shell .toast {
  border-radius: var(--sg-radius-lg);
  padding: var(--sg-space-3) var(--sg-space-4);
  background: var(--sg-ink);
  color: var(--sg-surface);
  box-shadow: var(--sg-shadow-md);
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
  line-height: var(--sg-leading-normal);
  animation: sg-toast-out 5.5s ease-out both;
}

.app-shell .toast[data-tone="success"] {
  background: var(--sg-success);
}

.app-shell .toast[data-tone="error"] {
  background: var(--sg-danger);
}

@keyframes sg-toast-out {
  0%,
  78% {
    opacity: 1;
    transform: translateY(0);
  }

  100% {
    opacity: 0;
    transform: translateY(-6px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell .toast {
    animation: none;
  }
}

/* Badges — estado por `data-status` (contrato §E), nunca clases nuevas.
   Familias de color: verde (success/ok/paid/cash), ámbar
   (warning/pending/low/installments), rojo (danger/debt), azul (info),
   neutro (default). Solo success/warning/danger/default se emiten hoy;
   el resto queda listo para pantallas futuras sin re-decidir color. */
.app-shell .badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sg-space-1);
  min-height: 22px;
  padding: 0.1429em 0.6429em;
  border-radius: var(--sg-radius-pill);
  border: 0;
  background: var(--sg-soft);
  color: var(--sg-muted);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-2xs);
  font-weight: var(--sg-weight-semibold);
  letter-spacing: 0.01em;
  line-height: 1.2;
  white-space: nowrap;
}

.app-shell .badge[data-status="default"],
.app-shell .badge[data-status="neutral"] {
  color: var(--sg-muted);
  background: var(--sg-sunken);
}

.app-shell .badge[data-status="success"],
.app-shell .badge[data-status="ok"],
.app-shell .badge[data-status="paid"],
.app-shell .badge[data-status="cash"] {
  color: var(--sg-success);
  border-color: transparent;
  background: var(--sg-success-soft);
}

.app-shell .badge[data-status="warning"],
.app-shell .badge[data-status="pending"],
.app-shell .badge[data-status="low"],
.app-shell .badge[data-status="installments"] {
  color: var(--sg-warning);
  border-color: transparent;
  background: var(--sg-warning-soft);
}

.app-shell .badge[data-status="danger"],
.app-shell .badge[data-status="debt"] {
  color: var(--sg-danger);
  border-color: transparent;
  background: var(--sg-danger-soft);
}

.app-shell .badge[data-status="info"] {
  color: var(--sg-accent-ink);
  border-color: transparent;
  background: var(--sg-accent-soft);
}

.app-shell .badge .icon {
  width: 13px;
  height: 13px;
}

/* Tablas */
.app-shell .data-table,
.app-shell .table-wrap {
  max-width: 100%;
}

.app-shell .table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.app-shell .table {
  width: 100%;
  min-width: 680px;
  border-collapse: collapse;
  font-size: var(--sg-text-sm);
}

.app-shell .table th,
.app-shell .table td {
  text-align: left;
  padding: 0.7143em;
  border-bottom: 1px solid var(--sg-border);
  vertical-align: top;
}

.app-shell .table-head th {
  background: var(--sg-soft);
  color: var(--sg-muted);
  font-weight: 600;
}

.app-shell .table-body tr:last-child td {
  border-bottom: 0;
}

/*
 * Jerarquia de celda (igual a `prototype/css/app.css` .table .cell-strong /
 * .cell-muted / .cell-sub): antes solo existia scopeada a
 * `.dashboard-page .table`, asi que el resto de los listados (Cuotas,
 * Ventas, Clientes, etc.) nunca tuvo esta jerarquia y todo el texto de
 * tabla se veia al mismo peso/color.
 */
.app-shell .table .cell-strong {
  font-weight: var(--sg-weight-strong);
  color: var(--sg-ink);
}

.app-shell .table .cell-muted {
  color: var(--sg-muted);
}

.app-shell .cell-sub {
  display: block;
  margin-top: 0.0714em;
  color: var(--sg-muted);
  font-size: var(--sg-text-xs);
  font-weight: var(--sg-weight-normal);
}

/*
 * `.row-link` (links de fila, ej. "#7" en Venta, nombre en Clientes): mismo
 * problema que `.cell-strong`/`.cell-muted` de arriba, solo existia scopeado
 * a `.dashboard-page`. Tinta neutra por defecto, acento SOLO al hover (no
 * link azul subrayado) — igual a `prototype/css/app.css` .row-link.
 */
.app-shell .row-link {
  color: var(--sg-ink);
  font-weight: var(--sg-weight-strong);
  text-decoration: none;
}

.app-shell .row-link:hover {
  color: var(--sg-accent-ink);
  text-decoration: underline;
}

/* Mobile: el sidebar pasa a drawer off-canvas (override del grid). */
@media (max-width: 920px) {
  .app-shell,
  .app-shell[data-sidebar="collapsed"] {
    grid-template-columns: 1fr;
  }

  /* En móvil nunca se "colapsa" a mini: el sidebar siempre va expandido
     dentro del drawer (las labels deben verse). */
  .app-shell[data-sidebar="collapsed"] .sidebar-link-label,
  .app-shell[data-sidebar="collapsed"] .sidebar-user .user-name {
    display: block;
  }
  .app-shell[data-sidebar="collapsed"] .brand-mark--full { display: inline-flex; }
  .app-shell[data-sidebar="collapsed"] .brand-mark--mini { display: none; }
  .app-shell[data-sidebar="collapsed"] .sidebar-branding,
  .app-shell[data-sidebar="collapsed"] .sidebar-link,
  .app-shell[data-sidebar="collapsed"] .sidebar-user-info,
  .app-shell[data-sidebar="collapsed"] .sidebar-logout {
    justify-content: flex-start;
    gap: var(--sg-space-3);
  }

  .app-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: var(--sg-z-drawer);
    width: min(86vw, var(--sg-sidebar-w));
    height: 100%;
    border-right: 1px solid var(--sg-border);
    border-bottom: 0;
    box-shadow: var(--sg-shadow-pop);
    transform: translateX(-100%);
    transition: transform 0.22s ease;
  }

  /* En móvil el botón de colapsar sidebar no aplica (barra inferior). */
  .sidebar-collapse { display: none; }

  .app-shell .page {
    padding: var(--sg-space-4);
  }

  .app-shell .panel-head,
  .app-shell .panel-body {
    padding: 0;
  }

  .app-shell .panel-toolbar {
    padding: 0 0 var(--sg-space-3);
  }

  .app-shell .table {
    min-width: 560px;
  }

  /* F3a — targets táctiles cómodos en móvil */
  .app-shell .btn {
    min-height: 40px;
  }
}

/* ============================================================
   Bloque F · F3b — Contenedores del contrato: content-section,
   tabs, empty-state. Estilo Notion/Claude.
   ============================================================ */

/* — content-section (secciones internas de panel/ficha) — */
.app-shell .content-section {
  display: grid;
  gap: var(--sg-space-3);
}

.app-shell .content-section + .content-section {
  margin-top: var(--sg-space-5);
}

.app-shell .section-title {
  margin: 0;
  font-size: var(--sg-text-base);
  font-weight: var(--sg-weight-semibold);
}

/* — Navegación interna: tabs (contrato §C) — */
.app-shell .tab-list {
  display: flex;
  align-items: center;
  gap: var(--sg-space-1);
  border-bottom: 1px solid var(--sg-border);
  overflow-x: auto;
  overflow-y: hidden;
}

.app-shell .tabs {
  display: flex;
  align-items: center;
  gap: var(--sg-space-1);
  border-bottom: 1px solid var(--sg-border);
  overflow-x: auto;
  overflow-y: hidden;
}

.app-shell .tab {
  display: inline-flex;
  align-items: center;
  gap: 0.4286em;
  appearance: none;
  flex: none;
  border: 0;
  background: transparent;
  margin: 0 var(--sg-space-3) -1px 0;
  padding: 0.7857em 0.2857em;
  border-bottom: 2px solid transparent;
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-strong);
  white-space: nowrap;
  cursor: pointer;
}

.app-shell .tab:hover {
  color: var(--sg-ink);
}

.app-shell .tab:focus-visible {
  outline: none;
  box-shadow: var(--sg-ring);
  border-radius: var(--sg-radius-sm);
}

.app-shell .tab[aria-selected="true"] {
  color: var(--sg-ink);
  border-bottom-color: var(--sg-ink);
  font-weight: var(--sg-weight-semibold);
}

.app-shell .tab-count {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  font-size: var(--sg-text-2xs);
  font-weight: var(--sg-weight-semibold);
  color: var(--sg-muted);
  background: var(--sg-sunken);
  border-radius: var(--sg-radius-pill);
  padding: 0.0714em 0.5em;
}

.app-shell .tab-panel {
  padding-top: var(--sg-space-4);
}

.app-shell .tab-panel[hidden] {
  display: none;
}

/* Un tab-panel puede combinar contenido con padding propio (formulario) y
   una tabla que sangra hasta el borde de la tarjeta (`.table-wrap`, sin
   padding por diseño): el padding va en el formulario, no en el tab-panel
   entero, para no quitarle el sangrado a la tabla que conviva en la misma
   pestaña (p. ej. "Nueva nota" + listado de notas en la ficha de cliente). */
.app-shell .tab-panel > .form {
  padding: 0 var(--sg-space-5) var(--sg-space-5);
}

.app-shell .ajustes-panel-body {
  display: grid;
  gap: var(--sg-space-5);
  padding: var(--sg-space-4) var(--sg-space-5) var(--sg-space-5);
}

.app-shell .ajustes-page > .panel > .tabs {
  padding: 0 var(--sg-space-4);
}

.app-shell .ajustes-form {
  max-width: 560px;
}

.app-shell .ajustes-actions {
  align-items: flex-start;
}

.app-shell .ajustes-section-title {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
}

.app-shell .ajustes-section-title .icon {
  width: 18px;
  height: 18px;
  color: var(--sg-muted);
}

.app-shell .ajustes-sep {
  height: 1px;
  background: var(--sg-border);
}

.app-shell .ajustes-category-create {
  align-self: end;
}

.app-shell .ajustes-category-list {
  display: flex;
  flex-direction: column;
}

.app-shell .ajustes-category-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sg-space-4);
  min-height: 52px;
  border-bottom: 1px solid var(--sg-border);
}

.app-shell .ajustes-category-row:last-child {
  border-bottom: 0;
}

.app-shell .ajustes-category-name,
.app-shell .ajustes-category-row-actions { display: flex; align-items: center; gap: var(--sg-space-2); }
.app-shell .ajustes-category-name { flex: 1; min-width: 0; }
.app-shell .category-icon-preview {
  display: grid; place-items: center; flex: none; width: 28px; height: 28px;
  border-radius: 6px; color: var(--sg-accent-ink); background: var(--sg-accent-soft);
}
.app-shell .category-icon-preview .icon { width: 16px; height: 16px; }
.app-shell .category-icon-control {
  display: flex; align-items: center; gap: var(--sg-space-2); width: 100%; height: 38px;
  padding: 4px 10px 4px 4px; border: 1px solid var(--sg-border-strong); border-radius: 8px;
  color: var(--sg-ink); background: var(--sg-surface); text-align: left; cursor: pointer;
}
.app-shell .category-icon-control > span:nth-child(2) { display: flex; flex: 1; align-items: center; }
.app-shell .category-icon-control > .icon { width: 16px; height: 16px; color: var(--sg-muted); }
.app-shell .category-icon-control:hover { border-color: var(--sg-accent); }
.app-shell .category-icon-control:focus-visible,
.app-shell .category-icon-option:focus-visible { outline: none; box-shadow: var(--sg-ring); }
.app-shell .category-icon-dialog { width: min(680px, calc(100vw - 32px)); }
.app-shell .category-icon-search { width: 100%; margin-bottom: var(--sg-space-4); }
.app-shell .category-icon-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: var(--sg-space-2);
}
.app-shell .category-icon-option {
  display: flex; align-items: center; gap: var(--sg-space-2); min-height: 48px; padding: var(--sg-space-2);
  border: 0; border-radius: 8px; color: var(--sg-ink); background: var(--sg-soft);
  font: inherit; font-size: var(--sg-text-sm); text-align: left; cursor: pointer;
}
.app-shell .category-icon-option:hover,
.app-shell .category-icon-option[aria-selected="true"] { color: var(--sg-accent-ink); background: var(--sg-accent-soft); }
.app-shell .category-icon-option .icon { flex: none; width: 20px; height: 20px; }

.app-shell .ajustes-category-note {
  margin-top: var(--sg-space-3);
}

@media (max-width: 760px) {
  .app-shell .ajustes-category-create {
    align-self: stretch;
  }

  .app-shell .ajustes-category-create .btn {
    width: 100%;
  }
}

.app-shell .check {
  display: inline-flex;
  align-items: center;
  gap: var(--sg-space-2);
  color: var(--sg-ink);
  font-size: var(--sg-text-sm);
}

.app-shell .check input {
  width: 16px;
  height: 16px;
  accent-color: var(--sg-accent);
}

/* — empty-state (sin resultados / vacío, contrato §D) — */
.app-shell .empty-state,
.app-shell .empty {
  display: grid;
  justify-items: center;
  gap: var(--sg-space-1);
  padding: var(--sg-space-6) var(--sg-space-4);
  text-align: center;
  color: var(--sg-muted);
}

.app-shell .empty-title {
  font-weight: var(--sg-weight-medium);
  color: var(--sg-ink);
}

.app-shell .empty-desc,
.app-shell .empty-text {
  font-size: var(--sg-text-sm);
}

/* ============================================================
   Bloque F · F3c — Degradación tabla → tarjetas apiladas en
   móvil (override mobile-first del contrato §D). Reusa los
   data-label/data-cell que D dejó cableados en cada <td>; sin
   tocar PHP ni inventar clases. En teléfono (≤640px) cada fila
   se vuelve una tarjeta vertical label/valor; tablet (641–920px)
   conserva el scroll horizontal. Decisión validada: tarjetas,
   no scroll horizontal en teléfono.
   ============================================================ */
@media (max-width: 640px) {
  /* La tabla deja de necesitar ancho mínimo / scroll: se apila. */
  .app-shell .table {
    min-width: 0;
    font-size: var(--sg-text-sm);
  }
  .app-shell .table-wrap {
    overflow-x: visible;
  }

  /* Cada parte de la tabla pasa a bloque (se rompe el layout tabular). */
  .app-shell .table,
  .app-shell .table-body,
  .app-shell .table-body tr,
  .app-shell .table-body td {
    display: block;
    width: 100%;
  }

  /* Encabezado: oculto accesible (las labels las da data-label por celda). */
  .app-shell .table-head {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  /* Cada fila = una tarjeta (las tarjetas ya son la caja; hairline only). */
  .app-shell .table-body tr {
    margin-bottom: var(--sg-space-3);
    padding: var(--sg-space-2) var(--sg-space-4);
    background: var(--sg-surface);
    border: 1px solid var(--sg-border);
    border-radius: var(--sg-radius);
  }
  .app-shell .table-body tr:last-child {
    margin-bottom: 0;
  }

  /* Celda = fila label/valor; el data-label rotula a la izquierda. */
  .app-shell .table-body td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--sg-space-4);
    padding: var(--sg-space-2) 0;
    text-align: right;
    border-bottom: 1px solid var(--sg-border);
  }
  .app-shell .table-body td:last-child {
    border-bottom: 0;
  }
  .app-shell .table-body td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    text-align: left;
    font-weight: var(--sg-weight-medium);
    color: var(--sg-muted);
  }
  /* Si una celda no trae data-label, no fuerza la columna vacía. */
  .app-shell .table-body td[data-label=""]::before,
  .app-shell .table-body td:not([data-label])::before {
    content: none;
  }

  /* Celda de acciones: ancho completo, sin rótulo; botones alineados. */
  .app-shell .table-body td[data-cell="actions"] {
    justify-content: flex-end;
    text-align: left;
  }
  .app-shell .table-body td[data-cell="actions"]::before {
    content: none;
  }
  .app-shell .table-body td[data-cell="actions"] .row-actions {
    flex-wrap: wrap;
    justify-content: flex-end;
  }
}

/* ============================================================
   Bloque F · F4a — Resumen: gráficos + limpieza de paneles.
   Estética Notion/Claude (no literal al mock). La paleta del
   propio Chart.js vive en dashboard-charts.js (lee tokens F1).
   ============================================================ */

/* Toolbars de panel sin contenido: el dashboard imprime
   `panel-toolbar` con `toolbar-left`/`toolbar-right` vacíos.
   Se colapsan para no dejar una franja con borde y padding
   de balde (limpieza visual, sin tocar el PHP). */
.app-shell .panel-toolbar:not(:has(.toolbar-left > *)):not(:has(.toolbar-right > *)) {
  display: none;
}

/* Contenedor de gráfico: alto acotado y responsive.
   El canvas llena el contenedor (maintainAspectRatio:false en JS),
   así no crece sin límite en pantallas anchas. */
.app-shell .chart-wrap {
  position: relative;
  width: 100%;
  height: clamp(220px, 40vw, 320px);
}

.app-shell .chart-wrap > canvas {
  max-width: 100%;
}

@media (min-width: 921px) {
  .app-shell .chart-wrap {
    height: clamp(240px, 22vw, 300px);
  }
}

/* ============================================================
   Bloque F · F4b — Listados afinados (Ventas, Cuotas/Cobranzas,
   Clientes, Productos, Finanzas, Proveedores). Solo estética
   Notion/Claude sobre la estructura existente del contrato
   (page-header / panel / data-table / empty-state). NO agrega
   búsqueda/filtros/exportar: eso es lógica (Bloque C/D), diferido
   y registrado en el log. Las refinaciones de densidad/hover van
   acotadas a ≥641px para no pisar el modo tarjetas móvil (F3c).
   ============================================================ */

/* Encabezado de pantalla: un respiro más generoso bajo el título,
   coherente con el aire Notion de las capturas. */
@media (min-width: 641px) {
  /* Densidad cómoda de tabla (filas más aireadas, Notion). */
  .app-shell .table {
    font-size: var(--sg-text-sm);
  }

  .app-shell .table th,
  .app-shell .table td {
    padding: 0.7143em var(--sg-space-4);
    vertical-align: middle;
  }

  /* Encabezado de columnas: sin relleno, rótulo tenue en
     minúsculas con tracking; definición por hairline inferior. */
  .app-shell .table-head th {
    background: transparent;
    color: var(--sg-faint);
    font-size: var(--sg-text-2xs);
    font-weight: var(--sg-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom-color: var(--sg-border);
    white-space: nowrap;
  }

  /* Hover de fila: realce sutil para escaneo (no zebra). */
  .app-shell .table-body tr {
    transition: background 0.12s ease;
  }
  .app-shell .table-body tr:hover {
    background: var(--sg-sunken);
  }

  /* Montos: alineados a la derecha + cifras tabulares para que las
     unidades/decimales cuadren columna abajo (alineación tabular real;
     left + tabular-nums no alcanza con enteros de distinto largo). El
     encabezado de esas columnas se alinea a la derecha vía data-col. */
  .app-shell .table-body td[data-cell="money"] {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .app-shell .table-head th[data-col="money"] {
    text-align: right;
  }

  /* Nombre/enlace de la entidad (primera columna): tinta legible,
     peso medio, el acento aparece al hover (no link azul subrayado). */
  .app-shell .table-body td[data-cell="texto"] > a {
    color: var(--sg-ink);
    font-weight: var(--sg-weight-medium);
  }
  .app-shell .table-body td[data-cell="texto"] > a:hover {
    color: var(--sg-accent);
  }
}

/* Empty-state dentro del listado: un poco más de aire para que no
   se vea pegado al borde del panel cuando la tabla está vacía. */
.app-shell .panel-body > .empty-state {
  padding-block: var(--sg-space-6);
}

/* Respeto a usuarios con motion reducido. */
@media (prefers-reduced-motion: reduce) {
  .app-sidebar { transition: none; }
  .app-shell .table-body tr { transition: none; }
}

/* ============================================================
   Aliases temporales del contrato aprobado del prototipo.
   Permiten emitir clases nuevas sin romper las pantallas que todavia usan
   el vocabulario anterior; se retiran en la limpieza final de la migracion.
   ============================================================ */
.app-shell .toolbar {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
  padding: var(--sg-space-3) var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
}

.app-shell .ventas-list-panel > .panel-head {
  display: none;
}

.app-shell .ventas-list-panel > .toolbar {
  display: block;
  padding: 0;
  border-bottom: 0;
}

.app-shell .ventas-list-panel > .toolbar > .toolbar-left {
  display: block;
}

.app-shell .ventas-list-panel .tabs {
  display: flex;
  align-items: center;
  gap: var(--sg-space-1);
  padding: 0 var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
  overflow-x: auto;
  overflow-y: hidden;
}

.app-shell .ventas-list-panel .tabs .tab {
  display: inline-flex;
  align-items: center;
  gap: 0.4286em;
  padding: 0.7857em 0.2857em;
  margin: 0 var(--sg-space-3) -1px 0;
}

.app-shell .ventas-list-panel > .toolbar > .toolbar-right {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sg-space-2);
  padding: var(--sg-space-3) var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
}

.app-shell .ventas-list-panel > .toolbar > .toolbar-right .search {
  flex: 0 1 320px;
  width: 320px;
  max-width: 100%;
}

/* Mismo esqueleto que .ventas-list-panel: tabs en su propia fila, buscador debajo.
   A diferencia de Ventas (siempre tiene filas en los datos de prueba), acá la
   pestaña activa por defecto ("Vencidas") puede estar vacía: sin `.table-wrap`
   el panel no matchea `.panel:has(.table-wrap)` y pierde la caja con borde
   (ver regla base `.panel` mas arriba). Se fuerza la misma caja acá,
   sin depender de si hay tabla, para que no varíe según la pestaña. */
.app-shell .cobranzas-list-panel {
  background: var(--sg-surface);
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius-lg);
  padding: 0;
  overflow: hidden;
}

.app-shell .cobranzas-list-panel > .panel-body {
  padding: 0;
}

.app-shell .cobranzas-list-panel > .panel-head {
  display: none;
}

.app-shell .cobranzas-list-panel > .toolbar {
  display: block;
  padding: 0;
  border-bottom: 0;
}

.app-shell .cobranzas-list-panel > .toolbar > .toolbar-left {
  display: block;
}

.app-shell .cobranzas-list-panel .tabs {
  display: flex;
  align-items: center;
  gap: var(--sg-space-1);
  padding: 0 var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
  overflow-x: auto;
  overflow-y: hidden;
}

.app-shell .cobranzas-list-panel .tabs .tab {
  display: inline-flex;
  align-items: center;
  gap: 0.4286em;
  padding: 0.7857em 0.2857em;
  margin: 0 var(--sg-space-3) -1px 0;
}

.app-shell .cobranzas-list-panel > .toolbar > .toolbar-right {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sg-space-2);
  padding: var(--sg-space-3) var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
}

.app-shell .cobranzas-list-panel > .toolbar > .toolbar-right .search {
  flex: 0 1 320px;
  width: 320px;
  max-width: 100%;
}

.app-shell .search {
  position: relative;
  flex: 1 1 240px;
  max-width: 320px;
}

.app-shell .search .icon {
  position: absolute;
  left: 10px;
  top: 50%;
  translate: 0 -50%;
  width: 15px;
  height: 15px;
  color: var(--sg-faint);
  pointer-events: none;
}

.app-shell .search input {
  width: 100%;
  min-height: 34px;
  padding: 0 0.7143em 0 2.2857em;
  border: 1px solid var(--sg-border-strong);
  border-radius: var(--sg-radius);
  background: var(--sg-surface);
  color: var(--sg-ink);
  font-size: var(--sg-text-sm);
}

.app-shell .search input::placeholder {
  color: var(--sg-muted);
}

.app-shell .search input:focus-visible {
  outline: none;
  border-color: var(--sg-accent);
  box-shadow: var(--sg-ring);
}

.app-shell .suggest {
  position: relative;
}

.app-shell .suggest-pop {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + 4px);
  z-index: var(--sg-z-popover);
  max-height: 300px;
  overflow-y: auto;
  border-radius: var(--sg-radius);
  background: var(--sg-surface);
  box-shadow: var(--sg-shadow-pop);
  padding: var(--sg-space-1);
}

.app-shell .suggest-pop[hidden] {
  display: none;
}

.app-shell .suggest-pop button,
.app-shell .suggest-pop .cart-result {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sg-space-3);
  width: 100%;
  min-width: 0;
  padding: 0.5714em 0.7143em;
  border: 0;
  border-radius: var(--sg-radius-sm);
  background: transparent;
  color: var(--sg-ink);
  cursor: pointer;
  font-size: var(--sg-text-sm);
  line-height: 1.25;
  text-align: left;
}

.app-shell .suggest-pop button:hover,
.app-shell .suggest-pop button[aria-selected="true"],
.app-shell .suggest-pop .cart-result:hover,
.app-shell .suggest-pop .cart-result[aria-selected="true"] {
  background: var(--sg-soft);
}

.app-shell .suggest-name,
.app-shell .cart-result-title {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: var(--sg-weight-strong);
}

.app-shell .suggest-sub,
.app-shell .cart-result-meta {
  display: block;
  margin-top: 0.0714em;
  color: var(--sg-muted);
  font-size: var(--sg-text-2xs);
  font-weight: var(--sg-weight-normal);
  line-height: 1.35;
}

.app-shell .suggest-side {
  flex: none;
  max-width: 42%;
  color: var(--sg-muted);
  font-size: var(--sg-text-2xs);
  line-height: 1.35;
  text-align: right;
  white-space: nowrap;
}

.app-shell .m-only {
  display: none;
}

.app-shell .form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sg-space-4);
}

.app-shell .form-grid .is-full {
  grid-column: 1 / -1;
}

.app-shell .field {
  min-width: 0;
}

.app-shell .field-label .req {
  color: var(--sg-danger);
}

.app-shell .field-hint {
  margin-top: 0.4286em;
  color: var(--sg-muted);
  font-size: var(--sg-text-xs);
}

.app-shell .control {
  width: 100%;
  min-height: 38px;
  padding: 0 0.8571em;
  border: 1px solid var(--sg-border-strong);
  border-radius: var(--sg-radius);
  background: var(--sg-surface);
  color: var(--sg-ink);
  font: inherit;
  font-size: var(--sg-text-md);
}

.app-shell .control:focus-visible {
  outline: none;
  border-color: var(--sg-accent);
  box-shadow: var(--sg-ring);
}

.app-shell .control::placeholder {
  color: var(--sg-muted);
}

.app-shell select.control {
  appearance: none;
  padding-right: 2.4286em;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2363625c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
}

.app-shell dialog.dlg {
  width: min(560px, calc(100vw - 32px));
  max-height: min(86vh, 720px);
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: var(--sg-radius-xl);
  background: var(--sg-surface);
  color: var(--sg-ink);
  box-shadow: var(--sg-shadow-pop);
  overflow: hidden;
}

.app-shell dialog.dlg[open] {
  display: flex;
  flex-direction: column;
}

.app-shell dialog.dlg::backdrop {
  background: rgba(35, 33, 28, 0.45);
}

.app-shell .dlg-wide {
  width: min(680px, calc(100vw - 32px));
}

.app-shell .stepper {
  display: flex;
  gap: var(--sg-space-2);
  padding: 0 var(--sg-space-5) var(--sg-space-3);
  flex: none;
}

.app-shell .step {
  flex: 1;
  min-width: 0;
  padding-top: 0.5714em;
  border-top: 3px solid var(--sg-sunken);
  color: var(--sg-faint);
  overflow: hidden;
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-2xs);
  font-weight: var(--sg-weight-semibold);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-shell .step.is-done {
  border-top-color: var(--sg-accent);
  color: var(--sg-muted);
}

.app-shell .step.is-current {
  border-top-color: var(--sg-accent);
  color: var(--sg-accent-ink);
}

.app-shell .wz-step[hidden] {
  display: none;
}

.app-shell .wizard-payment-hint {
  margin-top: var(--sg-space-3);
}

.app-shell .cart-cols {
  display: none;
  grid-template-columns: 1fr 76px 110px 96px 30px;
  gap: var(--sg-space-2);
  padding-bottom: 0.4286em;
  margin-top: var(--sg-space-3);
  border-bottom: 1px solid var(--sg-border);
  color: var(--sg-faint);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-2xs);
  font-weight: var(--sg-weight-semibold);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.app-shell .cart-cols span:nth-child(4) {
  text-align: right;
}

.app-shell .total-lines {
  display: flex;
  flex-direction: column;
  gap: var(--sg-space-2);
  max-width: 360px;
  margin-left: auto;
}

.app-shell .total-line {
  display: flex;
  justify-content: space-between;
  gap: var(--sg-space-4);
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
}

.app-shell .total-line strong {
  color: var(--sg-ink);
  font-weight: var(--sg-weight-strong);
}

.app-shell .total-line.is-total {
  padding-top: var(--sg-space-2);
  border-top: 1px solid var(--sg-border);
  font-size: var(--sg-text-lg);
}

.app-shell .total-line.is-total strong {
  font-weight: var(--sg-weight-display);
}

.app-shell .dlg-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sg-space-3);
  padding: var(--sg-space-4) var(--sg-space-5) var(--sg-space-3);
  flex: none;
}

.app-shell .dlg-title {
  font-size: var(--sg-text-lg);
  font-weight: var(--sg-weight-display);
  letter-spacing: -0.01em;
  line-height: var(--sg-leading-tight);
}

.app-shell .dlg-sub {
  margin-top: 0.1429em;
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
}

.app-shell .dlg-body {
  padding: var(--sg-space-2) var(--sg-space-5) var(--sg-space-5);
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.app-shell .dlg-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sg-space-2);
  padding: var(--sg-space-3) var(--sg-space-5);
  border-top: 1px solid var(--sg-border);
  background: var(--sg-surface);
  flex: none;
}

.app-shell .dlg-foot .push {
  margin-right: auto;
}

.app-shell .sg-wa-dialog {
  width: min(640px, calc(100vw - 32px));
}

.app-shell .sg-wa-summary {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sg-space-2);
  padding: var(--sg-space-3);
  margin-bottom: var(--sg-space-4);
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius);
  background: var(--sg-soft);
}

.app-shell .sg-wa-summary div {
  min-width: 0;
}

.app-shell .sg-wa-summary span {
  display: block;
  color: var(--sg-faint);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-2xs);
  font-weight: var(--sg-weight-semibold);
}

.app-shell .sg-wa-summary strong {
  display: block;
  margin-top: 0.1429em;
  color: var(--sg-ink);
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
  overflow-wrap: anywhere;
}

.app-shell .sg-wa-form {
  gap: var(--sg-space-4);
}

.app-shell .sg-wa-message {
  min-height: 220px;
  padding-block: 0.7143em;
  line-height: var(--sg-leading-normal);
  resize: vertical;
}

.app-shell dialog.dlg-drawer {
  width: min(320px, calc(100vw - 48px));
  height: 100dvh;
  max-height: 100dvh;
  margin: 0 0 0 auto;
  border-radius: 0;
}

.app-shell dialog.dlg-drawer .form-grid {
  grid-template-columns: 1fr;
  gap: var(--sg-space-4);
}

.app-shell dialog.dlg-drawer .field {
  gap: 0.4286em;
}

.app-shell dialog.dlg-drawer .field-label {
  color: var(--sg-ink);
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-medium);
}

.app-shell dialog.dlg-drawer .control {
  width: 100%;
  min-height: 38px;
  padding: 0 0.8571em;
  border: 1px solid var(--sg-border-strong);
  border-radius: var(--sg-radius);
  background: var(--sg-surface);
  color: var(--sg-ink);
  font-size: var(--sg-text-sm);
}

.app-shell dialog.dlg-drawer .control:focus-visible {
  outline: none;
  border-color: var(--sg-accent);
  box-shadow: var(--sg-ring);
}

.app-shell dialog.dlg-drawer .dlg-body {
  padding-top: var(--sg-space-3);
}

.app-shell dialog.dlg-drawer[open] {
  animation: sg-drawer-in 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes sg-drawer-in {
  from {
    translate: 24px 0;
    opacity: 0;
  }
  to {
    translate: 0 0;
    opacity: 1;
  }
}

.app-shell .opt-list {
  display: flex;
  flex-direction: column;
  gap: 0.1429em;
}

.app-shell .opt-list label {
  display: flex;
  align-items: center;
  gap: 0.7143em;
  padding: 0.5em 0.7143em;
  border-radius: var(--sg-radius);
  font-size: var(--sg-text-sm);
  cursor: pointer;
}

.app-shell .opt-list label:hover {
  background: var(--sg-soft);
}

.app-shell .opt-list input {
  accent-color: var(--sg-accent);
}

.app-shell .table-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sg-space-3);
  padding: 0.5714em var(--sg-space-5);
  border-top: 1px solid var(--sg-border);
  color: var(--sg-muted);
  font-size: var(--sg-text-xs);
}

.app-shell .pager-nav {
  display: flex;
  gap: 0.2857em;
}

.app-shell .pager-nav .icon-btn:disabled,
.app-shell .pager-nav .icon-btn.is-disabled {
  opacity: 0.35;
  pointer-events: none;
}

@media (max-width: 760px) {
  .app-shell .toolbar {
    flex-wrap: wrap;
  }

  .app-shell .toolbar > .toolbar-right {
    flex-wrap: nowrap;
  }

  .app-shell .ventas-list-panel > .toolbar > .toolbar-right {
    padding-inline: var(--sg-space-4);
  }

  .app-shell .cobranzas-list-panel > .toolbar > .toolbar-right {
    padding-inline: var(--sg-space-4);
  }

  .app-shell .search {
    max-width: none;
  }

  .app-shell .toolbar > .toolbar-right .search {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
  }

  .app-shell dialog.dlg,
  .app-shell .dlg-wide {
    width: 100vw;
    max-width: none;
    height: 100dvh;
    max-height: 100dvh;
    margin: 0;
    border-radius: 0;
  }

  .app-shell .form-grid {
    grid-template-columns: 1fr;
  }

  .app-shell .form-grid .is-full {
    grid-column: auto;
  }

  .app-shell .cart-line-meta {
    grid-template-columns: minmax(68px, 0.75fr) minmax(104px, 1fr) 40px;
    gap: var(--sg-space-2);
  }

  .app-shell .cart-line-subtotal {
    grid-column: 1 / 3;
    text-align: left;
  }

  .app-shell .total-lines {
    max-width: none;
    margin-left: 0;
  }

  .app-shell .dlg-foot {
    flex-wrap: nowrap;
    align-items: center;
    padding-inline: var(--sg-space-4);
    gap: 0.4286em;
  }

  .app-shell .dlg-foot .push {
    flex: 1 1 auto;
    min-width: 0;
    margin-right: auto;
    justify-content: flex-start;
  }

  .app-shell .dlg-foot .btn {
    min-height: 36px;
    padding-inline: 0.7143em;
    gap: 0.3571em;
    font-size: var(--sg-text-xs);
  }

  .app-shell .dlg-foot .btn svg {
    width: 14px;
    height: 14px;
  }

  .app-shell .sg-wa-summary {
    grid-template-columns: 1fr;
  }

  .app-shell .sg-wa-message {
    min-height: 240px;
  }

  .app-shell dialog.dlg-drawer {
    width: min(320px, 88vw);
    margin-left: auto;
  }

  .app-shell .table-pager {
    padding-inline: var(--sg-space-4);
  }

  .app-shell .ajustes-page .ajustes-user-create {
    position: fixed;
    right: max(var(--sg-space-4), env(safe-area-inset-right));
    bottom: calc(60px + env(safe-area-inset-bottom) + var(--sg-space-4));
    z-index: var(--sg-z-toast);
    width: 52px;
    height: 52px;
    padding: 0;
    border-radius: var(--sg-radius-pill);
    box-shadow: var(--sg-shadow-md);
    font-size: 0;
    gap: 0;
  }

  .app-shell .ajustes-page .ajustes-user-create .icon,
  .app-shell .ajustes-page .ajustes-user-create svg {
    width: 22px;
    height: 22px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .app-shell dialog.dlg-drawer[open] {
    animation: none;
  }
}

/* ============================================================
   Bloque F · F5a — Pantalla de acceso (login).
   Card centrada Notion sobre fondo hundido, a pantalla completa
   (plantilla page-auth.php, sin chrome del theme). El login usa
   `.app-shell.sgc-auth` para heredar tokens + reset + componentes
   (panel/field/button/notices); aquí solo se ajusta el layout de
   auth: la grid del shell se anula por especificidad (.app-shell.sgc-auth).
   ============================================================ */
.app-shell.sgc-auth {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--sg-space-5);
  background: var(--sg-sunken);
}

.sgc-auth .sgc-auth-main {
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: var(--sg-space-4);
}

/* Marca centrada arriba de la card (wordmark autosuficiente, sin texto al lado). */
.sgc-auth .sgc-auth-brand {
  display: flex;
  justify-content: center;
  margin-bottom: var(--sg-space-1);
}
.sgc-auth .sgc-auth-brand .sgc-brand {
  height: 26px;
  width: auto;
}

/* Card de auth: superficie flotante. Sombra suave SIN borde
   (regla impeccable: nunca borde 1px + sombra ancha en el mismo elemento). */
.sgc-auth .panel {
  background: var(--sg-surface);
  border: none;
  border-radius: var(--sg-radius);
  box-shadow: var(--sg-shadow-md);
  overflow: hidden;
}

/* La cabecera de la card no lleva la franja gris del panel: lee como una
   sola superficie con título + subtítulo, con aire generoso. */
.sgc-auth .panel-head {
  background: transparent;
  border-bottom: 0;
  padding: var(--sg-space-5) var(--sg-space-5) 0;
}
.sgc-auth .panel-body {
  padding: var(--sg-space-4) var(--sg-space-5) var(--sg-space-5);
}

/* Jerarquía: el título de la card manda (grande + semibold + tinta);
   el subtítulo y los labels quedan claramente por debajo (sm, muted). */
.sgc-auth .panel-title {
  font-size: var(--sg-text-xl);
  font-weight: var(--sg-weight-semibold);
  line-height: var(--sg-leading-tight);
  color: var(--sg-ink);
}
.sgc-auth .sgc-auth-subtitle {
  margin: var(--sg-space-1) 0 0;
  color: var(--sg-muted);
  font-size: var(--sg-text-sm);
}

/* Fila "Mantener sesión": checkbox + label en línea, tamaño cómodo. */
.sgc-auth .sgc-auth-remember {
  display: flex;
  align-items: center;
  gap: var(--sg-space-2);
  font-size: var(--sg-text-sm);
  color: var(--sg-muted);
  cursor: pointer;
}

/* Acción primaria a ancho completo (patrón de auth). */
.sgc-auth .form-actions {
  margin-top: var(--sg-space-2);
}
.sgc-auth .form-actions .btn {
  width: 100%;
  justify-content: center;
}

/* Móvil: card a ancho completo con menos padding lateral. */
@media (max-width: 480px) {
  .app-shell.sgc-auth {
    padding: var(--sg-space-4);
    align-items: stretch;
  }
  .sgc-auth .sgc-auth-main {
    max-width: none;
    margin-block: auto;
  }
}

/* ============================================================
   Accesibilidad — forced-colors (modo alto contraste de Windows).
   El foco de estos componentes se resuelve hoy con box-shadow
   (--sg-ring / anillo inset), que el navegador IGNORA en forced-colors
   (Chromium/Edge no pinta box-shadow en ese modo). Como además llevan
   `outline: none`, quedarían sin ningún indicador de foco visible.
   Se restaura un outline real con keywords del sistema (CanvasText/
   ButtonText: los colores de marca no existen en este modo, el UA los
   reemplaza). No toca layout ni color fuera de forced-colors.
   ============================================================ */
@media (forced-colors: active) {
  .app-shell .control:focus-visible,
  .app-shell .icon-btn:focus-visible,
  .app-shell .btn:focus-visible,
  .app-shell .fab:focus-visible,
  .app-shell .search input:focus-visible,
  .app-shell .cart-search input:focus-visible,
  .app-shell .predictive-field input[type="search"]:focus-visible,
  .app-shell .cart-result:focus-visible,
  .app-shell .cart-line-field input:focus-visible,
  .app-shell .cart-line-desc:focus-visible {
    outline: 2px solid CanvasText;
    outline-offset: 2px;
  }

  /* El FAB no lleva borde propio (solo background de acento, que
     forced-colors ignora): sin esto queda sin contorno sobre Canvas. */
  .app-shell .fab {
    border: 1px solid ButtonText;
  }
}

/* ============================================================
   Migracion UI Parte 2 - Shell y navegacion
   Sidebar desktop + bottom nav mobile, sin topbar ni drawer lateral.
   ============================================================ */
.app-shell {
  --sg-sidebar-w: 248px;
  --sg-content-max: 1160px;
  grid-template-columns: var(--sg-sidebar-w) 1fr;
}

.app-shell[data-sidebar="collapsed"] {
  grid-template-columns: var(--sg-sidebar-w-collapsed) 1fr;
}

.app-sidebar {
  padding: var(--sg-space-5) var(--sg-space-3) var(--sg-space-3);
  background: var(--sg-soft);
}

.sidebar-branding {
  display: flex;
  padding: 0 var(--sg-space-2) var(--sg-space-5);
  min-height: 0;
}

.sidebar-link:hover,
.sidebar-logout:hover,
a.sidebar-user-info:hover {
  background: var(--sg-sunken);
}

.sidebar-user-info {
  gap: 0.7143em;
  padding: 0.7143em;
  border-radius: var(--sg-radius);
}

.sidebar-user-avatar {
  background: var(--sg-ink);
  color: #ffffff;
}

.sidebar-user-copy,
.sheet-user > span:last-child {
  min-width: 0;
  display: grid;
  gap: 0.0714em;
}

.sidebar-user-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--sg-text-sm);
  font-weight: var(--sg-weight-semibold);
  line-height: 1.2;
}

.sidebar-user-role {
  color: var(--sg-muted);
  font-size: var(--sg-text-2xs);
  line-height: 1.3;
}

.sidebar-logout:hover {
  color: var(--sg-danger);
}

.app-content {
  min-width: 0;
}

.app-shell .page {
  padding: var(--sg-space-7) var(--sg-space-8) 6em;
}

.content {
  max-width: var(--sg-content-max);
  margin: 0 auto;
}

.bottom-nav,
.sheet-backdrop,
.sheet {
  display: none;
}

.sheet-backdrop[hidden],
.sheet[hidden] {
  display: none;
}

/* ============================================================
   Migracion UI Parte 4 - Resumen
   Contrato del prototipo: stat-row, panel-duo, panel-grid y tablas m-*.
   ============================================================ */
.app-shell .stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin-bottom: var(--sg-space-6);
  border-block: 1px solid var(--sg-border);
}

.app-shell .stat {
  min-width: 0;
  padding: var(--sg-space-4) var(--sg-space-5);
  border-left: 1px solid var(--sg-border);
}

.app-shell .stat:first-child {
  border-left: 0;
  padding-left: 0;
}

.app-shell .stat-label {
  display: flex;
  align-items: center;
  gap: 0.4286em;
  margin-bottom: 0.4286em;
  color: var(--sg-muted);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-xs);
  font-weight: var(--sg-weight-medium);
}

.app-shell .stat-label .icon {
  width: 14px;
  height: 14px;
  color: var(--sg-faint);
}

.app-shell .stat-value {
  color: var(--sg-ink);
  font-size: var(--sg-text-xl);
  font-weight: var(--sg-weight-semibold);
  letter-spacing: -0.01em;
  line-height: 1.2;
  white-space: nowrap;
}

.app-shell .stat-note {
  margin-top: 0.2857em;
  font-size: var(--sg-text-xs);
}

.app-shell .stat-note[data-tone="success"] { color: var(--sg-success); }
.app-shell .stat-note[data-tone="danger"] { color: var(--sg-danger); }
.app-shell .stat-note[data-tone="warning"] { color: var(--sg-warning); }
.app-shell .stat-note[data-tone="neutral"] { color: var(--sg-muted); }

.app-shell .dashboard-page .panel {
  background: var(--sg-surface);
  border: 1px solid var(--sg-border);
  border-radius: var(--sg-radius-lg);
  overflow: hidden;
  padding: 0;
}

.app-shell .dashboard-page .panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sg-space-3);
  margin-bottom: 0;
  padding: var(--sg-space-3) var(--sg-space-4);
  border-bottom: 1px solid var(--sg-border);
  background: transparent;
}

.app-shell .dashboard-page .panel-title {
  display: flex;
  align-items: center;
  gap: 0.5714em;
  color: var(--sg-ink);
  font-size: var(--sg-text-md);
  font-weight: var(--sg-weight-semibold);
}

.app-shell .panel-link {
  display: inline-flex;
  align-items: center;
  gap: 0.2857em;
  color: var(--sg-accent-ink);
  font-family: var(--sg-font-display);
  font-size: var(--sg-text-xs);
  font-weight: var(--sg-weight-semibold);
  text-decoration: none;
}

.app-shell .panel-link:hover {
  text-decoration: underline;
}

.app-shell .panel-link .icon {
  width: 13px;
  height: 13px;
}

.app-shell .dashboard-page .panel-body {
  padding: var(--sg-space-4);
}

.app-shell .dashboard-page .panel-duo {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: var(--sg-space-4);
  margin-bottom: var(--sg-space-6);
}

.app-shell .dashboard-page .panel-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sg-space-4);
  align-items: start;
}

.app-shell .dashboard-page .panel-grid .is-wide {
  grid-column: 1 / -1;
}

.app-shell .dashboard-page .chart-box {
  position: relative;
  height: 240px;
}

.app-shell .dashboard-page .chart-box > canvas {
  max-width: 100%;
}

.app-shell .dashboard-page .table .num,
.app-shell .dashboard-page .table th.num {
  text-align: right;
}

.app-shell .dashboard-page .m-only {
  display: none;
}

.app-shell .dashboard-page .nowrap,
.app-shell .dashboard-page .table td.num,
.app-shell .dashboard-page .table td .badge {
  white-space: nowrap;
}

@media (min-width: 641px) {
  .app-shell .dashboard-page .table-wrap {
    overflow-x: visible;
  }

  .app-shell .dashboard-page .table {
    min-width: 0;
    table-layout: fixed;
  }

  .app-shell .dashboard-page .table th,
  .app-shell .dashboard-page .table td {
    padding: 0.8571em var(--sg-space-4);
    vertical-align: middle;
  }

  .app-shell .dashboard-page .panel-grid .table th,
  .app-shell .dashboard-page .panel-grid .table td {
    padding-inline: 0.8571em;
  }

  .app-shell .dashboard-page .table th:first-child,
  .app-shell .dashboard-page .table td:first-child {
    width: auto;
  }

  .app-shell .dashboard-page .table th.num,
  .app-shell .dashboard-page .table td.num,
  .app-shell .dashboard-page .table th[data-col="money"],
  .app-shell .dashboard-page .table td[data-cell="money"] {
    width: 116px;
  }

  .app-shell .dashboard-page .table th[data-col="money"],
  .app-shell .dashboard-page .table td[data-cell="money"] {
    text-align: right;
  }

  .app-shell .dashboard-page .table th:nth-last-child(2),
  .app-shell .dashboard-page .table td:nth-last-child(2) {
    width: 112px;
  }

  .app-shell .dashboard-page .table .cell-strong,
  .app-shell .dashboard-page .table .row-link {
    overflow-wrap: anywhere;
  }

  .app-shell .dashboard-page .table .cell-muted {
    overflow-wrap: normal;
  }
}

@media (max-width: 960px) {
  .app-shell .page {
    padding: var(--sg-space-7) var(--sg-space-8) 6em;
  }

  .app-shell .dashboard-page .panel-duo,
  .app-shell .dashboard-page .panel-grid {
    grid-template-columns: 1fr;
  }

  .app-shell .dashboard-page .panel-grid .is-wide {
    grid-column: auto;
  }
}

/* Tablet (641–960px): la tabla sigue siendo tabla (no tarjeta apilada como en
   mobile chico), pero solo muestra las columnas ya marcadas m-keep/m-sub/
   m-num/m-badge/m-actions — las mismas que ya resume el mobile card — para
   no forzar scroll horizontal ni columnas apretadas. El resto de columnas
   (detalle secundario) se oculta; el texto de apoyo `.cell-sub.m-only` que
   ya vive dentro de la celda m-keep se muestra inline como refuerzo. */
@media (min-width: 641px) and (max-width: 960px) {
  .app-shell .table:has(.m-keep) {
    width: 100%;
    min-width: 0;
  }

  .app-shell .table:has(.m-keep) th:not(.m-keep):not(.m-sub):not(.m-num):not(.m-badge):not(.m-actions):not(.m-select),
  .app-shell .table:has(.m-keep) .table-body td:not(.m-keep):not(.m-sub):not(.m-num):not(.m-badge):not(.m-actions):not(.m-select) {
    display: none;
  }

  .app-shell .table:has(.m-keep) th.m-select,
  .app-shell .table:has(.m-keep) .table-body td.m-select {
    width: 1%;
    white-space: nowrap;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-keep {
    overflow-wrap: anywhere;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-keep .cell-sub.m-only {
    display: block;
    margin-top: 0.1429em;
    color: var(--sg-muted);
    font-size: var(--sg-text-xs);
    font-weight: var(--sg-weight-normal);
  }

  .app-shell .table:has(.m-keep) th.m-num,
  .app-shell .table:has(.m-keep) .table-body td.m-num,
  .app-shell .table:has(.m-keep) th.m-badge,
  .app-shell .table:has(.m-keep) .table-body td.m-badge,
  .app-shell .table:has(.m-keep) th.m-sub,
  .app-shell .table:has(.m-keep) .table-body td.m-sub {
    white-space: nowrap;
  }

  /* `.m-sub.m-only` (fecha/detalle combinados, celda propia) esta oculta por
     defecto (`.m-only` = display:none en desktop >960px); en tablet vuelve a
     ser una columna real de la tabla, igual que el resto de columnas m-*. */
  .app-shell .table:has(.m-keep) th.m-sub.m-only,
  .app-shell .table:has(.m-keep) .table-body td.m-sub.m-only {
    display: table-cell;
  }

  .app-shell .table:has(.m-keep) .cell-actions.m-actions .row-actions {
    justify-content: flex-end;
  }
}

@media (max-width: 640px) {
  .app-shell .stat-row {
    grid-template-columns: 1fr 1fr;
    gap: 0.0714em;
    margin-bottom: var(--sg-space-5);
    border: 0;
    border-radius: var(--sg-radius-lg);
    background: var(--sg-border);
    overflow: hidden;
  }

  .app-shell .stat {
    padding: var(--sg-space-3) var(--sg-space-4);
    border-left: 0;
    background: var(--sg-soft);
  }

  .app-shell .stat:first-child {
    padding-left: var(--sg-space-4);
  }

  .app-shell .stat-value {
    font-size: var(--sg-text-lg);
  }

  .app-shell .m-only {
    display: inline;
  }

  .app-shell .cell-sub.m-only {
    display: block;
  }

  .app-shell .table:has(.m-keep) {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    font-size: var(--sg-text-sm);
  }

  .app-shell .table-wrap:has(.m-keep) {
    overflow-x: visible;
    max-width: 100%;
  }

  .app-shell .table:has(.m-keep) .table-head {
    display: none;
  }

  .app-shell .table:has(.m-keep),
  .app-shell .table:has(.m-keep) .table-body {
    display: block;
  }

  /* Estructura fija de 2 renglones (contrato, validado 2026-07-07):
     renglon 1 = selector/titulo o nombre/precio
     renglon 2 = fecha (o detalle) / info secundaria / chip de estado
     Grid en vez de flex: cada pieza tiene una celda fija (columna+fila), asi
     el precio siempre queda a la altura del titulo y el chip a la altura
     del subtitulo, sin importar el largo del texto ni si falta alguna pieza
     (ej. Clientes/Proveedores no tienen precio ni chip: esas celdas de la
     grilla quedan vacias, no rompen el acomodo del resto). */
  .app-shell .table:has(.m-keep) .table-body tr {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: auto auto auto;
    align-items: start;
    column-gap: 0.5714em;
    row-gap: 0.1429em;
    width: 100%;
    margin-bottom: 0;
    padding: 0.7143em var(--sg-space-4);
    border: 0;
    border-bottom: 1px solid var(--sg-border);
    border-radius: 0;
    background: transparent;
  }

  .app-shell .table:has(.m-keep) .table-body tr:last-child {
    border-bottom: 0;
  }

  .app-shell .table:has(.m-keep) .table-body td {
    display: none;
    width: auto;
    padding: 0;
    border: 0;
    text-align: left;
  }

  .app-shell .table:has(.m-keep) .table-body td::before {
    content: none;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-keep {
    display: block;
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    font-weight: var(--sg-weight-strong);
  }

  .app-shell .table:has(.m-keep) .table-body td.m-select {
    display: flex;
    grid-column: 1;
    grid-row: 1 / span 2;
    align-self: center;
    align-items: center;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-num {
    display: block;
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    font-weight: var(--sg-weight-strong);
    white-space: nowrap;
    text-align: right;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-badge {
    display: block;
    grid-column: 3;
    grid-row: 2;
    justify-self: end;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-sub {
    display: block;
    grid-column: 2;
    grid-row: 2;
    min-width: 0;
    color: var(--sg-muted);
    font-size: var(--sg-text-xs);
    font-weight: var(--sg-weight-normal);
  }

  .app-shell .table:has(.m-keep) .table-body td.m-sub .cell-sub {
    display: inline;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-actions {
    display: flex;
    grid-column: 1 / -1;
    grid-row: 3;
    min-width: 0;
    justify-content: flex-end;
    gap: 0.5714em;
    margin-top: 0.4286em;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-actions .row-actions {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
    justify-content: flex-end;
    gap: 0.5714em;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-actions .row-actions form {
    flex: 0 0 auto;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-actions:empty {
    display: none;
  }

  .app-shell .table:has(.m-keep) .table-body td.m-actions .icon-btn {
    width: 38px;
    height: 34px;
    border: 1px solid var(--sg-border-strong);
    border-radius: var(--sg-radius);
    color: var(--sg-muted);
    background: var(--sg-surface);
  }

  /* Ventas ya no necesita override propio: sin columna `m-sub` separada
     (la fecha ahora vive en el `.cell-sub.m-only` dentro de `.m-keep`, igual
     que Productos/Cuotas/Proveedores/Ajustes), la tarjeta usa el mismo
     layout generico de abajo — titulo+subtitulo comparten renglon con
     monto/estado a la derecha, en vez de apilarse en filas propias. */
  .app-shell .ventas-list-panel .table:has(.m-keep) .table-body td.m-keep .row-link {
    display: block;
    max-width: 100%;
    overflow-wrap: anywhere;
  }
}

@media (max-width: 960px) {
  .app-shell .page {
    padding: var(--sg-space-7) var(--sg-space-8) 6em;
  }
}

@media (min-width: 761px) and (max-width: 920px) {
  .app-sidebar {
    position: sticky;
    top: 0;
    left: auto;
    bottom: auto;
    z-index: auto;
    width: auto;
    height: 100vh;
    box-shadow: none;
    transform: none;
  }

  /* En tablet el sidebar siempre se ve (nunca colapsa a drawer). */
  .sidebar-collapse { display: inline-flex; }

  /* El bloque F2 de "mobile <=920" (más arriba en el archivo, aún vigente
     para <=760) fuerza labels/nombre visibles y el logo completo porque en
     ese diseño viejo el sidebar nunca colapsaba en pantallas chicas — acá sí
     colapsa de verdad, así que se revierte esa fuerza para 761–920px. */
  .app-shell[data-sidebar="collapsed"] .sidebar-link-label,
  .app-shell[data-sidebar="collapsed"] .sidebar-user-name,
  .app-shell[data-sidebar="collapsed"] .sidebar-user-role {
    display: none;
  }

  .app-shell[data-sidebar="collapsed"] .brand-mark--full { display: none; }
  .app-shell[data-sidebar="collapsed"] .brand-mark--mini { display: inline-flex; }

  .app-shell[data-sidebar="collapsed"] .sidebar-branding,
  .app-shell[data-sidebar="collapsed"] .sidebar-link,
  .app-shell[data-sidebar="collapsed"] .sidebar-user-info,
  .app-shell[data-sidebar="collapsed"] .sidebar-logout {
    justify-content: center;
    gap: 0;
  }
}

@media (max-width: 760px) {
  .app-shell,
  .app-shell[data-sidebar="collapsed"] {
    grid-template-columns: 1fr;
  }

  .app-sidebar {
    display: none;
  }

  .app-shell .page {
    padding: var(--sg-space-5) var(--sg-space-4) 8.5714em;
  }

  /* Acción primaria del page-header → FAB (idéntico a prototype/css/app.css) */
  .app-shell .page-actions .btn-primary {
    position: fixed;
    /* px fijo, no `em`: este mismo selector pone `font-size:0` mas abajo
       para ocultar el texto y dejar solo el icono, y un margen en `em` se
       calcularia sobre ESE font-size (0), colapsando a 0px. */
    right: max(24px, env(safe-area-inset-right));
    bottom: calc(60px + env(safe-area-inset-bottom) + 24px);
    z-index: var(--sg-z-toast);
    width: 52px;
    height: 52px;
    padding: 0;
    border-radius: var(--sg-radius-pill);
    box-shadow: var(--sg-shadow-md);
    font-size: 0;
    gap: 0;
  }

  .app-shell .page-actions .btn-primary .icon,
  .app-shell .page-actions .btn-primary svg {
    width: 22px;
    height: 22px;
  }

  .bottom-nav {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    height: calc(60px + env(safe-area-inset-bottom));
    z-index: var(--sg-z-sticky);
    padding-bottom: env(safe-area-inset-bottom);
    border-top: 1px solid var(--sg-border);
    background: var(--sg-surface);
  }

  .bottom-nav a,
  .bottom-nav button {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.2em;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--sg-faint);
    font-family: var(--sg-font-display);
    font-size: 0.7333em;
    font-weight: var(--sg-weight-semibold);
    line-height: 1.1;
    text-decoration: none;
    cursor: pointer;
  }

  .bottom-nav .icon {
    width: 20px;
    height: 20px;
    stroke-width: 1.8;
  }

  .bottom-nav [aria-current="page"],
  .bottom-nav .is-active {
    color: var(--sg-ink);
  }

  .sheet-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: var(--sg-z-backdrop);
    background: rgba(35, 33, 28, 0.45);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
  }

  .sheet {
    display: block;
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: var(--sg-z-modal);
    padding: var(--sg-space-3) var(--sg-space-4) calc(var(--sg-space-4) + env(safe-area-inset-bottom));
    border-radius: 14px 14px 0 0;
    background: var(--sg-surface);
    box-shadow: 0 -18px 50px rgba(35, 33, 28, 0.18);
    translate: 0 105%;
    transition: translate 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .app-shell.sheet-open .sheet {
    translate: 0 0;
  }

  .app-shell.sheet-open .sheet-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  .sheet-grab {
    width: 36px;
    height: 4px;
    margin: 0 auto var(--sg-space-3);
    border-radius: var(--sg-radius-pill);
    background: var(--sg-border-strong);
  }

  .sheet a {
    display: flex;
    align-items: center;
    gap: 0.8571em;
    padding: 0.8571em 0.5714em;
    border-radius: var(--sg-radius);
    color: var(--sg-ink);
    font-family: var(--sg-font-display);
    font-size: var(--sg-text-md);
    font-weight: var(--sg-weight-medium);
    text-decoration: none;
  }

  .sheet a:hover,
  .sheet a[aria-current="page"] {
    background: var(--sg-soft);
  }

  .sheet .icon {
    width: 18px;
    height: 18px;
    color: var(--sg-muted);
  }

  .sheet-user {
    padding: 0.5714em;
    margin-bottom: var(--sg-space-2);
    border-bottom: 1px solid var(--sg-border);
  }

  .sheet .is-logout,
  .sheet .is-logout .icon {
    color: var(--sg-danger);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sheet,
  .sheet-backdrop {
    transition: none;
  }
}

.sheet-backdrop[hidden],
.sheet[hidden] {
  display: none;
}

/* ============================================================
   Parte intermedia · sub-paso 3 — Acciones de fila reubicadas.
   Sin columna "Acciones" dedicada: el `.row-actions` de cada fila vive
   dentro de la misma celda del checkbox (`td[data-cell="select"]`),
   oculto por defecto en cualquier breakpoint. El JS mueve el nodo real
   de la fila tildada a uno de dos lugares segun el ancho de pantalla
   (matchMedia, ver `initRowSelection` en sg-app.js):
   - Desktop/tablet (>760px): `.row-actions-bar--desktop`, dentro del
     mismo `.toolbar-right` que el buscador/filtro (conviven, no se
     reemplazan), alineada a la derecha; si no entran en una sola linea
     (tablets angostos) el propio `.toolbar-right` pasa a `flex-wrap` y
     la barra cae a su propia linea, sin desordenar ni superponer nada.
   - Mobile (<=760px): `.row-actions-bar--mobile`, que reutiliza la
     clase `.detail-top` (mismo lugar/estilo que la fila de acciones de
     las fichas de detalle), ubicada ANTES del `.page-header` y
     reemplazando visualmente el titulo (el JS oculta `.page-header`
     mientras hay una fila seleccionada). Incluye una flecha de volver
     (`.back-link` como <button>, icono `arrow-left`) que hace lo mismo
     que Escape/destildar: sale de las acciones y hace reaparecer el titulo.
   Sin boton de cerrar tipo "X": se destilda con el propio checkbox, con
   Escape o con la flecha de volver en mobile.
   ============================================================ */
.app-shell [data-row-select] .table-body td[data-cell="select"] .row-actions {
  display: none;
}

.app-shell .row-select-input {
  width: 16px;
  height: 16px;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: var(--sg-border);
  cursor: pointer;
  accent-color: var(--sg-ink);
}

.app-shell th.cell-select,
.app-shell td.cell-select {
  padding-left: 0.4286em;
  padding-right: 0.2857em;
}

.app-shell th.cell-select {
  width: 1%;
  white-space: nowrap;
}

@media (max-width: 760px) {
  .app-shell .row-select-input {
    width: 14px;
    height: 14px;
  }
}

.app-shell .row-actions-bar-slot {
  display: inline-flex;
  align-items: center;
  gap: 0.5714em;
  flex-wrap: wrap;
}

.app-shell .toolbar-right .row-actions-bar--desktop {
  display: inline-flex;
  align-items: center;
  gap: 0.5714em;
  margin-left: auto;
  padding-left: 0.5714em;
  border-left: 1px solid var(--sg-border);
}

.app-shell .toolbar-right .row-actions-bar--desktop[hidden] {
  display: none;
}

/* El buscador nunca cambia de tamaño preferido (sigue en 320px, sin
   `flex-grow`, para no estirarse al ancho que quede libre); lo unico que
   agrega esta regla es `min-width:0`, que le permite achicarse de verdad
   por debajo de su contenido cuando no entra junto a filtro/"Limpiar"/
   acciones (por defecto un flex item no se achica mas alla de su
   min-content, aunque tenga `flex-shrink:1`). Sin esto, en anchos de
   tablet (ej. iPad Mini) el conjunto pasaba directo al wrap de abajo en
   vez de comprimir el buscador primero. */
.app-shell .toolbar-right:has(.row-actions-bar--desktop:not([hidden])) .search {
  min-width: 0;
}

/* Si aun asi no entra (tablets muy angostos), el propio `.toolbar-right` se
   habilita a wrapear: la barra cae ordenada a su propia linea (alineada a
   la derecha por el `margin-left:auto` de arriba) en vez de comprimir/
   superponer todo. */
.app-shell .toolbar > .toolbar-right:has(.row-actions-bar--desktop:not([hidden])) {
  flex-wrap: wrap;
  row-gap: 0.4286em;
}

.app-shell .row-actions-bar--mobile {
  display: none;
}

.app-shell .page-header.is-row-selected,
.app-shell .detail-top.is-row-selected {
  display: none;
}

/* Boton de volver (misma pieza que `.back-link` de las fichas de detalle,
   pero como <button>: no navega, solo destilda la fila y hace reaparecer
   el titulo). Reset de estilos nativos de boton para que se vea igual que
   el link. */
.app-shell button.back-link {
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
}

@media (max-width: 760px) {
  .app-shell .row-actions-bar--mobile:not([hidden]) {
    display: flex;
  }

  .app-shell .row-actions-bar--mobile .icon-btn {
    flex: none;
    width: 38px;
    height: 34px;
    border: 1px solid var(--sg-border-strong);
    border-radius: var(--sg-radius);
    color: var(--sg-muted);
    background: var(--sg-surface);
  }

  /* La regla de arriba fuerza `background` en todo `.icon-btn` movido a la
     barra móvil (misma especificidad, gana por orden); el estado "ya tiene
     comprobante" necesita seguir viéndose ahí tambien. */
  .app-shell .row-actions-bar--mobile .icon-btn.is-success {
    background: var(--sg-success-soft);
  }
}

@media (min-width: 761px) {
  .app-shell .page-header.is-row-selected {
    display: flex;
  }

  .app-shell .detail-top.is-row-selected {
    display: flex;
  }
}
